基于多路复用的服务端
1、目前服务端的瓶颈分析
2、解决方案:阻塞变轮询
- 通过select()函数首先监听服务端server_fd,目标事件为“连接”(读)
- 当事件发生(客户端连接)则调用sccpet()接收连接
- 将client_fd加入监听范围,目标事件为“数据接收”
- 循环查看各个被监听的文件描述符是否有时间发生
3、实现方式
4、实现逻辑
5、实现关键
- 动态调整需要监视的文件描述符
- 当接收到客户端连接时,将客户端文件描述符加入监听变量(fd_set)中
- 当发现客户端断开时,在监听变量(fd_set)中剔除客户端文件描述符
if(client > -1)
{
FD_SET(client, &reads);
max = (client > max) ? client : max;
printf("client accept\n");
}
if(r == -1)
{
FD_CLR(i, &reads);
close(i);
}
- 保证每个需要监视的文件描述符能够被轮询
max = (client > max)?client:max;
6、代码实现
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int server_handle(int server)
{
struct sockaddr_in addr = {0};
socklen_t asize = sizeof(addr);
return accept(server, (struct sockaddr*)&addr, &asize);
}
int client_handle(int client)
{
char buf[32] = {0};
int ret = read(client, buf, sizeof(buf)-1);
if(ret > 0)
{
buf[ret] = 0;
printf("Receive buf:%s\n",buf);
if(strcmp(buf, "quit") != 0)
{
ret = write(client, buf, ret);
}
else
{
ret = -1;
}
}
return ret;
}
int main(void)
{
int server = 0;
struct sockaddr_in saddr = {0};
int max = 0;
int num = 0;
fd_set reads = {0};
fd_set tmps = {0};
struct timeval timeout = {0};
server = socket(PF_INET, SOCK_STREAM, 0);
if( server == -1)
{
printf("server socket err\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(20108);
if( bind(server,(struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
{
printf("server bind err\n");
return -1;
}
if( listen(server,1) == -1 )
{
printf("server listen err\n");
return -1;
}
printf("server start success\n");
FD_ZERO(&reads);
FD_SET(server, &reads);
max = server;
while( 1 )
{
tmps = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10*1000;
num = select(max+1, &tmps, 0, 0, &timeout);
if(num > 0)
{
int i = 0;
for(i = 1; i <= max; i++)
{
if(FD_ISSET(i, &tmps))
{
if(i == server)
{
int client = server_handle(server);
if(client > -1)
{
FD_SET(client, &reads);
max = (client > max) ? client : max;
printf("client accept\n");
}
}
else
{
int r = client_handle(i);
if(r == -1)
{
FD_CLR(i, &reads);
close(i);
}
}
}
}
}
}
close(server);
return 0;
}
7、思考
改进后的服务端是否还有优化的空间?
select()是Linux系统特有的吗?