I/O多路复用之select
来源:互联网 发布:老外中国快递知乎 编辑:程序博客网 时间:2024/05/22 04:26
I/O多路复用之select
在I/O编程中,在调用读写函数如write/read、send/recv、sendto/recvfrom(主要用于UDP编程)时,系统默认为阻塞模式,即当没有东西读或没有东西写时,程序发送阻塞,直到满足读或写时,进程才会被唤醒继续运行。这样,在C/S架构中就无法实现服务器的并发运行。
解决这样的问题有以下思路:
1、采用多进程:在主进程中循环监听网络连接,当有网络连接请求时,主进程创建子进程完成网络连接请求,实现服务器端的并发。
2、采用多线程:在程序中循环监听网络连接,当有网络连接请求时,创建一个线程完成网络连接请求,实现服务器端的并发。
3、I/O多路复用:I/O多路复用采用系统内核缓冲I/O数据,当某个I/O准备好后,系统通知应用程序该I/O可读或可写,这样应用程序可以马上完成相应的I/O操作,而不需要等待系统完成相应I/O操作,从而应用程序不必因等待I/O操作而阻塞,由此实现服务器端的并发。
I/O多路复用:等待多个文件描述符的多个事件发生,如果有发生,即进行I/O操作,其大致步骤如下。
1、管理等待对象及事件
2、把管理的对象及事件传递给内核
3、等其中的对象的事件发生(select、poll、epoll)
4、轮询是谁是什么事件发生
5、完成事件(read/write、recv/send、...)
A、管理对象
fd_set对象的集合名; //创建对象集合
/*从指定集合中添加删除指定文件描述符*/
voidFD_CLR(int fd, fd_set *set);
/*向指定集合中添加一个文件描述符*/
voidFD_SET(int fd, fd_set *set);
/*清空集合*/
voidFD_ZERO(fd_set *set);
B、通知内核及等待事件的发生
intselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, structtimeval *timeout);
/*
* 功能:通知内核要等的对象及事件,并等待发生
* 参数:
int nfds - 等待的最大文件描述符+1
fd_set *readfds - 读集合(集合放在不同的参数位置表示不同的事件)
fd_set *writefds- 写集合
fd_set *exceptfds - 异常集合
struct timeval *timeout:超时集合
时间值0 :非阻塞
合理的时间值 : 时间到时不满足即超时
-1 : 阻塞(-1的补码即是最大的整数,等待时间最长即为阻塞)
返回值:
-1:失败
0 :超时
>0: 有事件发生
C、轮询是谁是什么发生
for(inti = 最小的文件描述符; i < nfds; i++){
//测试是谁是什么事情
/*测试指定的文件描述符的指定事件是否*/
//int FD_ISSET(int fd, fd_set *set);
if(FD_ISSET(int fd, fd_set *set)){
D、做事件
read/write();
}
}
利用select()构建一个模拟并发服务器(实现多连接):
#include <stdio.h>#include <string.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <sys/select.h>#define MAX 1024int init_server(const char *ipstr, unsigned short port, int backlog){int s = socket(AF_INET, SOCK_STREAM, 0);if(0 > s){perror("socket");return -1;}struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(port),.sin_addr = {.s_addr = (NULL == ipstr ? INADDR_ANY : inet_addr(ipstr)),},};memset(addr.sin_zero, 0, sizeof(addr.sin_zero));socklen_t len = sizeof(addr);if(0 > bind(s, (struct sockaddr *)&addr, len)){perror("bind");return -1;}if(0 > listen(s, backlog)){perror("listen");return -1;}return s;}int main(int argc,char *argv[]){unsigned short port = 9999;char *ipstr = NULL;if(3 == num){ipstr = arg[1];port = atoi(arg[2]);}if(2 == num){port = atoi(arg[1]);}int s = init_server(ipstr, 9999, 1);if(0 > s){return -1;}fd_set oldset, newset;FD_ZERO(&newset); //清空对象集合FD_SET(s, &newset); //添加一个文件描述符到对象集合int maxfd = s+1; //最大文件描述符while(1){ /* select函数每执行一次后,对象集合会被清空,使用对象集合oldset进行监听,newset更新对象集合 */oldset = newset; int ret = select(maxfd, &oldset, NULL, NULL, NULL);//通知内核等待if(0 > ret){ //失败perror("select");break;}else if(0 == ret){ //超时continue;}if(FD_ISSET(s, &oldset)){ ///测试指定的文件描述符的指定事件是否发生,这里就是检测是否有网络连接请求。struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));socklen_t len = sizeof(addr);int rws = accept(s, (struct sockaddr*)&addr, &len);if(0 > rws){perror("accept");return -1;}printf("a nen comming [%s:%u] \n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));FD_SET(rws, &newset); //有连接时把新的文件描述符加到对象集合中if(maxfd == rws){ //更新文件描述符的最大值maxfd = rws+1;}}for(int i = s+1; i < maxfd; i++){if(FD_ISSET(i, &oldset)){char buf[MAX];memset(buf, 0, MAX);int len = read(i, buf, MAX-1);if(0 >= len){printf("read [%d] fail .\n", i);close(i);FD_CLR(i, &newset);if(maxfd == (i+1)){maxfd--;}}else{printf("sock[%d], RECV[%dbytes]:%s\n", i, len, buf);}}}}close(s);}
- I/O多路复用之select
- I/O 多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O 多路复用之select
- I/O多路复用select
- 多路复用I/O--select
- I/O多路复用之select总结
- I/O多路复用技术之 - select
- I/O 多路复用之 Select & Epoll
- I/O多路复用之select/poll/epoll
- I/O多路复用之select模型
- HDU5380 Travel with candy
- RPC与REST的区别
- 九度 Online Judge 算法 刷题 题目1087:约数的个数
- P2P, P2C, O2O, B2C, B2B, C2C
- 动态规划
- I/O多路复用之select
- Java实现文件重命名 以及file类的其他函数讲解
- 杭州电子科技大学acm-2001
- poj(2528)——Mayor's posters(线段树+离散化)
- hdu 1233 还是畅通工程 (克鲁斯卡尔裸题)
- 线性表顺序存储结构
- 九度 Online Judge 算法 刷题 题目1089:数字反转
- I/O多路复用之poll
- Eclipse插件安装