写在前面
我们知道在网络编程中,listen接口是用来建立socket监听的,其参数只有两个。我们就listen展开说明。
函数原型
int listen(int sockfd, int backlog);
函数作用
当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。根据TCP状态转换图,调用listen导致套接字从CLOSED状态转换成LISTEN状态。
案列分析
server.c
#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 信号的处理函数,触发时结束主进程中的循环*/
static void handle_term(int sig)
{
sig=0;
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];
int port = atoi(argv[2]);
int backlog = atoi(argv[3]);
int sock = socket(AF_INET, SOCK_STREAM, 0);
assert(sock >= 0);
struct sockaddr_in address;
bzero(&address, sizeof(address));
#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 信号的处理函数,触发时结束主进程中的循环*/
static void handle_term(int sig)
{
sig=0;
stop = true;
}
int main(int argc, char* argv[])
{
signal(SIGTERM, handle_term);
if(argc <= 3)
{
printf("usage: %s ip_address port_number count\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int count= atoi(argv[3]);
struct sockaddr_in server_address;
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &server_address.sin_addr);
server_address.sin_port = htons(port);
int sockfd[count];
for(int i = 0; i < count; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM,0);
assert(sockfd[i] >= 0);
if(connect(sockfd[i],(struct sockaddr*)&server_address,sizeof(server_address)) < 0)
{
printf("connection failed\n");
}
}
/*循环等待连接,直到有SIGTERM信号将它中断*/
while(!stop)
{
sleep(1);
}
for(int j = 0; j < count; j++)
{
close(sockfd[j]);
}
return 0;
}
address.sin_family = AF_INET; inet_pton(AF_INET, ip, &address.sin_addr); 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;}
client.c
#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 信号的处理函数,触发时结束主进程中的循环*/
static void handle_term(int sig)
{
sig=0;
stop = true;
}
int main(int argc, char* argv[])
{
signal(SIGTERM, handle_term);
if(argc <= 3)
{
printf("usage: %s ip_address port_number count\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int count= atoi(argv[3]);
struct sockaddr_in server_address;
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &server_address.sin_addr);
server_address.sin_port = htons(port);
int sockfd[count];
for(int i = 0; i < count; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM,0);
assert(sockfd[i] >= 0);
if(connect(sockfd[i],(struct sockaddr*)&server_address,sizeof(server_address)) < 0)
{
printf("connection failed\n");
}
}
/*循环等待连接,直到有SIGTERM信号将它中断*/
while(!stop)
{
sleep(1);
}
for(int j = 0; j < count; j++)
{
close(sockfd[j]);
}
return 0;
}
启动server
./server 12.7.0.0.1 6666 5
启动client(客户端程序,接收3个参数:IP地址、端口和count连接数。我们在Linux另外一个终端上运行(建立10个连接):)
./client myself 6666 9
启动listen监听队列内容
netstat -nt|grep 6666
结论:
其中处于LISTEN状态的为建立监听的服务端程序,在监听队里中,处于ESTABLISHED状态的连接有6个(backlog值加1),其他的连接都处于SYN_RCVD状态。我们改变服务端与客户端程序的第3个参数并重新运行之,能发现同样的规律,即完成连接最多有(backlog+1)个。在不同的系统上,运行结果会有些差别,不过监听队列中已完成连接的上限通常比backlog值略大。