以前一直以爲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),更不關心任何網絡狀況的變化。