TCP协议与UDP协议 (the difference of tcp and udp protocol)

TCP和UDP是TCP/IP协议中的两个传输层协议,它们使用IP路由功能把数据包发送到目的地,从而为应用程序及应用层协议(包括:HTTP、SMTP、SNMP、FTP和Telnet)提供网络服务。

    TCP,传输控制协议,Transmission Control Protocol,提供的是面向连接、可靠的字节流服务。

    UDP,用户数据报协议,User Datagram Protocol,无面向连接的、不可靠的、无序的、无流量控制的传输层协议。

    在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如上图所示。

 (1) 第一次握手:建立连接时,Client发送SYN包(SYN=j)到Server,并进入SYN_SEND状态,等待Server确认。

 (2) 第二次握手:Server收到SYN包,必须确认Client的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时Server进入SYN_RECV状态。

 (3) 第三次握手:Client收到Server的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,Client和Server进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

 

    TCP四次挥手关闭连接。由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

(1)Client发送一个FIN,用来关闭Client到Server的数据传送(报文段4)。

(2)Server收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。

(3)Server关闭与Client的连接,发送一个FIN给Client(报文段6)。

(4)Client发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

 

1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这个问题可以参考《unix 网络编程》(第三版,2.7 TIME_WAIT状态)。

TIME_WAIT状态由两个存在的理由。

(1)可靠的实现TCP全双工链接的终止。

这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

 (2)允许老的重复的分节在网络中消逝。

假设在12.106.32.254的1500端口和206.168.1.112.219的21端口之间有一个TCP连接。我们关闭这个链接,过一段时间后在相同的IP地址和端口建立另一个连接。后一个链接成为前一个的化身。因为它们的IP地址和端口号都相同。TCP必须防止来自某一个连接的老的重复分组在连接已经终止后再现,从而被误解成属于同一链接的某一个某一个新的化身。为做到这一点,TCP将不给处于TIME_WAIT状态的链接发起新的化身。既然TIME_WAIT状态的持续时间是MSL的2倍,这就足以让某个方向上的分组最多存活msl秒即被丢弃,另一个方向上的应答最多存活msl秒也被丢弃。通过实施这个规则,我们就能保证每成功建立一个TCP连接时。来自该链接先前化身的重复分组都已经在网络中消逝了

 

3. 为什么不能用两次握手进行连接?

我们知道,3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
       现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发  送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S  是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

 

补充:

a. 默认情况下(不改变socket选项),当你调用close( or closesocket,以下说close不再重复)时,如果发送缓冲中还有数据,TCP会继续把数据发送完。

b. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接收数据。

c. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用recv时,如果返回0,则表示对端关闭。在这个时候通常的做法就是也调用close,那么TCP层就发送FIN,继续完成四次握手。如果你不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。

d. 在很多时候,TCP连接的断开都会由TCP层自动进行,例如你CTRL+C终止你的程序,TCP连接依然会正常关闭,你可以写代码试试。

 

插曲:

   特别的TIME_WAIT状态:

 

   从以上TCP连接关闭的状态转换图可以看出,主动关闭的一方在发送完对对方FIN报文的确认(ACK)报文后,会进入TIME_WAIT状态。TIME_WAIT状态也称为2MSL状态。

 

   什么是2MSL?MSL即Maximum Segment Lifetime,也就是报文最大生存时间,引用《TCP/IP详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么,2MSL也就是这个时间的2倍。其实我觉得没必要把这个MSL的确切含义搞明白,你所需要明白的是,当TCP连接完成四个报文段的交换时,主动关闭的一方将继续等待一定时间(2-4分钟),即使两端的应用程序结束。你可以写代码试试,然后用setstat查看下。

 

   为什么需要2MSL?根据《TCP/IP详解》和《The TCP/IP Guide》中的说法,有两个原因:

   其一,保证发送的ACK会成功发送到对方,如何保证?我觉得可能是通过超时计时器发送。这个就很难用代码演示了。

   其二,报文可能会被混淆,意思是说,其他时候的连接可能会被当作本次的连接。直接引用《The TCP/IP Guide》的说法:The second is to provide a “buffering period” between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.

 

   TIME_WAIT状态所带来的影响:

   当某个连接的一端处于TIME_WAIT状态时,该连接将不能再被使用。事实上,对于我们比较有现实意义的是,这个端口将不能再被使用。某个端口处于TIME_WAIT状态(其实应该是这个连接)时,这意味着这个TCP连接并没有断开(完全断开),那么,如果你bind这个端口,就会失败。对于服务器而言,如果服务器突然crash掉了,那么它将无法再2MSL内重新启动,因为bind会失败。解决这个问题的一个方法就是设置socket的SO_REUSEADDR选项。这个选项意味着你可以重用一个地址。

 

   对于TIME_WAIT的插曲:

   当建立一个TCP连接时,服务器端会继续用原有端口监听,同时用这个端口与客户端通信。而客户端默认情况下会使用一个随机端口与服务器端的监听端口通信。有时候,为了服务器端的安全性,我们需要对客户端进行验证,即限定某个IP某个特定端口的客户端。客户端可以使用bind来使用特定的端口。对于服务器端,当设置了SO_REUSEADDR选项时,它可以在2MSL内启动并listen成功。但是对于客户端,当使

