io复用select方法编写的服务器
- 摘要: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方法编写的服务器的更多相关文章
- IO复用——select系统调用
1.select函数 此函数用于在一段时间内,监听用户感兴趣的文件描述符上的可读.可写和异常等事件. #include<sys/select.h> int select(int nfds, ...
- IO复用: select 和poll 到epoll
linux 提供了select.poll和epoll三种接口来实现多路IO复用.下面总结下这三种接口. select 该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经 ...
- Linux IO多路复用 select
Linux IO多路复用 select 之前曾经写过简单的服务器,服务器是用多线程阻塞,客户端每一帧是用非阻塞实现的 后来发现select可以用来多路IO复用,就是说可以把服务器这么多线程放在一个线程 ...
- IO复用的三种方法(select,poll,epoll)深入理解
(一)IO复用是Linux中的IO模型之一,IO复用就是进程告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程处理,从而不会在单个IO上阻塞了,Linux中,提供 ...
- linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)
IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file ...
- 第十七篇:IO复用之select实现
前言 在看过前文:初探IO复用后,想必你已对IO复用这个概念有了初步但清晰的认识. 接下来,我要在一个具体的并发客户端中实现它(基于select函数),使得一旦服务器中的客户进程被终止的时候,客户端这 ...
- IO复用之select实现
前言 在看过前文:初探IO复用后,想必你已对IO复用这个概念有了初步但清晰的认识.接下来,我要在一个具体的并发客户端中实现它( 基于select函数 ),使得一旦服务器中的客户进程被终止的时候,客户端 ...
- 多路IO复用模型--select, poll, epoll
select 1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数 2.解决1024以下客户端时使用se ...
- IO复用(Reactor模式和Preactor模式)——用epoll来提高服务器并发能力
上篇线程/进程并发服务器中提到,提高服务器性能在IO层需要关注两个地方,一个是文件描述符处理,一个是线程调度. IO复用是什么?IO即Input/Output,在网络编程中,文件描述符就是一种IO操作 ...
随机推荐
- Spark机器学习基础二
无监督学习 0.K-means from __future__ import print_function from pyspark.ml.clustering import KMeans #from ...
- opencv dlib caffe 安装
编译opencv记录 1.opencv,opencv_contrib包, 3.4.5版本 2. 到opencv/build 目录下 删除所有文件 $rm -rf * 3. 打开cmake图形界面 $c ...
- Ubuntu14.04+Dell 7060安装无线/有线网络驱动
7060的有线网卡I219-LM,可以用e1000e的驱动 1.sudo mkdir -p /usr/local/src/e1000e (在/usr/local/src/中新建文件夹e1000e) s ...
- 剑指offer:反转链表
问题描述 输入一个链表,反转链表后,输出新链表的表头. c++代码 /* struct ListNode { int val; struct ListNode *next; ListNode(int ...
- scipy插值与拟合
原文链接:https://zhuanlan.zhihu.com/p/28149195 1.最小二乘拟合 实例1 import numpy as np import matplotlib.pyplot ...
- curl的POST请求,封装方法
//POST请求//参数1是请求的url//参数2是发送的数据的数组//参数3是其他POST选项public static function POST($url, array $post = arra ...
- allure--下的各装饰器的翻译及自己的总结
翻译图-快捷键 红色字体感觉用的会比较多,起码现在感觉应该是比其他的多一点 lable应该没有什么特殊的用法,只是对下面方法的一个汇总(或者可以这么说,下面的方法是lable更具体的实现) sever ...
- Java8:Lambda表达式增强版Comparator和排序
1.概述 在这篇教程里,我们将要去了解下即将到来的JDK 8(译注,现在JDK 8已经发布了)中的Lambda表达式——特别是怎样使用它来编写Comparator和对集合(Collection)进行排 ...
- hdoj3247
注意fail时怎么走. #include <iostream> #include <cstdio> #include <cmath> #include <al ...
- Envoy 源码分析--buffer
目录 Envoy 源码分析--buffer BufferFragment RawSlice Slice OwnedSlice SliceDeque UnownedSlice OwnedImpl Wat ...