TCP服务的特点
TCP协议相对于UDP协议的特点是面向连接、字节流和可靠传输。
使用TCP协议通信的双方必须先建立链接,然后才能开始数据的读写。双方都必须为该链接分配必要的内核资源,以还礼链接状态和连接上数据的传输。TCP链接是全双工的,即双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放系统资源。
TCP协议的这种连接是一对一的,所以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP服务。而无连接协议UDP则非常适合于广播和多播。
字节流服务和数据报服务对应到实际编程中的区别体现在通信双方是否必须执行相同次数的读、写操作。当发送端应用程序连续执行多次写操作时,TCP模块先将这些数据放入TCP发送缓冲区中。当TCP模块哦真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文发出去。因此,TCP模块发出去的TCP报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系。
当接收端收到一个或多个TCP报文段后,TCP模块将它们携带的应用程序数据按照TCP报文段的序号依次放入TCP接收缓冲区中,并通知应用程序读取数据,接收端应用程序可以一次性将TCP接收缓冲区中的数据全部读出,也可以分多次读取,这取决于用户指定的应用程序读缓冲区的大小。因此,应用程序执行的读操作次数和TCP模块接收到的TCP报文个数之间也没有固定的数量关系。
综上所述,TCP发送端执行写操作次数和接收端执行读操作次数之间没有任何数量关系,这就是字节流的概念:应用程序对数据的发送和接收是没有边界限制的。
UDP则不然,发送端应用程序没执行一次写操作,UDP模块就将其封装成一个UDP数据包并发送之。接收端必须及时针对每一个UDP数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则UDP数据将被截断。
TCP传输室可靠的。首先,TCP协议采用发送应答机制,即发送端的每个TCP报文段都必须得到接收方的应答,才认为这个TCP报文段传输成功。其次,TCP协议采用超市重传机制,发送端在发送一个TCP报文段之后启动定时器,如果在定时时间内未收到应答,它将重发报文段。最后,因为TCP报文段最终是以IP数据报发送的,而IP数据报到达接收端可能乱序、重复,所以TCP协议还会对接收到的TCP报文段重排、整理,再交付给应用层。
TCP头部结构
头部结构
16位端口号:告知主机该报文来自哪里(源端口)以及传给哪个上层协议或应用程序(目的端口)。进行TCP通信时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名服务端口号。
32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号。假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN(初始化序号值)。那么在传输方向上,后续的TCP报文段中序号值将被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
32位确认好:用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1.
4位头部长度:标识该TCP头部有多少个32bit(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。
6位标识包含如下几项:
URG标志,表示紧急指针是否有效
ACK标志,表示确认好是否有效。我们称携带ACK标志的TCP报文段为确认报文段。
PSH标志,提示接收端应用程序应该立即从TCP接收缓冲区中读取数据,为接受后续数据腾出空间。
RST标志,表示请求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段。
SYN标志,表示请求建立一个连接。我们称携带SYN标志的TCP报文段为同步报文段。
FIN标志,表示通知对方本端要关闭连接了。我们称携带FIN标志的TCP报文段为结束报文段。
16位窗口大小:是TCP流量控制的一个手段。这里说的窗口,指的是接收通知窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法以校验TCP报文段在传输过程中是否损坏。
16位紧急指针:是一个正的偏移量。它和序号字段值相加表示最后一个紧急数据的下一字节的序号。TCP的紧急指针式发送端向接收端发送紧急数据的方法。
最后一个选项字段是可变长的可选信息,这里不做讨论。
TCP连接的建立和关闭
使用tcpdump观察TCP连接的建立和关闭
这里的测试网络及搭建见http://blog.csdn.net/walkerkalr/article/details/34420041
chen123@ubuntu:~$ sudo tcpdump -i eth0 -nt '(src192.168.73.130 and dst 192.168.73.129) or (src 192.168.73.129 and dst192.168.73.130)' chen123@ubuntu:~$ telnet 192.168.73.130 Trying 192.168.73.130... Connected to 192.168.73.130. Escape character is '^]'.
^](回车)#输入ctrl+]并回车
telnet> quit Connection closed.
在输入tcpdump后的终端会显示如下内容:
IP 192.168.73.129.43999 >192.168.73.130.23: Flags [S], seq 304007584, win 29200, options [mss1460,sackOK,TS val 1129159 ecr 0,nop,wscale 7], length 0 IP 192.168.73.130.23 >192.168.73.129.43999: Flags [S.], seq 3646952588, ack 304007585, win 28960,options [mss 1460,sackOK,TS val 1128235 ecr 1129159,nop,wscale 7], length 0 IP 192.168.73.129.43999 >192.168.73.130.23: Flags [.], ack 1, win 229, options [nop,nop,TS val 1129160ecr 1128235], length 0 IP 192.168.73.129.43999 >192.168.73.130.23: Flags [F.], seq 123, ack 329, win 229, options [nop,nop,TSval 1133956 ecr 1130769], length 0 IP 192.168.73.130.23 >192.168.73.129.43999: Flags [F.], seq 329, ack 124, win 227, options[nop,nop,TS val 1133032 ecr 1133956], length 0 IP 192.168.73.129.43999 >192.168.73.130.23: Flags [.], ack 330, win 229, options [nop,nop,TS val 1133957ecr 1133032], length 0
下图是TCP连接的建立和关闭时序图
第1个TCP报文段包含SYN标志,因此它是一个同步报文段,即chen123向li123发起请求。同时,该同步报文段包含一个ISN值为304007584的序号。第2个TCP报文段也是同步报文段,表示li123同意与chen123建立连接。同时发送自己的ISN值为3646952588的序号,并对第一个同步进行确认。确认值为304007585,即第1个同步报文段额序号值加1.前文说过,序号值是用来标识TCP数据流中的每一字节的。但同步报文比较特殊,即使它并没有携带任何应用程序数据,它也要占用一个序号值。第3个TCP报文段是chen123对第2个报文段的确认,但至于为什么ack是1,如有知道者请指教!至此,TCP连接就建立起来了。建立TCP连接的这3个步骤被称为TCP三次握手。
后面3个TCP报文段是关闭连接的过程。第4个TCP报文段包含FIN标志,因此它是一个结束报文段,即chen123要求关闭连接。结束报文段和同步报文段一样,也要占用一个序号值。li123用报文段5来确认结束该报文段,并发送自己的结束报文段。但有时,这一步会分为两个步骤,即将确认结束报文段和发送自己的结束报文段分为两次发送。报文段6是chen123发送给li123的确认结束报文段。
半关闭状态
TCP连接时全双工的,所以它允许两个方向的数据传输被独立关闭。换言之,通信的一段可以发送结束报文段给对方,告诉它本端已经完成了数据的发送,但允许继续接收来自对方的数据,直到对方也发送结束报文段以关闭连接。TCP连接的这种状态成为半关闭状态。
连接超时
如果客户端访问一个距离它很远的服务器,或者由于网络繁忙,导致服务器对客户端发送出的同步报文段没有应答,此时对于提供可靠服务的TCP来说,它必然先进行重连,如果仍然无效,则通知应用程序连接超时。
为了观察连接超时,在chen123上执行下面的操作:
chen123@ubuntu:~$ sudo iptables –F chen123@ubuntu:~$ sudo iptables -I INPUT -p tcp --syn-i eth0 -j DROP
在li123执行telnet难过了 chen123,并用tcpdump抓取这个过程中TCP报文段,如下操作:
li123@ubuntu:~$sudo tcpdump -n -i eth0 port 23 li123@ubuntu:/$date; telnet 192.168.73.129; date
在输入telnet的终端输出如下:
Thu Jun 2604:06:27 PDT 2014 Trying192.168.73.129... telnet: Unableto connect to remote host: Connection timed out Thu Jun 2604:08:34 PDT 2014
从两次date命令等输出来看,li123建立TCP连接的超时时间是127s。本次tcpdump的输出如下:
04:06:27.638626IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3877349 ecr 0,nop,wscale 7], length 0 04:06:28.638036 IP192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3877599 ecr 0,nop,wscale 7], length 0 04:06:30.643072IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3878100 ecr 0,nop,wscale 7], length 0 04:06:34.650500IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3879102 ecr 0,nop,wscale 7], length 0 04:06:42.658032IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3881104 ecr 0,nop,wscale 7], length 0 04:06:58.690290IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3885112 ecr 0,nop,wscale 7], length 0 04:07:30.722385IP 192.168.73.130.56817 > 192.168.73.129.23: Flags [S], seq 205703381, win29200, options [mss 1460,sackOK,TS val 3893120 ecr 0,nop,wscale 7], length 0
我们一共抓取了7个TCP报文段,他们都是同步报文段,并且具有相应的序号值,这说明后面5个同步报文段都是超时重发报文段。观察这些TCP报文段被发送的时间间隔,他们分别是1s、2s、4s、8s、16s和32s。因此TCP一共执行了6次重连重连操作,这是由/proc/sys/net/ipv4/tcp_syn_retries内核变量所定义的,每次重连的超时时间都增加了一倍。在6次重连均失败的情况下,TCP模块放弃连接并通知应用程序。
作者:walkerkalr 原文网址:http://blog.csdn.net/walkerkalr/article/details/34889527