监听socket
socket被命名之后,还不能马上接受客户连接,我们需要使用如下系统调用来创建一个监听队列以存放待处理的客户连接:
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd参数指定被监听的socket。backlog参数提示内核监听队列的最大长度。监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息。在内核版本2.2之前的Linux中,backlog参数是指所有处于半连接状态(SYN_ RCVD)和完全连接状态(ESTABLISHED) 的socket的上限。但自内核版本2.2之后,它只表示处于完全连接状态的socket的上限,处于半连接状态的socket的上限则由/proc/s/netipv4/tcp_ max_syn_backlog 内核参数定义。backlog参数的典型值是5。
listen成功时返回0,失败则返回-1并设置errno。
下面我们编写一个服务器程序,如代码清单5-3所示,以研究backlog参数对listen系统调用的实际影响。
//testlisten->main.cpp
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
static bool stop = false;
static void handle_term( int sig )
{
stop = true;
}
int main( int argc, char* argv[] )
{
signal( SIGTERM, handle_term );
if( argc <= 3 )
{
printf( "usage: %s ip_address port_number backlog\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1]; //监听ip地址
int port = atoi( argv[2] ); //atoi()字符串转为int
int backlog = atoi( argv[3] );
//创建socket
int sock = socket( PF_INET, SOCK_STREAM, 0 ); //IPv4 TCP
assert( sock >= 0 );
//创建一个IPv4 socket地址
struct sockaddr_in address;
bzero( &address, sizeof( address ) ); //将结构体清零
address.sin_family = AF_INET; //TCP/IPv4协议族
inet_pton( AF_INET, ip, &address.sin_addr ); //ip地址转换函数,二进制网络字节序
address.sin_port = htons( port ); //转换为网络字节
int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) ); //绑定
assert( ret != -1 );
ret = listen( sock, backlog ); //监听
assert( ret != -1 );
//循环等待连接,直到有SIGTERM信号将它中断
while ( ! stop )
{
sleep( 1 );
}
//关闭socket
close( sock );
return 0;
}
在linux机器上执行cmake ..
和make
后,执行./testlisten 172.16.160.240 12345 5
(本机ip地址)。
然后再开一个新的终端输入telnet 172.16.160.240 12345
来连接该服务器程序。同时每使用telnet命令建立一个连接,就执行一次netstat命令来查看服务器上连接的状态。
然后再另外一个机器上(注意关闭防火墙)或者还是在本机上开一个终端输入netstat -nt | grep 12345
,显示这一时刻listen监听队列的内容。
可见,在监听队列中,处于ESTABLISHED状态的连接只有2个(backlog 值加1)。我们改变服务器程序的第3个参数并重新运行之,能发现同样的规律,即完整连接最多有(backlog+1) 个。在不同的系统上,运行结果会有些差别,不过监听队列中完整连接的上限通常比backlog值略大。