shell连接不上的解决方案

2020-04-14

Shell连接不上原因分析

shell连不上,网络却能正常ping通?为何?

通过抓包,发现发送的ioGlobalStdSet(1,ioGlobalStdGet())\n,分成了三次从目标机端发送回来。

分别是:

1.    i

2.    oGlobalStdSet(1,ioGlobalStdGet()

3.    )

而且,每一个包都有PSH标志位,这导致不符合shell客户端的判断条件。

现在的解决方案是,修改判断条件,让它在不完整的返回字符串时,也能通过。但现场的抓包文件也体现出了很多网络问题,现在一一分析一下。

0

Seq and ack

TCP通过肯定的确认应答ack实现可靠的数据传输。当发送端将数据发出之后,会等待对端的确认应答,说明数据已经成功到达对端。反之,则数据丢失的可能性很大。

当确认应答ack延迟达到或达不到时,源发送主机则会重发数据,这对目标机来说是灾难,会反复收到相同的数据。而为了对上层应用提供可靠的传输,必须放弃重复的数据包。为此,得引入一种机制,能够识别是否已经接收数据,也能判断是否需要接收。

于是,有了序列号seq。序列号按顺序给发送数据的每一个字节都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据长度,将自己下一步应该接收的序号作为确认应答反送回去。这样,通过序列号和确认应答号,TCP可以实现可靠传输。

1

 

TCP out-of-order

Wireshark判断TCP out-of-order是基于TCP包中SEQ number并非期望收到的下一个SEQ number,则判断为out-of-order。因此,出现TCP out-of-order时,很大可能是TCP存在乱序或丢包,导致接收端的seq number不连续。

 

TCP acked unseen segment

反馈ACK指向了一个未知的TCP片段。

这个意思是说ACK反馈的是一个wireshark上不存在的TCP包。很可能是wireshark漏抓了这个包,但却抓到了对端反馈的该报的ack包。

 

TCP keep-alive

不太好的TCP堆栈实现,可能会要求保活报文必须携带有1个字节的数据负载

TCP Keepalive应该在服务器端启用,客户端不做任何改动;若单独在客户端启用,若客户端异常崩溃或出现连接故障,存在服务器无限期的为已打开的但已失效的文件描述符消耗资源的严重问题。但在特殊的NFS文件系统环境下,需要客户端和服务器端都要启用TcpKeepalive机制。

 

TCP spurious retransmission

按照rfc4138的描述,虚假超时重传指的是实际上没有超时,但看起来超时了(根据rtt估算出的RTO超时了,但实际上只是rtt发生了突变而已),进而引起了协议栈的一系列行为,而这些行为不是最优的。spurious retransmission意味着发送端认为发送的包已经丢失了,然后就重传了,尽管此时接收端已经发送了对这些包的确认(确认还没收到或者已经丢失了)。这是需要在发送端抓包,分析原因。如果接收端以前收到过相同的包,接收端就会报spurious retransmission,但是发送端是不会报spurious retransmission的。

导致虚假超时重传的原因有很多种:(1)对于部分移动网络,当网络发生切换时会导致网络延时突增(2)当网络的可用带宽突然变小时,网络rtt会出现突增的情况,这会导致虚假超时重传(3)网络丢包(原始和重传的包都有可能丢包)会导致虚假重传超时。

 

PSH

TCP 模块什么时候将数据发送出去(从发送缓冲区中取数据),以及 read 函数什么时候将数据从接收缓冲区读取都是未知的。

如果使用 PSH 标志,上面这件事就确认下来了:

发送端

对于发送方来说,由 TCP 模块自行决定,何时将接收缓冲区中的数据打包成 TCP报文,并加上 PSH 标志(在图 1 中,为了演示,我们假设人为的干涉了 PSH 标志位)。一般来说,每一次 write,都会将这一次的数据打包成一个或多个 TCP 报文段(如果数据量大于 MSS 的话,就会被打包成多个 TCP 段),并将最后一个 TCP 报文段标记为 PSH。

当然上面说的只是一般的情况,如果发送缓冲区满了,TCP 同样会将发送缓冲区中的所有数据打包发送。

接收端

如果接收方接收到了某个 TCP 报文段包含了 PSH 标志,则立即将缓冲区中的所有数据推送给应用进程(read 函数返回)。

当然有时候接收缓冲区满了,也会推送。

解决方案

当前的解决方案是,只判断返回的第一个字符。比如:ioGlobalStdSet(1,ioGlobalStdGet())\n,则只需要有i就算返回成功。

另一种的解决办法,通过查看包,可以知道,每次命令的返回都有0a,也就是\n转义字符的十六进制。

所以,可以在主机端获取,直到获取到0a,则表明一个完整的命令接收成功,然后再判断,即可。