1. 用UDP实现可靠传输
在前面介绍了UDP是一种不可靠的面向数据包的传输协议。那么如何实现UDP的可靠传输呢?
其实就是在应用层自己实现像TCP一样的可靠性机制:引入序列号、引入确认应答、超时重传、流量控制、拥塞控制等机制。
2. listen系统接口
在之前的socket编程的时候用到了listen接口,想和之socket为监听状态,也是TCP所特有的一个接口;函数原型如下:
int listen(int sockfd, int backlog);
返回值:成功返回0,失败返回-1。
下面就详细讲解一下listen的第二个参数backlog:
基于TCP的客户/服务端程序在三次握手建立链接的时候,会有两个队列:
- 半连接队列:当客户端在向服务端发送SYN想要建立链接时,服务端会在半连接队列中创建一个记录,此时还没有完成三次握手。
- 全连接队列:当三次握手完成后,链接从半连接队列移动到全连接队列
在 Linux 中,backlog
指定了全连接队列的大小,而半连接队列的大小由 tcp_max_syn_backlog
系统参数控制。tcp允许最多有backlog+1个完整的链接被建立。
也就是说在全连接队列中的链接是服务端和客户端是处于established状态的,半连接队列中的链接客户端是处于syn_sent / established状态的,服务端是处于syn_rcvd状态。三次握手完成后,链接建立成功,将链接从半连接队列移入全连接队列,服务端也从syn_rcvd状态变味了establised状态,等待accept调用来取走这个链接。
下图为一个多进程版本的服务器例子,当我们在代码中将backlog设置为2时, 只建立了三个链接,第四个链接的服务端处于syn_recv状态,该链接在半连接队列中。
下面为三次握手的状态变化:
-
调用
listen
函数:在服务器端调用listen
函数后,套接字进入 LISTEN 状态。这是一个被动的状态,表示服务器准备接受连接请求,但并不直接处理连接。listen
函数的成功返回表示套接字已进入 LISTEN 状态,但此时不涉及连接的实际接收和处理。 -
客户端发起连接:客户端向服务器发送一个带有SYN标志的TCP连接请求(SYN),这是连接建立的第一步。客户端进入SYN_SEND状态。
-
服务器接收到SYN请求:服务器端的套接字在 LISTEN 状态时接收到客户端的SYN请求。此时,服务器将这个连接请求放入半连接队列(也称为SYN队列)。连接状态此时是 SYN-RCVD。
-
处理连接请求:服务器向客户端发送SYN-ACK响应,表示接受连接请求并准备建立连接。
-
客户端发送ACK确认:客户端发送ACK响应,完成三次握手。客户端变为ESTABLISHED状态。
-
三次握手完成:
- 当服务器接收到客户端的ACK响应时,连接状态从 SYN-RCVD 变为 ESTABLISHED。
- 此时,连接从半连接队列(SYN队列)移到全连接队列(已连接队列)。
-
accept
函数的作用:服务器应用程序调用accept
函数来从全连接队列中取出一个已完成三次握手的连接,并为其分配一个新的已连接套接字,用于实际的数据通信。