相比之前这个最简单的版本 http://.com/q/1010000003986172
我在本地压测结果
[root@localhost ~]# ab -n30000 -c3000 http://127.0.0.1:8031/
Server Software: mengkang
Server Hostname: 127.0.0.1
Server Port: 8031
Document Path: /
Document Length: 12 bytes
Concurrency Level: 3000
Time taken for tests: 1.635 seconds
Complete requests: 30000
Failed requests: 0
Write errors: 0
Total transferred: 2792697 bytes
HTML transferred: 360348 bytes
Requests per second: 18349.03 [#/sec] (mean)
Time per request: 163.496 [ms] (mean)
然后使用select
的方式,为什么完全不具备并发能力了呢。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <string.h>
#define SERV_PORT 8031
#define MAXDATASIZE 1024
#define FD_SET_SIZE 128
int main(void)
{
int fd_ready_num; // select返回的准备好的描述符个数
int listenfd, connectfd, maxfd, scokfd;
struct sockaddr_in serv_addr;
fd_set read_set, allset;
int client[FD_SETSIZE];
int i;
int maxi = -1;
int sin_size; //地址信息结构体大小
char recvbuf[MAXDATASIZE];
int len;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("套接字描述符创建失败");
exit(1);
}
printf("listenfd :%d\n", listenfd);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
if(bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
perror("绑定失败");
exit(1);
}
if(listen(listenfd, FD_SET_SIZE) == -1)
{
perror("监听失败");
exit(1);
}
maxfd = listenfd + 1;
for (i = 0; i < FD_SET_SIZE; ++i)
{
client[i] = -1;
}
FD_ZERO(&allset);
FD_SET(listenfd,&allset);
while(1){
struct sockaddr_in addr;
read_set = allset;
fd_ready_num = select(maxfd, &read_set, NULL, NULL, NULL);
// printf("有 %d 个文件描述符准备好了\n", fd_ready_num);
if (FD_ISSET(listenfd,&read_set))
{
sin_size = sizeof(addr);
if ((connectfd = accept(listenfd, (struct sockaddr *)&addr, &sin_size)) == -1)
{
perror("接收错误\n");
continue;
}
for (i = 0; i < FD_SETSIZE; ++i)
{
if (client[i] < 0)
{
client[i] = connectfd;
printf("接收client[%d]一个请求来自于: %s:%d\n", i, inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
break;
}
}
if (i == FD_SETSIZE)
{
printf("连接数过多\n");
}
FD_SET(connectfd,&allset);
maxfd = (connectfd > maxfd) ? (connectfd + 1) : maxfd;
maxi = (i > maxi) ? i : maxi;
if (--fd_ready_num <= 0)
{
continue;
}
}
for (i = 0; i < maxi; ++i)
{
if ((scokfd = client[i]) < 0)
{
continue;
}
if (FD_ISSET(scokfd,&read_set))
{
if((len = recv(scokfd,recvbuf,MAXDATASIZE,0)) == 0)
{
close(scokfd);
printf("clinet[%d] 连接关闭\n", i);
FD_CLR(scokfd, &read_set);
client[i] = -1;
}
else
{
char web_result[] = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\nContent-Length: 11\r\nServer: mengkang\r\n\r\nhello world";
write(scokfd,web_result,sizeof(web_result));
}
if (--fd_ready_num <= 0)
{
break;
}
}
}
}
close(listenfd);
return 0;
}
访问能正常访问,但是不知道为什么用ab压测结果就很差了
[zhoumengkang@localhost ~]$ ab -n100 -c10 http://localhost:8031/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)...apr_poll: The timeout specified has expired (70007)
select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
epoll优化了第一点、第二点、第三点。