Lon*_*ner 9 c sockets linux connection
我试图了解backlog参数int listen(int sockfd, int backlog);如何影响新连接的处理方式.
这是我的服务器程序.
/* server.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main()
{
int sockfd;
int ret;
int yes = 1;
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((ret = getaddrinfo(NULL, "8000", &hints, &ai)) == -1) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
return 1;
}
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd == -1) {
perror("server: socket");
return 1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
perror("server: setsockopt");
close(sockfd);
return 1;
}
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
perror("server: bind");
close(sockfd);
return 1;
}
freeaddrinfo(ai);
if (listen(sockfd, 2) == -1) {
perror("server: listen");
close(sockfd);
return 1;
}
printf("server: listening ...\n");
printf("server: sleep() to allow multiple clients to connect ...\n");
sleep(10);
printf("server: accepting ...\n");
while (1) {
int connfd;
struct sockaddr_storage client_addr;
socklen_t client_addrlen = sizeof client_addr;
char buffer[1024];
int bytes;
connfd = accept(sockfd, (struct sockaddr *) &client_addr, &client_addrlen);
if (connfd == -1) {
perror("server: accept");
continue;
}
if ((bytes = recv(connfd, buffer, sizeof buffer, 0)) == -1) {
perror("server: recv");
continue;
}
printf("server: recv: %.*s\n", (int) bytes, buffer);
close(connfd);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是我的客户端程序.
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(int argc, char **argv)
{
int sockfd;
int ret;
struct addrinfo hints, *ai;
if (argc != 2) {
fprintf(stderr, "usage: %s MSG\n", argv[0]);
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((ret = getaddrinfo(NULL, "8000", &hints, &ai)) == -1) {
fprintf(stderr, "client: getaddrinfo: %s\n", gai_strerror(ret));
return 1;
}
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd == -1) {
perror("client: socket");
return 1;
}
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
perror("client: connect");
close(sockfd);
return -1;
}
printf("client: connected\n");
if (send(sockfd, argv[1], strlen(argv[1]), 0) == -1) {
perror("client: send");
close(sockfd);
return -1;
}
printf("client: send: %s\n", argv[1]);
freeaddrinfo(ai);
close(sockfd);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用以下脚本编译和运行这些程序.
# run.sh
gcc -std=c99 -Wall -Wextra -Wpedantic -D_DEFAULT_SOURCE server.c -o server
gcc -std=c99 -Wall -Wextra -Wpedantic -D_DEFAULT_SOURCE client.c -o client
./server &
sleep 1
./client hello1 &
sleep 1
./client hello2 &
sleep 1
./client hello3 &
sleep 1
./client hello4 &
sleep 1
./client hello5 &
sleep 5
pkill server
Run Code Online (Sandbox Code Playgroud)
当我运行上面的脚本时,我得到了这个输出.
$ sh run.sh
server: listening ...
server: sleep() to allow multiple clients to connect ...
client: connected
client: send: hello1
client: connected
client: send: hello2
client: connected
client: send: hello3
client: connected
client: send: hello4
client: connected
client: send: hello5
server: accepting ...
server: recv: hello1
server: recv: hello2
server: recv: hello3
Run Code Online (Sandbox Code Playgroud)
输出显示,当服务器在listen()和之间休眠时accept(),所有五个客户端都可以成功connect()并且send()到服务器.但是,服务器只能accept()和recv()三个客户端.
我不明白以下几点.
listen()使用backlog参数as 调用2.为什么所有五个客户都成功了connect()?我期待只有2 connect()秒才能成功.accept()与recv()来自3个客户端,而不是2?当客户端连接到侦听端口时,根据套接字堆栈的实现,它可能是:
在待办事项中保留挂起的连接,并仅在accept()调用从待办事项中删除该客户端时完成3路TCP握手.这是您期望的行为,也是旧系统的行为方式.
在后台立即完成握手,然后将完全连接的连接存储在待办事项中,直到accept()将其删除.这是您的示例似乎展示的行为,并且在现代系统中并不罕见.
根据Linux手册页listen():
使用Linux 2.2更改了TCP套接字上的backlog参数的行为. 现在它指定了等待接受的完全建立的套接字的队列长度,而不是未完成的连接请求的数量.可以使用设置不完整套接字的队列的最大长度
/proc/sys/net/ipv4/tcp_max_syn_backlog.启用syncookies时,没有逻辑最大长度,并忽略此设置.有关更多信息,请参阅tcp(7).
因此,在您的情况下,所有5个连接可能在您开始调用之前在后台完成accept(),从而允许客户端调用send()(并且可以在他们检测到某些连接被丢弃之前这样做),但不是全部由于其尺寸小,连接能够保留在积压中.
当您的代码在每个客户端之间休眠一秒钟时,客户端就有时间在下一个客户端到来之前完成并关闭其连接。
因此服务器端的队列(这是 backlog 参数控制的)始终为空。
在没有“睡眠”语句的情况下重试。
| 归档时间: |
|
| 查看次数: |
797 次 |
| 最近记录: |