UNIX网络编程--TCP网络编程中的listen

int listen(int fd, int backlog);  

 

有几个概念需要在开头澄清一下

TCP socket分两种,监听socket和传输socket两种

监听socket:负责处理网络上来的连接请求(客户端的syn包到达便是连接请求来了,如果不知道syn包,请参看一下TCP三次握手);

传输socket:负责在网络上的两个端点之间传输TCP数据。

 

未决socket:pending socket,就是某客户端的syn包到达,内核为这个syn包对应的tcp请求生成一个socket,但是此时三次握手并没有完成,这样的socket就是pending socket,是未决连接,没有经过三次握手认证的tcp连接。这些套接字处理SYN_RCVD状态。

已建立连接的socket:established socket,tcp服务器利用三次握手完成对客户端的简单认证之后,未决socket就变成已连接socket,后续可以用这个socket传输数据。这些套接字处于ESTABLISHED状态。

两个队列之和不超过backlog。

内核为每个tcp服务器维护两个socket队列:未决socket队列和已建立连接的socket队列

UNIX网络编程--TCP网络编程中的listen_第1张图片

图 1

 

现在进入主题:)

在TCP服务器端创建socket完毕,调用listen函数的时候,系统下层发生了以下动作:

1. 将刚才创建的(fd所标示的)socket转换为此tcp服务器的监听socket,让此socket进入监听请求模式,此socket的tcp状态由CLOSE转至LISTEN.

2.内核为此监听socket所对应的tcp服务器建立一个未决socket队列和一个已建立连接socket队列

backlog这个参数用来决定未决socket队列的长度,有个映射关系,0表示长度可以无限大。

 

现在来串一串整个过程

监听socket收到某客户端的syn包,第一次握手完成;

然后内核为此syn请求生成一个pending socket,例如图1中的socket5,标记状态为SYN_RECV,并且将socket5加入相应的pending socket队列,并且服务器发出ack和syn,第二次握手完成。

后续针对此socket5有两种可能

1可能

过会儿客户端响应了服务器的syn(第三个ack到达),第三次握手结束。内核触发accept函数执行,将socket5状态标记为ESTABLISHED,并且将此socket5由pending socket queue移至establishedsocket queue,如图2

UNIX网络编程--TCP网络编程中的listen_第2张图片

             图2

 

2可能

客户端的最后一个ack并未来到,过很久,图1中的socket5超时了,被移除,如图3

UNIX网络编程--TCP网络编程中的listen_第3张图片

            图3

 

到这儿,listen的作用应该清楚了

 

另外针对此listen,有两个极限情况导致的拒绝服务情况需要考虑

backlog设置过小,pending socket队列已满,此时客户端调用connect发送syn分节给服务器端请求连接,服务端会忽略此syn包,客户端收不到syn的ack,会触发syn超时,这个超时时间比较长,重发syn,导致客户端长时间连接不上。

backlog设置为0或者过大,只要收到syn包,就会在pending socket队列中增加节点,这个容易导致物理内存耗尽

Syn flood就是攻击pending socket队列的

 

linux内核可以全局设置此pending socket队列大小

/proc/sys/net/ipv4/tcp_max_syn_backlog


在这里也解释了三次握手和accept之间的先后顺序关系! 也就是说,客户端的connect函数从开始调用到函数返回正好是三次握手的过程,第三次握手成功,内核调用accept.如下图所示:

UNIX网络编程--TCP网络编程中的listen_第4张图片

从上图也可以看出来函数调用和三次握手之间的关系!


你可能感兴趣的:(c,网络编程)