总图(Big Picture)
UDP
- UDP应用程序将数据封装到UDP数据报,,并将其写到UDP socket。进而封装成IPV4或IPV6数据报(datagram),然后发送到目的地
- UDP提供的是无连接的服务(connectionless service)
- 每个UDP数据报都有一个长度,如果一个数据报正确地到达目的地,那么数据报的长度将随数据一道传递给接收端应用程序
- RFC 768 [Postel 1980]
TCP
- 面向连接(Connection-oriented)
- 提供可靠传输(Reliable)
- TCP通过给每个字节关联一个序列号(sequence)对所发送的数据进行排序(sequencing)
- TCP提供流量控制(flow control)==> 控制接收端收包的速率
- 全双工(full-duplex)
TCP连接的建立和终止
三路握手 ———— Three-Way Handshake (SYN, ACK)
服务器通过socket,bind,listen创建一个监听socket,并调用accpet准备接受外来的连接(这些工作要在客户端请求来临之前做好)。
客户端通过socket函数创建一个未连接的socket,再调用connect尝试连接到服务器(此时客户端阻塞,等待服务器响应)。此时会向服务器发送一个SYN包(表明客户端欲向服务器申请服务),等待服务器的确认。
服务器收到SYN包,则向客户端发送一个ACK(告诉客户端,请求我已经收到)。同时发送一个SYN(通知客户端服务器需要知道客户端是否收到回复,收到则请回一个ACK)
客户端收到服务器发回的ACK和SYN之后,connect函数返回(表示连接服务器成功),并向服务器回一个ACK(告知服务器我已经收到你的回复)
服务器收到客户端的ACK以后accept函数返回(此时连接正式建立,可以开始后通信)
- ACK如果不捎带数据的话,是不占序列号的
- SYN占一个序列号
四路终止 ———— Four-Way Termination (FIN, ACK)
客户端调用close(任意一端都可以发起终止,此处以客户端发起为例,发起终止的一端称为执行主动关闭)。接着向服务器发送一个FIN,表示数据发送完毕
服务器接收到FIN后回复一个ACK(此时服务器称为执行被动关闭的一端),同时服务器的read函数会读到一个EOF,导致read函数退出
一段时间后,服务器也调用close,向客户端发送一个FIN
客户端收到FIN以后回复一个ACK
- FIN占一个序列号
- 在被动关闭一端收到FIN到其调用close函数之间的间隙,称为半关闭状态(half-close),在这段时间内理论上仍然有发送数据的可能(但是通过close方式关闭的话,时无法再进行通信的,因为close会同时关闭写一端和读一端,后面接收shutdown就可以只关闭写或读)
- 当一个Unix进程无论自愿(调用exit或main函数返回)还是非自愿(收到一个终止本进程的信号)的终止时,所有打开的描述符都将被关闭,这也导致主机向仍然打开的任何TCP连接上发出一个FIN(对于那些描述符只被这个终止的进程持有的连接而言)
TCP状态转换图
- 上述状态转移图中的细线表示的是可能执行的路线。粗线和虚线表示的是TCP服务器和客户端正常情况(或说大多数情况)走的状态转移路线。
- 当发生错误,或遇到一些比较凑巧的事件,就可能走细线指向的转移路线
- 举例来说:途中有一条细线是SYN_SENT ——> SYN_RCVD的
- 这种情况可能是客户端发出了一个SYN到服务器,并希望服务器回一个ACK和一个SYN
- 但是由于一些不可预知的错误,服务器返回的SYN和ACK只有SYN回到了客户端,而ACK则延迟了或丢了
- 这个时候客户端的状态就转移到了SYN_RCVD。之后一旦收到ACK(可能是服务器发送的消息丢包后重传,也可能是延迟的消息到达了),则客户端的状态可正常转移到ESTABLISHED状态
TCP的限制
- TCP同时提供可靠数据传输和严格的有序数据分发。然而有一些应用程序需要可靠数据传输,但是不需要顺序维护,而另一些应用过程序只需要部分有序即可。
- TCP这种面向数据流的传输方式有时候是不方便的。应用程序必须添加自己的记录标记。
- TCP套接字的范围有限,使得为多宿主主机提供高可用数据传输能力的任务变得复杂
- 容易受到拒绝服务攻击,例如SYN攻击
- 这些限制影响公共交换电话网的IP性能
端口号(Port Numbers)
- 众所周知(well-know)的端口:0 ~ 1023
- 已注册端口:1024 ~ 49151
- 动态或私有端口:49152 ~ 65535
TCP端口号与并发服务器