多路转接服务器之select

12 篇文章 0 订阅

多路转接server之select

多路转接是5种I/O模型中的一种,I/O分为两步,第一步等I/O事件就绪,第二步对数据进行I/O,提高I/O效率实质上是要减少等的时间的比重。I/O模型分为5种:

  • 阻塞式I/O
  • 非阻塞轮循式I/O
  • 信号驱动式I/O
  • 多路转接I/O
  • 异步I/O

前四种是同步I/O,即自己等自己进行数据搬迁。后一种是异步I/O,即自己发起让底层去做,做好了通知我。 
阻塞/非阻塞,都是同步I/O,前者是死等,后者是只要I/O条件不成熟就返回。

select_server
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/select.h>


static void usage(char* proc)
{
printf("Usage :%s [local_ip] [local_port]\n",proc);
}

int startup(const char *ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0){
perror("bind");
exit(2);
}
if(listen(sock, 10) < 0){
perror("listen");
exit(4);
}
return sock;
}
int fds[1024];//定义全局数组用来存放就绪的文件描述符
int main(int argc, char *argv[])
{
if(argc != 3){//用户不会使用时打印使用手册
usage(argv[0]);
reutrn 1;
}
int listen_sock = startup(argv[1], atoi(argv[2]));
fd_set rfds;//创建一个文件描述符集
int nums = sizeof(fds)/sizeof(fds[0]);
int i = 0;
for(; i < 1024; i++){// fd都为非负数所以初始化为-1表明没有存放
fds[i] = -1;
}
fds[0] = listen_sock;// 将监听套接字放在0号下标处
while(1){
int maxfd = -1;
struct timeval timeout = {0, 0};
FD_ZERO(&rfds);// 清空文件描述符集
for(i=0; i < nums; i++){
if(fds[i] == -1){//找一个没有被占的位置
continue;
}
FD_SET(fds[i], &rfds);// 把这个fd[i]放进fd集中
if(maxfd < fds[i]){
maxfd = fds[i];// 修改最大的文件描述符
}
}
switch(select(maxfd+1, &rfds, NULL, NULL, NULL))
{
case 0:
perror("timeout");
break;
case -1:
perror("select");
close(fd[i]);
break;
default:
{
for(i = 0; i < nums; i++){
if(i == 0 && FD_ISSET(fds[i], &rfds)){//说明监听套接字已经就绪
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock\
, (struct sockaddr*)&client, &len);
printf("[%s:%d]\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));

if(new_sock < 0){
perror("accept");
continue;
}
int j = 0;
for(; j < nums; j++){//遍历数组寻找空位
if(fds[j] == -1){
break;
}
}
if(j == nums){//说明放不下了关闭接收套接字
close(new_sock);
}else{
fds[j] = new_sock;
}
}else if(i != 0 && FD_ISSET(fds[i]\
, &rfds)){// 别的事件已就绪
char buf[1024];
ssize_t s = read(fds[i], buf\
, sizeof(buf)-1);
if(s > 0){
buf[s] = 0;
printf("client say# %s\n", buf);
write(fds[i], buf, strlen(buf));
}else if(s == 0){
printf("client quit!!!\n");
close(fds[i]);
fds[i] = -1;
}else{
perror("read");
close(fds[i]);
fds[i] = -1;
}
}else{
}
}
}
break;
};
}
return 0;
}

测试结果:

可以一次被多个client连接,相比较多进程多线程select_server服务器当用户量较大时性能更好,而且它所占的资源是较少的。但是呢它还是有很多的缺点滴

  • 每次调用select时都要把fd从用户态拷贝到内核态,这在fd很多时系统开销会很大
  • 每次调用select都要在内核遍历所有传递进来的fd,在fd很多时系统开销也会很大
  • 所能处理的fd数量有限,因为它是一个集合,是集合就有大小
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值