Linux下高并发socket最大连接数所受的各种限制(详解)
在高并发socket编程过程中,最大连接数是一个非常重要的指标,通常情况下,我们希望在达到最大连接数时,能够有效地处理多余的连接请求。然而,在Linux系统下,最大连接数受到了多种限制,下面将对这些限制做详细的介绍。
1. 系统级别限制
1.1 somaxconn
在 Linux 系统中,有一个参数可以设置最大连接数,即 somaxconn
(TCP默认队列大小)。somaxconn
在内核中限制了监听队列的最大长度,它的值可以通过如下的命令查看:
cat /proc/sys/net/core/somaxconn
默认情况下,somaxconn
的值为 128,你可以通过如下命令修改 somaxconn
的值:
echo 1024 > /proc/sys/net/core/somaxconn
该命令将参数值修改为 1024。注意,这样的修改在系统重启后会失效,需要将其添加到 /etc/sysctl.conf
文件中才能永久生效。
1.2 ulimit
ulimit 是一个 Linux 系统下限制进程资源可用的命令,在网络编程中,我们通常使用 ulimit 命令来设置一个进程可以打开的最大文件数(文件描述符数量),同时也可以用来设置进程能够创建的线程栈大小等资源限制。
可以通过如下命令查看某个用户的文件描述符限制:
ulimit -a
显然,如果在进程中限制了文件描述符数量,那么最大连接数也就受到了限制。可以使用如下命令修改最大文件描述符数量上限:
ulimit -n 65535
该命令将最大文件描述符数量上限修改为 65535。同样需要注意的是,这样的修改也只对当前登录的用户会话有效,在会话关闭后需要重新设置限制才能生效。你也可以将其添加到 /etc/security/limits.conf
文件中,从而实现全局设置。
2. 程序级别限制
2.1 socket()函数
在 Linux 系统中,可以通过 socket()
函数创建一个套接字,该函数有三个参数,分别是协议域、类型和协议。其中,第二个参数类型也会对最大连接数产生影响,例如:
int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
其中,第二个参数 SOCK_STREAM
表示使用 TCP 协议,而 TCP 协议的最大连接数约为64K,在必要的情况下,可以使用 SOCK_DGRAM
来使用 UDP 协议,进而规避 TCP 协议在连接数量上的限制。
2.2 epoll()
在高并发网络编程中,epoll 是一种高效的 I/O 多路复用机制,其核心思想是将所有需要等待的文件描述符都放到一个 epoll 对象中,等待事件产生时再统一进行处理。epoll 也不是万能的,它也有一定的限制,例如在单个 epoll 对象中,能够支持的最大文件描述符数量也会产生限制,可以通过如下命令查看:
cat /proc/sys/fs/epoll/max_user_instances
默认情况下,该值为 128,可以通过如下命令修改其上限:
echo 1024 > /proc/sys/fs/epoll/max_user_instances
同样,这样的修改也只对当前会话有效,可以将其添加到 /etc/sysctl.conf
文件中从而实现全局设置。
示例说明
示例1:使用 ulimit
在进程级别限制最大连接数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 128
int main()
{
struct sockaddr_in client, server;
int server_fd, client_fd;
socklen_t client_len;
char buffer[BUFFER_SIZE];
// 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 配置socket
server.sin_family = AF_INET;
server.sin_port = htons(9000);
server.sin_addr.s_addr = htonl(INADDR_ANY);
bind(server_fd, (struct sockaddr *)&server, sizeof(server));
listen(server_fd, 500);
printf("server started at port %d...\n", 9000);
// 设置接收客户端连接的最大数量
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) == -1)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// 设置进程最大文件描述符数量
if (system("ulimit -n 65535") == -1)
{
perror("ulimit failed");
exit(EXIT_FAILURE);
}
while (1)
{
// 接收连接
client_fd = accept(server_fd, (struct sockaddr *)&client, &client_len);
// 发送数据
sprintf(buffer, "Hello World!\n");
write(client_fd, buffer, sizeof(buffer));
// 关闭socket
close(client_fd);
}
return 0;
}
在该示例中,使用了 setsockopt
来配置接收客户端连接的最大数量,同时使用了 system
函数来实现在进程级别限制文件描述符数量。
示例2:使用 epoll 在程序级别限制最大连接数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define BUFFER_SIZE 128
int main()
{
struct sockaddr_in client, server;
int server_fd, client_fd, epoll_fd;
socklen_t client_len;
char buffer[BUFFER_SIZE];
struct epoll_event event, *events;
// 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 配置socket
server.sin_family = AF_INET;
server.sin_port = htons(9000);
server.sin_addr.s_addr = htonl(INADDR_ANY);
bind(server_fd, (struct sockaddr *)&server, sizeof(server));
listen(server_fd, 500);
printf("server started at port %d...\n", 9000);
// 创建epoll
epoll_fd = epoll_create(1024);
if (epoll_fd == -1)
{
perror("epoll_create failed");
exit(EXIT_FAILURE);
}
// 将监听socket加入epoll中
event.data.fd = server_fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
// 设置socket为非阻塞模式
fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL) | O_NONBLOCK);
// 创建events列表
events = calloc(1024, sizeof(event));
while (1)
{
int n, i;
// 等待事件
n = epoll_wait(epoll_fd, events, 1024, -1);
for (i = 0; i < n; i++)
{
if (events[i].data.fd == server_fd)
{
// 接收连接
client_fd = accept(server_fd, (struct sockaddr *)&client, &client_len);
// 设置客户端socket为非阻塞模式
fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL) | O_NONBLOCK);
// 将客户端socket加入epoll中
event.data.fd = client_fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
}
else
{
// 接收数据
int n = read(events[i].data.fd, buffer, sizeof(buffer));
if (n == 0)
{
// 关闭socket
close(events[i].data.fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
}
else if (n < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
// 关闭socket
close(events[i].data.fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
}
}
else
{
// 发送数据
write(events[i].data.fd, buffer, sizeof(buffer));
}
}
}
}
// 关闭epoll
free(events);
close(epoll_fd);
return 0;
}
在该示例中,使用了 epoll 来管理连接,通过设置客户端 socket 为非阻塞模式,并通过 EPOLLET
模式来实现边缘触发,提高服务器效率。同时,使用 epoll 在程序级别限制了最大连接数。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Linux下高并发socket最大连接数所受的各种限制(详解) - Python技术站