以前一直以为TCP建立连接3次握手过程服务端是对应accept函数调用,看了《Linux高性能服务器编程》才发现以前理解有误。服务端建立了监听端口就可以接受客户端连接。服务端调用listen函数来创建一个监听队列以存放待处理的客户连接。其中第二个参数backlog表示内核监听队列最大长度,也就是完全连接状态(established)的socket上限,实际上是backlog+1。
#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;
// SIGTERM handler func
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 port [backlog]\n", argv[0]);
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int backlog = 5;
if( argc > 3)
{
backlog = atoi(argv[3]);
}
int fd_listen = socket( PF_INET, SOCK_STREAM, 0 );
assert( fd_listen >= 0 );
struct sockaddr_in svr_addr;
bzero( &svr_addr, sizeof(svr_addr) );
svr_addr.sin_family = AF_INET;
inet_pton( AF_INET, ip, &svr_addr.sin_addr );
svr_addr.sin_port = htons( port );
int ret = bind( fd_listen, (struct sockaddr*)&svr_addr, sizeof(svr_addr) );
assert( ret != -1 );
ret = listen( fd_listen, backlog );
assert( ret != -1 );
printf(" server listening [%s:%d]\n", ip, port);
while( !stop )
{
sleep( 1 );
}
close( fd_listen );
return 0;
}
运行服务端:默认backlog为5
开启多个nc连接服务端:
nc大于6个后,后面的连接状态就是SYN_RECV,说明最多建立backlog+1个完整连接。
accept函数只是从监听队列取出连接,不论连接处于何种状态(ESTABLISHED,CLOSE_WAIT),更不关心任何网络状况的变化。