用bind并设置SO_REUSEADDR时,如果在2MSL内启动,虽然bind会成功,但是在windows平台上connect会失败。而在linux上则不存在这个问题。(我的实验平台:winxp, ubuntu7.10)

    要解决windows平台的这个问题,可以设置SO_LINGER选项。SO_LINGER选项决定调用close时TCP的行为。SO_LINGER涉及到linger结构体,如果设置结构体中l_onoff为非0,l_linger为0,那么调用close时TCP连接会立刻断开,TCP不会将发送缓冲中未发送的数据发送,而是立即发送一个RST报文给对方,这个时候TCP连接就不会进入TIME_WAIT状态。如你所见,这样做虽然解决了问题,但是并不安全。通过以上方式设置SO_LINGER状态,等同于设置SO_DONTLINGER状态。

 

    断开连接时的意外:

    这个算不上断开连接时的意外,当TCP连接发生一些物理上的意外情况时,例如网线断开,linux上的TCP实现会依然认为该连接有效,而windows则会在一定时间后返回错误信息。这似乎可以通过设置SO_KEEPALIVE选项来解决,不过不知道这个选项是否对于所有平台都有效。

 

 

1.记录与字节流
 
  UDP协议:发送进程在发送每个数据报的时候并不等待多个数据报集中在一起以一个较大数据报发送出去,而是立即发送出去,它是记录型的协议。并且接收进程每次通过read或recv……获得的数据报必定是发送进程所发送的那个数据报不可能是多个数据报,接收进程可以识别到发送进程所发送的每个数据报的记录边界。
 
  TCP协议:发送进程在发送每个数据报的时候在内核处理过程中有可能并不立即发送出去,而是会将多个数据报集中在一起以一个较大的数据报来发送,它是字节流的协议。而接收进程每次通过read来读取发送进程发送过来的数据报并不一定是发送进程原先发送数据报,接收进程无法识别每个数据报的记录边界,所以TCP协议就是字节流的、无记录边界的协议。
 
  例如:QQ聊天所用到的协议就应该是有记录边界的,聊天过程中是以“消息”为单位,消息可以看成一个记录,所以QQ聊天协议采取UDP协议而不是TCP协议。
 
2.有序与无序
 
  UDP协议:发送进程所发送的每个数据报并不按照原先发送的顺序到达接收进程,有可能早发送的数据报较后到达接收进程。因为数据报在经过中间路径的传送时会因为各个数据报传送的路径不同或者其它原因而造成这些数据报到达的顺序不同,UDP协议是无序的传输协议。所以为了使基于UDP协议的应用程序有序,必须在应用程序中设置序号、确认机制来使其有序。
 
  TCP协议:有序协议,有超时、序号、重传、确认机制。
 
  例如:FTP协议是用于传送文件的协议,为了确保在传送文件内容的时候,传送的每个数据报协议有序接收,所以FTP协议是基于TCP协议。
 
  那为什么TFTP协议是基于UDP协议?因为为了保证有序,TFTP协议中引入了确认、序号字段。
 
  这里还有一个问题,FTP协议中的控制连接传送的内容好像都是基于消息形式,客户端在控制连接上发出一个请求消息,服务器端返回一个请求结果消息,感觉应该FTP控制连接采取UDP协议,为什么采取TCP协议?因为控制连接上是交互式的消息传送,客户端在发送一个请求之后,在服务器端的响应消息未到达之前,客户端是不会发送第二个请求消息,所以不用担心这两个请求消息会叠加在一起。也就是对于交互式的消息传递也可以采用TCP协议。
 
3.流量控制
 
  UDP协议:没有流量控制机制,如果发送进程发送数据报塞满了接收进程的接收缓冲区,就会丢弃数据报。出现这种情况,UDP协议不会通知发送进程减缓数据的发送速率。
 
  TCP协议:拥有流量控制。
 
