-
<protocol>, <src addr>, <src port>, <dest addr>, <dest port>
可以唯一地确一个连接,基于hashmap映射查找 -
可见最大连接数是受linux最大可打开文件文件数限制的而不是端口数,因为最大可打开文件数可以通过修改系统配置文件更改,所以最终最大连接数的限制来自于系统的资源限制。
在此之前的解决方案:
(1)指派一条线程专门进行accept,获取socket后分派给worker线程。这种方法使得进行accept的线程成为了单点,容易成为性能的瓶颈。
(2)多个线程同时进行accept。这种方法的问题是每一个线程accept成功的概率不均匀,导致负载不均衡。
setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse , sizeof(int));
SO_REUSEPORT支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:
- 允许多个套接字 bind()/listen() 同一个TCP/UDP端口
- 每一个线程拥有自己的服务器套接字
- 在服务器套接字上没有了锁的竞争
- 内核层面实现负载均衡
- 安全层面,监听同一个端口的套接字只能位于同一个用户下面
- 各个进程中accept socket fd不一样,有新连接建立时,内核只会唤醒一个进程来accept,并且保证唤醒的均衡性。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
void work () {
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror("listen socket");
_exit(-1);
}
int ret = 0;
int reuse = 1;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&reuse , sizeof(int));
if (ret < 0) {
perror("setsockopt");
_exit(-1);
}
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT,(const void *)&reuse , sizeof(int));
if (ret < 0) {
perror("setsockopt");
_exit(-1);
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
//addr.sin_addr.s_addr = inet_addr("10.95.118.221");
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_port = htons(9980);
ret = bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
perror("bind addr");
_exit(-1);
}
printf("bind success\n");
ret = listen(listenfd,10);
if (ret < 0) {
perror("listen");
_exit(-1);
}
printf("listen success\n");
struct sockaddr clientaddr;
int len = 0;
while(1) {
printf("process:%d accept...\n", getpid());
int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
if (clientfd < 0) {
printf("accept:%d %s", getpid(),strerror(errno));
_exit(-1);
}
close(clientfd);
printf("process:%d close socket\n", getpid());
}
}
int main(){
printf("uid:%d euid:%d\n", getuid(),geteuid());
int i = 0;
for (i = 0; i< 6; i++) {
pid_t pid = fork();
if (pid == 0) {
work();
}
if(pid < 0) {
perror("fork");
continue;
}
}
int status,id;
while((id=waitpid(-1, &status, 0)) > 0) {
printf("%d exit\n", id);
}
if(errno == ECHILD) {
printf("all child exit\n");
}
return 0;
}
网友评论