第6章 I/O多路复用
前一章节客户端同时处理两个输入:标准输入和TCP套接字,然而问题在于客户端阻塞于fgets调用期,服务器进程被杀死后,服务器tcp虽然可以正确发送一个fin,但进程正阻塞于标准输入,它无法看到eof,直到从套接字读为止.这样的进程需要一种预先告诉内核的能力,一旦内核发现进程指定的一个或者多个I/O就绪,它就通知进程,这就叫做I/O复用
#include <sys/select.h>
#include<sys/time.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
struct timeval{
long tv_sec;
long tv_usec;
};
void FD_ZERO(fd_set*fdset);
void FD_SET(int fd,fd_set*fdset);
void FD_CLR(int fd,fd_set *fdset);
int FD_ISSET(int fd,fd_set *fdset);
fd_set rset;
FD_ZERO(&rset);
FD_SET(1,&rset);
FD_SET(4,&rset);
FD_SET(5,&rset);
#include "unp.h"
void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
stdineof = 0;
FD_ZERO(&rset);
for ( ; ; ) {
if (stdineof == 0)
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
- if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1)
return; /* normal termination */
else
err_quit("str_cli: server terminated prematurely");
}
- Write(fileno(stdout), buf, n);
}
- if (FD_ISSET(fileno(fp), &rset)) {
if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);
continue;
}
Writen(sockfd, buf, n);
}
}
}
int main(int argc,char *argv[])
{
if(argc<3)
err_quit("please input IP Address : Port");
int connfd=Socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in servaddr;
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(atoi(argv[2]));
Inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
Connect(connfd,(SA*)&servaddr,sizeof(servaddr));
str_cli(stdin,connfd);
return 0;
}
#include "unp.h"
static int create_sock(char *ip)
{
int listenfd;
listenfd=Socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in servaddr;
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(atoi(ip));
Inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr);
Bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
Listen(listenfd,20);
return listenfd;
}
static void deal_cli(int listenfd)
{
int connfd,i;
fd_set allset,rset;//allset保存的整个描述符集。但并不会直接select.每次select都会使得清0.很不方便。于是当要select的时候把它付给rset
FD_ZERO(&allset);
FD_ZERO(&rset);
char buf[1024];//从客户端读取的缓冲区
int ret=0;
int maxfd=listenfd;//select第一个参数
int allfd[MAXLINE];//连接数组
for(i=0;i<MAXLINE;++i){
allfd[i]=-1;
}
int nready; //select I/O已经就绪
int max=0;//表示已完成连接数组中最大的队列
FD_SET(listenfd,&allset);//将监听连接加入allset集合中
for(;;){
rset=allset;//更新
nready=Select(maxfd+1,&rset,NULL,NULL,NULL);
if(FD_ISSET(listenfd,&rset)){
connfd=Accept(listenfd,NULL,NULL); //从已连接套接字拿取一个出来
for(i=0;i<MAXLINE;++i){ //遍历整个已连接数组。找到alifd[i]等于-1(表示该位可用)
if(allfd[i]<0){
allfd[i]=connfd;
break;
}
}
FD_SET(connfd,&allset);
if(maxfd<connfd) //每次都需要比较maxfd跟新来的描述符值大小
maxfd=connfd;
if(max<i)//还要更新最后可连接数组最后的那个已连接索引
max=i;
if(--nready<=0) //如果该连接已经处理完成。那就
continue;
}
for(i=0;i<max+1;++i){ //遍历以此处理所有已完成连接
if(allfd[i]!=-1)//跳过
if(FD_ISSET(allfd[i],&rset)){
ret=read(allfd[i],buf,1024);
if(ret==0){
close(allfd[i]);
FD_CLR(allfd[i],&allset);
allfd[i]=-1;
}else if(ret<0)
{
perror("error");
}else
Writen(allfd[i],buf,ret);
}
if(--nready<=0) //如果处理最后一个连接。继续返回到select中去
continue;
}
}
}
- }
- int main(int argc,char *argv[])
{
if(argc<2){
err_quit("please input ip aiddress\n");
}
int listenfd=create_sock(argv[1]);//创建一个套接字
deal_cli(listenfd);//开始处理细节
return 0;
}
#include <poll.h>
- int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
/* include fig01 */
#include "unp.h"
//#include <limits.h> /* for OPEN_MAX */
int
main(int argc, char **argv)
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
long OPEN_MAX = sysconf(_SC_OPEN_MAX);
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for (i = 1; i < OPEN_MAX; i++)
client[i].fd = -1; /* -1 indicates available entry */
maxi = 0; /* max index into client[] array */
/* end fig01 */
/* include fig02 */
for ( ; ; ) {
nready = Poll(client, maxi+1, INFTIM);
if (client[0].revents & POLLRDNORM) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen));
#endif
for (i = 1; i < OPEN_MAX; i++)
if (client[i].fd < 0) {
client[i].fd = connfd; /* save descriptor */
break;
}
if (i == OPEN_MAX)
err_quit("too many clients");
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; /* max index in client[] array */
if (--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 1; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
/*4connection reset by client */
#ifdef NOTDEF
printf("client[%d] aborted connection\n", i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
err_sys("read error");
} else if (n == 0) {
/*4connection closed by client */
#ifdef NOTDEF
printf("client[%d] closed connection\n", i);
#endif
Close(sockfd);
client[i].fd = -1;
} else
Writen(sockfd, buf, n);
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
/* end fig02 */
第6章 I/O多路复用的更多相关文章
- LPC43xx SGPIO Experimentation
LPC4350 SGPIO Experimentation The NXP LPC43xx microcontrollers have an interesting, programmable ser ...
- 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll
关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...
- Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程 第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...
- 第15章 高并发服务器编程(2)_I/O多路复用
3. I/O多路复用:select函数 3.1 I/O多路复用简介 (1)通信领域的时分多路复用 (2)I/O多路复用(I/O multiplexing) ①同一线程,通过“拨开关”方式,来同时处理多 ...
- 【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生
曾经的VIP服务 在网络的初期,网民很少,服务器完全无压力,那时的技术也没有现在先进,通常用一个线程来全程跟踪处理一个请求.因为这样最简单. 其实代码实现大家都知道,就是服务器上有个ServerSoc ...
- 深入理解计算机操作系统——12章:多进程,IO多路复用
三种并行的应用程序: 1. 基于进程的并发编程: 2. 基于IO多路复用的并发: 3. 基于线程的并发编程: 12.1 基于进程的并发编程 进程的优劣: (1)进程间共享文件表,但不共享用户地址空间, ...
- 一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生
在网络的初期,网民很少,服务器完全无压力,那时的技术也没有现在先进,通常用一个线程来全程跟踪处理一个请求.因为这样最简单. 其实代码实现大家都知道,就是服务器上有个ServerSocket在某个端口监 ...
- Python(七)Socket编程、IO多路复用、SocketServer
本章内容: Socket IO多路复用(select) SocketServer 模块(ThreadingTCPServer源码剖析) Socket socket通常也称作"套接字" ...
- 【翻译】XV6-DRAFT as of September 3,2014 第0章 操作系统接口
操作系统接口 操作系统的任务是让多个程序共享计算机(资源),并且提供一系列基于计算机硬件的但更有用的服务.操作系统管理并且把底层的硬件抽象出来,举例来说,一个文字处理软件(例如word)不需要关心计算 ...
随机推荐
- 我的offer之路(一)
目录 1.职业规划. 2.刷题. 3.看书. <剑指offer> <数据结构算法与应用:C++语言描述 > <Effective C++> <C与指针> ...
- mysql 备份 常用脚本
全备: innobackupex --defaults-file=/data/mysql3316/my3316.cnf --user=root --password=mysqlpass /data/b ...
- ipvsadm启动报错解决方法
Centos7 yum -y install ipvadm 安装后,启动ipvsadm却报错. Redirecting to /bin/systemctl start ipvsadm.service ...
- ThinkPHP5 高级查询之构建分组条件
ThinkPHP5 高级查询之构建分组条件 一.在tp5中通过where方法如何构建分组条件, 例如:where user_id=$this->user_id and (status in (4 ...
- JS处理数据四舍五入,tofixed与round的区别
此区别是在做微信端有关绑定设备数据曲线平滑处理的过程中,进行验证时候无意发现. 1 .tofixed方法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字.例如将数据Num保留 ...
- Mac远程访问Ubuntu
MacOS和Ubuntu连接到同一个网络使用ping命令可以通信即可.SSH使用SSH可以很方便的在MacOS上访问Ubuntu,不过只能用命令行操作,相当于连接了Ubuntu的终端. 1. Ubun ...
- 让Python带你看一场唯美的横飘雪!
“北国风光,千里冰封,万里雪飘”,这句诗描写了一句美丽肃静的风光图,恰逢昨天笔者这边也下了一场比较大的雪,要不今天就用Python带大家也来领略一次美丽的雪景? 开发环境 版本:Python3.6 系 ...
- 【HIHOCODER 1599】逃离迷宫4
描述 小Hi被坏女巫抓进一座由无限多个格子组成的矩阵迷宫. 小Hi一开始处于迷宫(x, y)的位置,迷宫的出口在(a, b).小Hi发现迷宫被女巫施加了魔法,假设当前他处在(x, y)的位置,那么他只 ...
- 彻底卸载gedit
$ sudo apt-get purge gedit gedit-plugins$ sudo apt-get autoremove
- 《小团团团队》第九次团队作业:Beta冲刺与验收准备
项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 实验十三 团队作业9:Beta冲刺与团队项目验收 团队名称 小团团团队 作业学习目标 (1)掌握软件黑盒测试技术:(2)学 ...