4.客户端通信过程比较
 
 4.1 客户端的连接过程比较
 
  UDP协议在创建插口之后,可以同多个服务器端建立通信,而TCP协议只能与一个服务器端建立通信,TCP不允许目的地址是广播或多播地址,UDP允许。UDP协议客户端同服务器端的通信关系可以是一对多的关系,而TCP协议只能是一对一的关系。
 
  当然UDP协议也可以像TCP协议一样,通过connect来指定对方的ip地址、端口(对应下图1中的③操作),connect是插口连接操作,connect操作之后代表对应的插口已连接,与TCP协议不同,UDP的connect实现不包含三向握手。不管是UDP协议还是TCP协议,connect实现的共同部分都包括:若所指定插口的本地地址、端口未指定,那么connect的时候由内核为其指定本地地址、本地端口,内核根据插口中的目的地址来判断外出接口,然后指定该外出接口的IP地址为插口的本地地址。UDP协议通过connect操作之后同服务器端的通信关系成为一对一关系,不再是一对多的关系,而且这时也不能指定目的地址为广播或多播地址,因为connect函数不允许目的地址为广播或多播地址。UDP协议经过 connect之后,在通过sendto来发送数据报时不需要指定目的地址、端口,如果指定了目的地址、端口,那么会返回错误。通过UDP协议可以给同一个插口指定多次connect操作,而TCP协议不可以,TCP只能指定一次connect操作。UDP协议指定第二次connect操作之后会先断口第一次的连接,然后建立第二次的连接。
 
  客户端在建立同服务器端的连接过程中,第一步都会通过socket建立连接套接字,然后通过bind来绑定本地地址、本地端口,当然绑定操作可以不用指定。
 
 UDP协议:若未指定绑定操作,那么可以通过下面connect操作来由内核负责插口的绑定操作,若connect又未指定,那么绑定操作只好通过插口的写操作(sendto、sendmsg)来指定目的地址、端口,这时插口本地地址不会指定,为通配地址,而本地端口由内核指定,第一次sendto 操作之后,插口的本地端口经过内核指定之后就不会更改。
 
  TCP协议:若未指定绑定操作,可以通过下面connect操作来由内核负责插口的绑定操作。内核会根据插口中的目的地址来判断外出接口,然后指定该外出接口的IP地址为插口的本地地址。Connect操作对于TCP协议的客户端是必不可少的,必须指定。
 
  (不管是UDP协议还是TCP协议,所对应插口经过connect操作之后就是已连接的插口,未经过connect就代表未连接的插口。)
 
  通过bind来绑定本地地址、本地端口的时候,不管是已连接的还是未连接的插口,如果存在某一个插口的本地端口同用户所要绑定的本地端口相同,都会返回EADDRINUSE(Address already in use)错误。如果要绑定同已存在插口的本地端口相同的端口,必须先设置插口选项SO_REUSEADDR,然后再绑定。在linux系统中如果绑定的本地地址不同而本地端口相同可以不用设置插口选项SO_REUSEADDR,而对于其它的类UNIX系统根据《unix网络编程》中所描述的都要预先设置 SO_REUSEADDR插口选项。
对于TCP协议绝不允许绑定的本地地址、端口同已存在的插口(不管是已连接的还是未连接的插口)相同。对于UDP协议通过设置插口选项 SO_REUSEPORT,允许绑定相同的本地地址、本地端口。在linux系统中,没有SO_REUSEPORT这个选项,所以在linux系统中 UDP协议同TCP协议一样都不允许存在两个插口有相同的本地地址、本地端口。
 
  TCP协议同UDP协议还有一个很大的不同点:例如有一台多宿主机,它所拥有的IP地址有A、B、C,现在创建4个相同的TCP监听端口port,对应的四个插口地址结构(*,port)、(A,port)、(B,port)、(C,port),现在有客户端要同(A,port)建立连接,那么只会同(A,port)插口建立连接,而不会同拥有通配地址*的插口建立连接。而如果是创建4个相同的UDP监听端口port,对应的四个插口地址结构(*,port)、(A,port)、(B,port)、(C,port),那么有客户端要同(A,port)建立通信,那么发送到(A,port)的数据报也会拷贝一份到(*,port)插口。原因:TCP协议之间的通信是一对一的关系,而UDP可以是一对多的关系。

4.2 服务器端的连接过程比较
 
  对于UDP协议客户端与服务器端没有什么本质的区别,每个UDP协议的客户端也是服务器端。而TCP协议就不同了,TCP协议必须通过listen 来申请监听,然后通过accept来接收一个客户端的连接,当接收客户端的连接会再创建一个单独的插口用来同客户端之间进行数据通信,也就是说服务器端由一个单独的监听插口负责监听客户端的连接请求,当接收到一个来自客户端的连接请求之后,服务器会另外创建一个插口负责同客户端之间进行连接通信。
 
  服务端在通过bind来绑定本地地址、本地端口的时候应注意的情况,同客户端是相同的。

 

 

 

本文选自网络多处:http://blog.chinaunix.net/uid-25002135-id-3314682.html

http://www.itxxk.com/news/2013.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值