• 摘要:io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作。select函数作为io多路复用的机制,第一个参数nfds是fd_set集合中最大描述符值+1,fdset是一个位数组,每一位代表其对应的描述符是否需要被检查。第二三四参数表示需要关注读、写、错误时间的文件描述符位数组,这些参数既是输入型参数也是输出型参数,可能会被内核修改用于标识哪些描述符上发生了关注的事件,所以每次调用select前都需要重新
  • io多路复用是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般都是读就绪或者写就绪),就能通知应用程序进行相应的读写操作。select函数作为io多路复用的机制,第一个参数nfds是fd_set集合中最大描述符值+1,fdset是一个位数组,每一位代表其对应的描述符是否需要被检查。第二三四参数表示需要关注读、写、错误时间的文件描述符位数组,这些参数既是输入型参数也是输出型参数,可能会被内核修改用于标识哪些描述符上发生了关注的事件,所以每次调用select前都需要重新初始化fdset。timeout参数为超时时间,该结构会被内核修改,其值为超时剩余时间。

    select函数返回值有三种,返回-1时表示select失败;返回0表示超时;其他返回值表示select成功。

  •  2018-12-02 18:56:42

 #include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
int rfds[];
void usage(const char* proc)
{
assert(proc);
printf("usage:%s: [ip] [port]/n",proc);
}
int start_up(const char* ip,int port)
{
assert(ip);
assert(port > );
int sock = socket(AF_INET,SOCK_STREAM,);
if(sock < )
{
perror("socket");
exit();
}
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)) < )
{
perror("bind");
exit();
} if(listen(sock,) < )
{
perror("listen");
exit();
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc != )
{
usage(argv[]);
return ;
}
int listen_sock = start_up(argv[],atoi(argv[]));
for(int i= ;i < ; i++)
{
rfds[i] = -;
}
fd_set rset;
int max_fd = ;
while()
{
struct timeval timeout = {,};
FD_ZERO(&;rset);
rfds[] = listen_sock;
max_fd = listen_sock;
for(int i= ;i < ; i++)
{
if(rfds[i] >= )
{
FD_SET(rfds[i],&;rset);
if(max_fd < rfds[i])
max_fd = rfds[i];
}
}
switch(select(max_fd + ,&;rset,NULL,NULL,NULL))
{
case -:
perror("select");
break;
case :
printf("timeout");
break;
default:
{
int j = ;
for(;j < ;j++)
{
if(rfds[j] < )
continue;
if(j == &;&;FD_ISSET(rfds[j],&;rset))
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_fd = accept(listen_sock,(struct sockaddr*)&;client,&;len);
if(new_fd < )
{
perror("accept");
}
else
{
printf("get a client:socket :%s:%d/n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
int k = ;
for(;k < ;k++)
{
if(rfds[k] == -)
{
rfds[k] = new_fd; break;
}
}
if( k == )
{
close(new_fd);
}
}
}
else if(FD_ISSET(rfds[j],&;rset))
{
char buf[];
ssize_t s = read(rfds[j],buf,sizeof(buf)-);
if(s > )
{
buf[s] = ;
printf("client#%s/n",buf);
}
else if(s == )
{
printf("client close.../n");
close(rfds[j]); rfds[j] = -;
}
else
{
perror("read");
}
}
}
}
break;
}
}
}

select_server.c

使用telnet为一个客户端访问:

当把服务器终止后,再次打开服务器会出现这种情况:

这是因为,虽然server的应用程序终止了,但tcp协议层的连接没有完全断开,因此不能再次监听同样的server端口。

client终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后 就可以再次启动server了。

