在基本的unix网络编程中,server-client模式是普遍应用的模式之一.在server端编程过程中,基本的模式是
socket()//创建fd|| bind()//将socket绑定到特定端口中|| listen()//监听相应的socket,用于创建服务端和用户端的连接|| accept()//接受客户端连接
本篇将主要详细介绍其中listen()
,内容包括:
- listen接口定义
- client连接到server之间做了什么
- backlog参数
定义
#include <sys/socket.h> int listen(int socket, int backlog);
listen接口非常简单,参数为socket的fd以及backlog. backlog将在后面详细解释.
listen的返回值和大多数socket api类似:
- 0 //listen成功
- -1 //错误,并且errno被设置.可以通过
strerror(errno)
查看相应的错误.
连接过程(Three-way hand shake)
当server端的socket fd调用listen()
后,server就开始监听相应端口的连接请求.当client连接时,server通过调用accept()
获取到相应的连接,生成一个新的socket, 用于和client交换数据.
那从client连接,发起TCP连接到最后accept
之间,过程是如何的?
我们知道,TCP采用的是three-way hand shake方式来建立连接.
- 当client调用
connect
时,client建立一个从client到server的tcp连接,发送一个包含SYN的tcp包 - 当server端接收到包含SYN的TCP包时,其将连接放入到
syn_received
队列中.并且返回一个包含ack及syn的tcp包 - 到client端接受到包含ack J+1的包时,表明连接成功,
connect
调用返回 connect
返回后,client端接着发送ACK K+1的TCP包,至此,three-way hand-shake完成
backlog
注意到listen
参数中,有socket
以及backlog
,那这个backlog
到底是什么,平常合理的取值是多少?
要明白backlog
,得先说明three-way hand shake.
从上面的连接过程中,我们可以看到,从client调用connect
到最后三次握手完成,中间有两个队列:
- syn_received
- established
syn_received为那些已经收到client发过来的tcp连接请求,但没有接到client回复的队列.即其three-way hand shake还未完成.
established为那些已经完成three-way hand shake,但还没被accept()
获取的连接.
backlog的意义
backlog
所指的值有两种情况.
- 在linux 2.2以及freebsd 4.5,syncache被引入之前, backlog所指的长度为两个队列之和
- 在linux2.2以后,linux引入了syncookie,类似的,freebsd引入了syncache.这时,three-way hand shake的第一步就交给了syncookie以及syncache,其值就与backlog无关了.而这么做的原因,就是为了消除当时出现的syn flood攻击造成的service无法提供服务的情况.
具体可以参见后面所给的linux man以及freebsd的man.
backlog选值
在给出backlog选值之前,需要先说明,在backlog中的队列为full时的处理.
通过man 2 listen
,可以得到
The backlog parameter defines the maximum length for the queue of pending connections. If a connection request arrives with the queue full, the client may receive an error with an indication of ECONNREFUSED. Alterna tively, if the underlying protocol supports retransmission, the request may be ignored so that retries may succeed.
因此,sevice端设置backlog成为了一个值得考虑的问题.
backlog=5
很多unix编程例子,以及手册中,都提及backlog=5.通常来说,在目前的网络环境中是远远不够的,尤其是对于http服务来说.
backlog=0
这并不是一个合适的方法,因为对于不同的平台来说,其值并不统一.linux平台下,backlog<=0会被设置为最小的backlog值
设置为一个固定值
通常,可以在代码里头固定hardcode一个较大的值,而在不同平台中,对应的实际backlog并不相同.
- Mac OSX以及freebsd会相应的对backlog值*1.5(from unix network programming & man)
- linux下,则是backlog+3(from unix network programming)
不过,hardcode通常不是个好选择,因此,可以选择通过环境变量的方式:
if ((ptr = getenv("LISTENQ")) != NULL) backlog = atoi(ptr);
设置一个超大的值
在当前linux以及unix实现中,都将会对backlog做一定处理.对于超大的backlog值,其将自动缩小到系统所能支持的最大值,也就是SOMAXCONN.在一般的实现中,其值为128.
总结
listen
是网络编程中非常常用的api,本文详细介绍了其处理过程以及相应参数的意义.
后记
在写下文章前,我没有仔细了解backlog的意义,因此,在编写一个测试程序时,出现了connect
调用返回,但是发送数据时却发生了SIGPIPE信号,导致client退出的问题.一直以来,我都以为backlog值的就是队列之和,因此当tcpdump获取tcp包过程时,对产生的结果十分不可理解.具体的描述可以参考stackoverflow.我问了问题,但是并没有一个合适的答案,在自己查找文档后,终于了解到真相了
参考:
- linux man
- free bsd man
- unix network programming
转载请注明:爱开源 » listen()详解