[PSH ACK]收到正确http报文格式后每次都收到ACK len=0的http报文格式,为什么?高手求救!

作者:vfhky | 时间: 15:51 | 分类:
在Linux TCP通信的调试中,tcpdump应该算是很好的一个工具。这篇文章主要使用Windows作为客户端,向作为服务端的Linux中的一个socket监听端口发送报文信息,然后在Linux中用TCPDUMP工具进行抓包。通过这个实例,可以较为完整的了解TCP通信中的“三次握手”等过程。
1 CentOS服务端建立监听并抓包
在虚拟机服务器(192.168.1.178)使用下面这个简单的服务端程序,建立8000端口的监听服务,然后使用tcpdump -n port 8000命令抓包。
* @FileName
server_socket.c
* @Describe
A simple example for creating a listen as a server in linux system.
21:32 https://typecodes.com/linux/simpletcpdumpapply.html
* @Compile
gcc server_socket.c -o server_socket
#include &stdio.h&
#include &string.h&
#include &sys/types.h&
#include &sys/socket.h&
#include &netinet/in.h&
#include &arpa/inet.h&
#include &errno.h&
int main( int argc, char **argv )
int server_sockfd;
int client_sockfd;
int llOpt = 1;
struct sockaddr_in my_addr;
struct sockaddr_in remote_addr;
int sin_size;
char buf[BUFSIZ];
memset( &my_addr, 0, sizeof(my_addr) );
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(8000);
if( ( server_sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) & 0 )
perror(&socket&);
if( setsockopt( server_sockfd, SOL_SOCKET, SO_REUSEADDR, &llOpt, sizeof(llOpt) ) ) {
close(server_sockfd);
return errno;
if( bind( server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr) ) & 0 )
perror( &bind& );
listen( server_sockfd, 5 );
sin_size = sizeof( struct sockaddr_in );
printf( &Socket server begin to recieve connectiong from client.\n& );
if( ( client_sockfd = accept( server_sockfd, (struct sockaddr *)&remote_addr, &sin_size ) ) & 0 )
perror( &accept& );
printf( &Accept client[%s].\n&, inet_ntoa(remote_addr.sin_addr) );
memset( buf, 0x00, BUFSIZ );
while( ( len = recv( client_sockfd ,buf, BUFSIZ, 0) ) & 0 )
buf[len]='\0';
printf( &Message from client=[%s]\n&, buf );
close( client_sockfd );
close( server_sockfd );
2 Windows客户端发送请求报文
在本地Windows机器(192.168.1.109)使用报文发送工具连接到虚拟机服务器192.168.1.178,然后发送两次请求报文。
其中,第一次发送“11111”的报文:
第二次发送“123321”的请求报文:
3 Windows客户端关闭socket
点击左边配置菜单栏中的“关闭”按钮,断开tcp连接。
4 分析抓包的情况
在Linux服务端的抓包情况如下,其中抓包内容中出现冒号表示范围,例如seq 1:6表示seq序号范围从1到6。另外,抓包中的iRDMI代表iRDMI/Shoutcast Server,对应着8000端口。
[root@typecodes ~]# tcpdump -n port 8000
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
//内容格式
客户端IP和端口
服务端IP和端口
SYN(客户端主动请求连接服务器)
8192(Window size),
11:12:02.534303 IP 192.168.1.109.8886 & 192.168.1.178.irdmi: Flags [S], seq , win 8192, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
//服务器端发送SYN+ACK(.)的报文给客户端,
ack=seq+1=+1,
11:12:02.534339 IP 192.168.1.178.irdmi & 192.168.1.109.8886: Flags [S.], seq , ack , win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
//客户端收到确认包后,发送ACK(.)报文给服务端,
11:12:02.534528 IP 192.168.1.109.8886 & 192.168.1.178.irdmi: Flags [.], ack 1, win 256, length 0
//三次握手成功后,
客户端发送&11111&报文内容给服务端, 长度为5字节, 标识P即PSH(Push function)为1时, 代表要发送缓冲区的封包, 无需等待缓冲区满了才送
11:12:16.501177 IP 192.168.1.109.8886 & 192.168.1.178.irdmi: Flags [P.], seq 1:6, ack 1, win 256, length 5
//服务端收到客户端请求报文包后, 发送ACK(.)报文给客户端,
长度为0字节,
ACK=SEQ+LENGTH=1+5
11:12:16.501208 IP 192.168.1.178.irdmi & 192.168.1.109.8886: Flags [.], ack 6, win 115, length 0
//客户端继续发送请求报文, 内容为&123321&, 长度6字节
11:44:16.181474 IP 192.168.1.109.8886 & 192.168.1.178.irdmi: Flags [P.], seq 6:12, ack 1, win 256, length 6
//服务端发送ACK(.)应答报文给客户端, ack=SEQ+length=6+6=12
11:44:16.181508 IP 192.168.1.178.irdmi & 192.168.1.109.8886: Flags [.], ack 12, win 115, length 0
//客户端关闭后向服务端发送标识为(R+.ACK), RST(Reset)为1时, 表示tcp连接马上会被结束,而无需等待终止确认手续. 也就是说, 这是个强制结束的连线, 且发送端已经断开.
//这里即使服务端没有调用close(accept_sockfd)来向客户端发送FIN报文,两者之间建立的tcp链路都会被关闭!!!
11:50:55.047977 IP 192.168.1.109.8886 & 192.168.1.178.irdmi: Flags [R.], seq 12, ack 1, win 0, length 0
Please enable JavaScript to view the
Categories
Recent Posts请登录查看
作为网络管理员,很多时间必然会耗费在修复慢速服务器和其他终端。但用户感到网络运行缓慢并不意味着就是网络问题。
解决网络性能问题,首先从TCP错误恢复功能(TCP重传与重复ACK)和流控功能说起。之后阐述如何发现网络慢速之源。最后,对网络各组成部分上的数据流进行概况分析。这几张内容将会帮助读者识别,诊断,以及排查慢速网络。
接下来的内容,较多是黑白图片了。虽然看起来有点不爽,但还是很值得一看。
TCP错误恢复功能:
TCP的错误恢复功能是定位,诊断及修复网络延时的最佳工具。延时可以在单程也可以往返方向测量。高延时是网络管理员的头号大敌。本节我们讨论TCP高延时是如何导致序列号和确认号乱序的。
主机报文重传是TCP最基本的错误恢复功能,它的目的是防止报文丢失。
报文丢失的可能因素有很多种,包括应用故障,路由设备过载,或暂时的服务宕机。报文级别速度是很高的,而通常报文丢失是暂时的,因此TCP能够发现和恢复报文丢失显得尤为重要。
决定报文是否有必要重传的主要机制是重传计时器(retransmission timer),它的主要功能是维护重传超时(RTO)值。当报文使用TCP传输时,重传计时器启动,收到ACK时计时器停止。报文发送至接收到ACK的时间称为往返时间(RTT)。对若干次时间取平均值,该值用于确定最终RTO值。在最终RTO值确定之前,确定每一次报文传输是否有丢包发生使用重传计时器,下图说明了TCP重传过程。
当报文发送之后,但接收方尚未发送TCP ACK报文,发送方假设源报文丢失并将其重传。重传之后,RTO值加倍;如果在2倍RTO值到达之前还是没有收到ACK报文,就再次重传。如果仍然没有收到ACK,那么RTO值再次加倍。如此持续下去,每次重传RTO都翻倍,直到收到ACK报文或发送方达到配置的最大重传次数。
最大重传次数取决于发送操作系统的配置值。默认情况下,Windows主机默认重传5次。大多数Linux系统默认最大15次。两种操作系统都可配置。
示例如下图:
TCP重传过程发送的第一个报文如下图所示(图片不很清楚,已经尽力了):
这是一个TCP PSH/ACK报文①,包含648字节数据②,从10.3.30.1发送至10.3.71.7。这是一个典型的数据报文。
在通常情况下,第一个报文发送之后很快会收到TCP ACK报文。然而,在这个case里,第二个是重传报文。可以在Packet list面板里看到。Info栏清楚的标明“TCP Retransmission”,报文以黑色背景红色字体标出。下图是Packet List面板中的重传示例(仍然不清楚,但可参见上图):
也可以在Packet Details和Packet Bytes面板中查看来确定是否是重传报文,如下图所示:
注意此报文与源报文相同(除了IP标识和checksum字段)。要验证这一点,比较两个报文的Packet Bytes①。
在Packet Details面板,注意到重传报文在SEQ/ACK Analysis下面有些额外的信息②。这些信息是由Wireshark提供的而并非报文本身。SEQ/ACK Analysis告诉我们这确实是一个重传报文,RTO值是0.206秒,此时的RTO是基于报文1的时间增量。
检查剩下的报文会得到类似的结果,不同之处只有IP标识和checksum,以及RTO值。要使报文之间的时间间隔形象化,在Packet List面板中查看Time栏,如下图所示。这里可以看到RTO值的翻倍增长关系。
TCP重复ACK以及快速重传:
重复ACK是指在接收方收到乱序报文时,所发出的一类TCP报文。TCP使用报文头的序列号和确认号以有效保证数据按照发送的顺序接收和重组。
当TCP连接建立以后,握手过程中交换的一个最重要的信息是初始序列号(ISN)。一旦连接双方设定了ISN之后,接下来发送的报文所包含的序列号增加一个数据载荷值。
假设有个主机ISN是5000,发送500字节报文至接收方。一旦报文接收之后,接收端回复一个ACK号为5500的TCP ACK报文,基于以下公式:
Sequence Number In + Bytes of Data Received = Acknowledgment Number Out
按照上述计算结果,返回发送端的确认编号实际上是接收端希望收到的序列号。示例如下图:
数据接收方通过序列号来检查报文丢失。接收方通过追踪接收到的序列号,能够确认序列号是否乱序。当接收方收到一个不正常的序列号,它会假设传输过程中有报文丢失。为了正确重传数据,接收方必须拥有丢失报文,所以它发送包含有丢失报文正确序列号的ACK报文,以便发送方重传此报文。
当重传主机从发送端接收到3个重复ACK时,它会假设此报文确实在传送中丢失,并且立即发送一个快速重传。一旦触发了快速重传,所有正在传输的其他报文都被放入队列中,直到快速重传报文发送为止。过程如下图所示:
承接上文的彩图:
本例中第一个报文如下图:
此文件中的第四个报文是发送端所发出具有错误序列号①的另一个数据块。因此,接收端发送第二个重复ACK②。接收端又收到一个乱序报文③。从而触发了第三以及最后一个重复ACK④.
一旦发送方接收到接收方所发来的第三个重复ACK,它就会暂停所有传输报文并且重传丢失报文,下图显示了快速重传过程:
重传报文同样可以通过Packet List面板的Info栏观察到。报文呈现黑色背景红色字体。这个报文的SEQ/ACK Analysis截面告诉我们这可能是一个快速重传帧。(标识报文为快速重传的信息不是报文本身所包含的内容,而是Wireshark的功能)。最后一个报文是接收到快速重传的ACK。
意见反馈:
联系方式:
广告等垃圾信息
不友善内容
违反法律法规的内容
不宜公开讨论的政治内容客户端多次RST以及不同场景下的RST报文的差异
在某个TCP交互过程中,我们发现在交互的后期,客户端多次向服务器端发送RST报文,如下图所示:
我们首先来看客户端发出的第一个RST报文的解码:
RST与ACK标志位都置一了,并且具有ACK number,非常明显,这个报文在释放TCP连接的同时,完成了对前面已接收报文的确认。
我们再来看看客户端发出的后续RST报文的解码:
我们可以看到,这些后续的RST报文仅Reset位置一,ACK位未置一,在这种情况下,该报文的ACK确认号应该为0,但是我们留意到在这个报文中,其ACK确认号与序列号是一致的。
这是为什么呢?
因为ACK位未置一,ACK确认号也就失去了意义,因此,不论ACK确认号是什么值都不会对接收端产生影响,因此大部分的系统都会将ACK确认号设置为0,之所以在这个报文中出现ACK确认号非0而是与序列号一致的情况,个人认为应该是该主机端系统的处理机制与大部分系统不一样导致的。
另外,我们也看到了wireshark的专家系统在此处给出了提示,由此可见wireshark在传输层的专家系统的强大之处。
为什么前后RST报文会出现这种差异?
原因为第一个RST报文是异常释放TCP连接的,在端系统发送RST报文之前,这个TCP连接尚在端系统的连接表中,因此其ACK位置一并且具有ACK确认号。而客户端后续收到DATA报文,因其连接表中已经没有相关信息与之对应,此时客户端发送的RST报文ACK位无需置一。
也许有朋友会问:服务器端为什么在收到客户端的RST报文后,还继续给客户端发送报文呢?
原因只有一个,那就是TCP成块数据流。服务器端一次性向客户端发送数个数据块,在客户端发出第一个RST报文之后,后续的报文已经在网络中传输了,并陆续达到客户端。
其交互过程大致如下:
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!linux下客户端与服务器建立连接时,发送SYN报文并收到对方SYN,ACK报文后,客户端发送RST报文,这是是什么原因导致的啊?
[问题点数:40分]
本版专家分:0
结帖率 50%
CSDN今日推荐
本版专家分:35823
2013年8月 Linux/Unix社区大版内专家分月排行榜第一2012年11月 Linux/Unix社区大版内专家分月排行榜第一2012年10月 Linux/Unix社区大版内专家分月排行榜第一2012年9月 Linux/Unix社区大版内专家分月排行榜第一2012年7月 Linux/Unix社区大版内专家分月排行榜第一2012年6月 Linux/Unix社区大版内专家分月排行榜第一2012年5月 Linux/Unix社区大版内专家分月排行榜第一2011年11月 Linux/Unix社区大版内专家分月排行榜第一
2013年6月 Linux/Unix社区大版内专家分月排行榜第二2013年5月 Linux/Unix社区大版内专家分月排行榜第二2013年3月 Linux/Unix社区大版内专家分月排行榜第二2013年1月 Linux/Unix社区大版内专家分月排行榜第二2012年12月 Linux/Unix社区大版内专家分月排行榜第二2012年8月 Linux/Unix社区大版内专家分月排行榜第二2011年12月 Linux/Unix社区大版内专家分月排行榜第二2011年10月 C/C++大版内专家分月排行榜第二2011年10月 Linux/Unix社区大版内专家分月排行榜第二
2012年6月 C/C++大版内专家分月排行榜第三2012年6月 PHP大版内专家分月排行榜第三2012年5月 C/C++大版内专家分月排行榜第三2012年3月 Linux/Unix社区大版内专家分月排行榜第三2012年2月 Linux/Unix社区大版内专家分月排行榜第三2011年11月 C/C++大版内专家分月排行榜第三
本版专家分:601
本版专家分:30
匿名用户不能发表回复!|
CSDN今日推荐TCP/IP报文头部结构
时间: 18:50:34
&&&& 阅读:37
&&&& 评论:
&&&& 收藏:0
标签:IP协议
IP协议是TCP/IP协议族的动力,它为上层协议提供无状态、无连接、不可靠的服务。 优点:简单,高效。
IPv4首部一般是20字节长。在以太网帧中,IPv4包首部紧跟着以太网帧首部,同时以太网帧首部中的协议类型值设置为0800无状态:IP通信双方不同步传输数据的状态信息,所有的IP数据报的传输都是独立的。所以容易发生重复和乱序的情况并且IP层不予处理。 然后将这些乱序的交给上层传输层(TCP/UDP等)来处理,将其处理成有序的,正确的。再交给应用层。 不可靠:IP协议不能保证IP数据报准确到达。所以它提供ICMP报文来辅助,一旦检测到IP数据报发送失败,通知上层协议。
版本号(Version): IP协议的版本号。有IPv4和IPv6,现在主流是IPv4。IP包头长度(Header Length):IP包头的长度IP包头最小长度为20字节,最大60字节。服务类型(Type of Service)包含一个4位优先权字段:最小延时,最大吞吐量,最高可靠性和最小费用。IP包总长(Total Length):以字节为单位计算的IP包的长度 (包括头部和数据),所以IP包最大长度65535字节,但由于MTU限制,一般无法到达这个值。标识符(Identifier):唯一的标识数据报。该字段和Flag和Fragment Offest字段联合使用,对大的上层数据包进行分段(fragment)操作。标记(Flag):当一个包过大被分片时,用来标识属于同一个包的片段。该字段第一位不使用。第二位是DF位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF位,当路由器对一个上层数据包分段,则路由器会在最后一个分段的IP包的包头中将MF位设为0,其余IP包的包头中将MF设为1。Bit 0: reserved, must be zeroBit 1: (DF) 0 = May Fragment, 1 = Don‘t Fragment.Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.分段序号(Fragment Offset):分片相对原始IP数据报开始处的偏移。由于IP包在网络上传送的时候不一定能按顺序到达,这个字段保证了目标路由器在接受到IP包之后能够还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送是丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。生存时间(Time to Live):当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包经过每一个沿途的路由器的时候,每个沿途的路由器会将IP包的TTL值减少1。如果TTL减少为0,则该IP包会被丢弃。这个字段可以防止由于故障而导致IP包在网络中不停被转发。协议(Protocol):长度8比特。标识了上层所使用的协议。用来区分上层协议(ICMP为1,TCP为6,UDP为17)。头部校验(Header Checksum):由于IP包头是变长的,仅以CRC算法检验数据报头部在传输过程中是否损坏。源IP地址(Source IP Addresses):标识了这个IP包的源IP地址。目的IP地址(Destination IP Address):标识了这个IP包的目的IP地址。可选项(Options):记录路由,告诉途径得所有路由把IP填进来。可用的IP选项包括:记录路由(record route),告诉数据报途经的所有路由器都将自己的IP地址填入IP头部的选项部分,这样我们就可以跟踪数据报的传递路径。时间戳(timestamp),告诉每个路由器都将数据报被转发的时间(或时间与IP地址对)填入IP头部的选项部分,这样就可以测量途经路由之间数据报传输的时间。松散源路由选择(loose source routing),指定一个路由器IP地址列表,数据报发送过程中必须经过其中所有的路由器。严格源路由选择(strict source routing),和松散源路由选择类似,不过数据报只能经过被指定的路由器。填充(Padding):因为IP包头长度(Header Length)部分的单位为32bit,所以IP包头的长度必须为32bit的整数倍。因此,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍
TCP(TransmissionControl Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。TCP连接包括三个状态:连接创建、数据传送和连接终止。
源目端口号:计算机上的进程要和其他进程通信是要通过计算机端口的,而一个计算机端口某个时刻只能被一个进程占用,所以通过指定源端口和目标端口,就可以知道是哪两个进程需要通信。TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。序列号:表示本报文段所发送数据的第一个字节的编号。在TCP连接中所传送的字节流的每一个字节都会按顺序编号。确认号:表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。也就是告诉发送发:我希望你(指发送方)下次发送的数据的第一个字节数据的编号是这个确认号。也就是告诉发送方:我希望你(指发送方)下次发送给我的TCP报文段的序列号字段的值是这个确认号。TCP首部长度:由于TCP首部包含一个长度可变的选项部分,所以需要这么一个值来指定这个TCP报文段到底有多长。或者可以这么理解:就是表示TCP报文段中数据部分在整个TCP报文段中的位置。该字段的单位是32位字,即:4个字节。URG:表示本报文段中发送的数据是否包含紧急数据。URG=1,表示有紧急数据。后面的紧急指针字段只有当URG=1时才有效。ACK:表示是否前面的确认号字段是否有效。ACK=1,表示有效。只有当ACK=1时,前面的确认号字段才有效。TCP规定,连接建立后,ACK必须为1。PSH:告诉对方收到该报文段后是否应该立即把数据推送给上层。如果为1,则表示对方应当立即把数据提交给上层,而不是缓存起来。RST:只有当RST=1时才有用。如果你收到一个RST=1的报文,说明你与主机的连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接。或者说明你上次发送给主机的数据有问题,主机拒绝响应。SYN:在建立连接时使用,用来同步序号。当SYN=1,ACK=0时,表示这是一个请求建立连接的报文段;当SYN=1,ACK=1时,表示对方同意建立连接。SYN=1,说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中SYN才置为1。FIN:标记数据是否发送完毕。如果FIN=1,就相当于告诉对方:“我的数据已经发送完毕,你可以释放连接了”窗口大小:表示现在运行对方发送的数据量。也就是告诉对方,从本报文段的确认号开始允许对方发送的数据量。校验和:提供额外的可靠性。具体如何校验,参考其他资料。紧急指针:标记紧急数据在数据字段中的位置。选项部分:其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*4-20=40字节。选项部分的应用:1.
MSS最大报文段长度(Maxium Segment Size):指明数据字段的最大长度,数据字段的长度加上TCP首部的长度才等于整个TCP报文段的长度。MSS值指示自己期望对方发送TCP报文段时那个数据字段的长度。通信双方可以有不同的MSS值。如果未填写,默认采用536字节。MSS只出现在SYN报文中。即:MSS出现在SYN=1的报文段中。2.
窗口扩大选项(Windows Scaling):由于TCP首部的窗口大小字段长度是16位,所以其表示的最大数是65535。但是随着时延和带宽比较大的通信产生(如卫星通信),需要更大的窗口来满足性能和吞吐率,所以产生了这个窗口扩大选项。3.
SACK选择确认项(Selective Acknowledgements):用来确保只重传缺少的报文段,而不是重传所有报文段。比如主机A发送报文段1、2、3,而主机B仅收到报文段1、3。那么此时就需要使用SACK选项来告诉发送方只发送丢失的数据。那么又如何指明丢失了哪些报文段呢?使用SACK需要两个功能字节。一个表示要使用SACK选项,另一个指明这个选项占用多少字节。描述丢失的报文段2,是通过描述它的左右边界报文段1、3来完成的。而这个1、3实际上是表示序列号,所以描述一个丢失的报文段需要64位即8个字节的空间。那么可以推算整个选项字段最多描述(40-2)/8=4个丢失的报文段。4.
时间戳选项(Timestamps):可以用来计算RTT(往返时间),发送方发送TCP报文时,把当前的时间值放入时间戳字段,接收方收到后发送确认报文时,把这个时间戳字段的值复制到确认报文中,当发送方收到确认报文后即可计算出RTT。也可以用来防止回绕序号PAWS,也可以说可以用来区分相同序列号的不同报文。因为序列号用32为表示,每2^32个序列号就会产生回绕,那么使用时间戳字段就很容易区分相同序列号的不同报文。5.
NOP(NO-Operation):它要求选项部分中的每种选项长度必须是4字节的倍数,不足的则用NOP填充。同时也可以用来分割不同的选项字段。如窗口扩大选项和SACK之间用NOP隔开。标签:原文地址:http://blog.51cto.com/8470
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!}

我要回帖

更多关于 ip报文格式 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信