io复用select方法编写的服务器的更多相关文章

  1. IO复用——select系统调用

    1.select函数 此函数用于在一段时间内,监听用户感兴趣的文件描述符上的可读.可写和异常等事件. #include<sys/select.h> int select(int nfds, ...

  2. IO复用: select 和poll 到epoll

    linux 提供了select.poll和epoll三种接口来实现多路IO复用.下面总结下这三种接口. select 该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经 ...

  3. Linux IO多路复用 select

    Linux IO多路复用 select 之前曾经写过简单的服务器,服务器是用多线程阻塞,客户端每一帧是用非阻塞实现的 后来发现select可以用来多路IO复用,就是说可以把服务器这么多线程放在一个线程 ...

  4. IO复用的三种方法(select,poll,epoll)深入理解

    (一)IO复用是Linux中的IO模型之一,IO复用就是进程告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程处理,从而不会在单个IO上阻塞了,Linux中,提供 ...

  5. linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

      IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file ...

  6. 第十七篇:IO复用之select实现

    前言 在看过前文:初探IO复用后,想必你已对IO复用这个概念有了初步但清晰的认识. 接下来,我要在一个具体的并发客户端中实现它(基于select函数),使得一旦服务器中的客户进程被终止的时候,客户端这 ...

  7. IO复用之select实现

    前言 在看过前文:初探IO复用后,想必你已对IO复用这个概念有了初步但清晰的认识.接下来,我要在一个具体的并发客户端中实现它( 基于select函数 ),使得一旦服务器中的客户进程被终止的时候,客户端 ...

  8. 多路IO复用模型--select, poll, epoll

    select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...

  9. IO复用(Reactor模式和Preactor模式)——用epoll来提高服务器并发能力

    上篇线程/进程并发服务器中提到,提高服务器性能在IO层需要关注两个地方,一个是文件描述符处理,一个是线程调度. IO复用是什么?IO即Input/Output,在网络编程中,文件描述符就是一种IO操作 ...

随机推荐

  1. ASP.NET Core API 接收参数去掉烦人的 [FromBody]

    在测试ASP.NET Core API 项目的时候,发现后台接口参数为类型对象,对于PostMan和Ajax的Post方法传Json数据都获取不到相应的值,后来在类型参数前面加了一个[FromBody ...

  2. PyQt5——高级控件

    PyQt5高级控件使用方法详见:https://blog.csdn.net/jia666666/article/list/4?t=1& PyQt5高级控件汇总: 1.QTableView 2. ...

  3. 《javascript经典入门》-day02

    <javascript经典入门>-day02 1.使用函数 1.1基本语法 function sayHello() { aler('Hello'); //...其他语句... } #关于函 ...

  4. 亚马逊促销活动Promotion②:Money Off(满减折扣)的设置教程

    满减.折扣是放之四海皆有效的促销手段,虽然亚马逊对卖家有诸多限制,但这个促销方式却是允许的,对亚马逊的卖家而言,这对提升商品销量.打造爆款都是极好的.今天小编来讲讲亚马逊的Money Off要怎么设置 ...

  5. 搭建Python自动化测试环境+元素定位

    https://blog.csdn.net/GitChat/article/details/79081187

  6. System.exit()源码分析

    最近代码中常用的System.exit(),就来看看源码. 首先位于java.lang.System中,源码如下: /** * Terminates the currently running Jav ...

  7. php国家或者编码英文字母排序

    1.直接复制传入需要排序的字符,然后可以把返回的数据当key,传送给前台function getFirstCharter($str){ if(empty($str)){return '';} $fch ...

  8. form 表单提交、后台的统一处理

    配合 form 提交后台 /ajaxSubmit/Submit等通过form提交springMvc下@RequestMapping("/save_oaflow_init")//Re ...

  9. rpm -ivh 安装与 rpm -U安装解决问题一例

    [root@localhost server3.10.12]# rpm  -ivh krb5-libs-1.15.1-18.el7.x86_64.rpm警告:krb5-libs-1.15.1-18.e ...

  10. 汇编语言实验1 & macOS下DOSBox的安装

    一.macOS下DOSBox的安装 1.在官网下载DOSBox-0.74.dmg 打开,此时默认z盘 一番折腾,终于搞定debug 然后为了不用每次都执行mount,遂更改配置文件自动挂在目录 再次打 ...