IO multiplexing 与 非阻塞网络编程
使用I/O multipexing 的网络编程中,一般需要采用非阻塞网络编程的风格,防止服务端在处理高连接量大时候阻塞在某个文件描述符上面,比如某个socket 有大量的数据需要写,但是内核发送缓冲区已经填满,无法在一次write中将需要发送到数据发送出去,程序就会阻塞在该处,导致select/poll/epoll_wait() 此时不能处理其它到来的请求,同样read或者accept也可能出现阻塞的情况,比如当客户端发起connect,之后立刻关闭该链接,在服务端尚未调用accept之前就关闭了该连接,当后来服务端accept得以调用此时完成队列中又没有完成的三次握手的连接,accept就会导致进程睡眠(详细情况可以参见UNPv1非阻塞accept的描述)。因此I/O multiplexing 一般采用非阻塞网络编程的风格。
对于read/wirte 操作来说,如果采用了非阻塞编程则需要为每个connection配备应用层缓冲区,read端主要防止一次来到数据太多,write主要防止出现阻塞,可以把没有发送完成的数据写入缓冲区,等到socket 可写之后继续发送。如果在新一次write请求到来的时候,应用层写缓冲区中还有之前未发送完的数据,则应该先将上次未写入内核的数据写入内核缓冲区,保证发送到顺序性。此处给一个简单的例子。
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <map>
#include <fcntl.h>
#include <errno.h>
#include <string>
#include <iostream>
#include <sys/select.h> #define SEVER_PORT 1314
#define MAX_LINE_LEN 1024 using namespace std; int Accept(int fd, struct sockaddr_in *addr)
{
socklen_t addr_len = static_cast<socklen_t>( sizeof *addr);
int connfd,flags; connfd = accept(fd,reinterpret_cast<struct sockaddr *>(addr),&addr_len); flags = fcntl(connfd,F_GETFL,);
fcntl(connfd,F_SETFL,flags | O_NONBLOCK); if(connfd < )
{
int ErrorCode = errno;
switch(ErrorCode)
{
case :
case EWOULDBLOCK:
case ECONNABORTED:
case EPROTO:
case EINTR:
case EMFILE:
errno = ErrorCode;
printf("Accept Error: %s\n",strerror(ErrorCode));
break;
default:
break;
}
}
return connfd;
} int Read(int fd, map<int, string> &bufMap)
{
struct iovec iov[];
char buf[MAX_LINE_LEN+];
char exbuf[]; // 如果一次read很多数据,则动用该缓冲区
int nrcv; iov[].iov_base = buf;
iov[].iov_len = MAX_LINE_LEN; iov[].iov_base = exbuf;
iov[].iov_len = sizeof exbuf; nrcv = readv(fd, iov, );// 使用readv保证能将数据读取完 if(nrcv > MAX_LINE_LEN)
{
bufMap[fd] += string(buf) + string(exbuf); // test !
printf("extrabuf in use! \n");
}
else if( nrcv > )
{
bufMap[fd] += string(buf);
}
else
{
return nrcv;
} return nrcv;
} int getSocketError(int fd)
{
int optval; socklen_t optlen = static_cast<socklen_t>(sizeof optval); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < )
{
return errno;
}
else
{
return optval;
}
} int main()
{
struct sockaddr_in cli_addr, server_addr;
vector<int> client(FD_SETSIZE,-);
map<int ,string> bufMap;// 简易应用层缓冲区 fd_set rset,wrset,allset;
int listenfd, connfd, sockfd, maxfd, nready, ix,maxid, nrcv,flags,nwrt,one;
char addr_str[INET_ADDRSTRLEN]; int accepted = ; server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SEVER_PORT); listenfd = socket(AF_INET,SOCK_STREAM,); flags = fcntl(listenfd,F_GETFL,);
fcntl(listenfd,F_SETFL,flags | O_NONBLOCK); one = ;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,&one, sizeof(one)); if(bind(listenfd,(struct sockaddr *)&server_addr,sizeof server_addr) < )
{
printf("socket bind error: %s\n",strerror(errno));
return ;
} listen(listenfd,); FD_ZERO(&rset);
FD_ZERO(&wrset);
FD_ZERO(&allset);
FD_SET(listenfd,&allset);
maxfd = listenfd;
maxid = -; while()
{
rset = allset;
nready = select(maxfd + , &rset,&wrset,NULL,NULL); if(nready < )
{
printf("select error: %s\n",strerror(errno));
exit();
} if(FD_ISSET(listenfd, &rset))
{ connfd = Accept(listenfd,&cli_addr); printf("recieve from : %s at port %d\n", inet_ntop(AF_INET,&cli_addr.sin_addr,addr_str,INET_ADDRSTRLEN),cli_addr.sin_port); for(ix = ; ix < static_cast<int>(client.size()); ix++)
{
if(client[ix] < )
{
client[ix] = connfd;
break;
}
} printf("client[%d] = %d\n",ix,connfd); if( FD_SETSIZE == ix)
{
printf("too many client! \n");
exit();
} if( connfd > maxfd)
{
maxfd = connfd;
} FD_SET(connfd, &allset); accepted++;
printf("accepted: %d\n",accepted); if(ix > maxid)
{
maxid = ix;
} if(--nready == )
{
continue;
} } for(ix = ; ix <= maxid; ix++)
{
if((sockfd = client[ix]) < )
{
continue;
} if(FD_ISSET(sockfd,&rset))
{ int left_len = bufMap[sockfd].length(); if( == (nrcv = Read(sockfd,bufMap)))
{
client[ix] = -;
printf("close! \n");
FD_CLR(sockfd,&allset);
bufMap.erase(sockfd);
close(sockfd);
}
else if ( nrcv > )
{
printf("nrcv = %d \n",nrcv); nrcv += left_len;//next time when client write to //nwrt = write(sockfd,bufMap[sockfd].c_str(),200);// 模拟还有剩余
nwrt = write(sockfd,bufMap[sockfd].c_str(),nrcv); if(nwrt < )
{
if( errno != EWOULDBLOCK)
{
printf("Write error: %s\n", strerror(errno));
}
} printf("nwrt = %d \n",nwrt); if(nwrt == nrcv) // 全部写到了内核缓冲区
{
bufMap[sockfd].clear();
//bufMap[sockfd].erase(0,nrcv);
if(FD_ISSET(sockfd,&wrset))
{
FD_CLR(sockfd,&wrset);
}
}
else // 还有剩余
{
printf("write left \n");
bufMap[sockfd].erase(,nwrt);
std::cout << " after erase: "<<bufMap[sockfd] <<std::endl;
FD_SET(sockfd,&wrset);//开始关注写事件
} }
else
{
int err = getSocketError(sockfd); printf("SocketError: %s\n",strerror(err));
}
} if(FD_ISSET(sockfd,&wrset))
{
nrcv = bufMap[sockfd].size();
printf("write again: nrcv left = %d \n",nrcv);
nwrt = write(sockfd,bufMap[sockfd].c_str(),nrcv); if(nwrt == nrcv)
{
bufMap[sockfd].clear();
if(FD_ISSET(sockfd,&wrset))
{
FD_CLR(sockfd,&wrset);
}
printf("Write complete! \n");
}
else
{
bufMap[sockfd].erase(,nwrt);
}
} if(--nready == )
{
break;
}
}
} return ;
}
IO multiplexing 与 非阻塞网络编程的更多相关文章
- NIO非阻塞网络编程原理
NIO非阻塞网络编程原理 1.NIO基本介绍 Java NIO 全称 java non-blocking IO,是指 JDK 提供的新 API.从 JDK1.4 开始,Java 提供了一系列改进的 输 ...
- Java网络编程 -- NIO非阻塞网络编程
从Java1.4开始,为了替代Java IO和网络相关的API,提高程序的运行速度,Java提供了新的IO操作非阻塞的API即Java NIO.NIO中有三大核心组件:Buffer(缓冲区),Chan ...
- {python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块
python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...
- Python网络编程-IO阻塞与非阻塞及多路复用
前言 问题:普通套接字实现的服务端的缺陷 一次只能服务一个客户端! accept阻塞! 在没有新的套接字来之前,不能处理已经建立连接的套接字的请求 re ...
- Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合
上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...
- Java网络编程和NIO详解3:IO模型与Java网络编程模型
Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...
- python开发IO模型:阻塞&非阻塞&异步IO&多路复用&selectors
一 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非 ...
- 五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O
五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O 五种I/O 模式:[1] 阻塞 I/O ...
- IO -阻塞,非阻塞, 同步,异步
转载自: http://blog.csdn.net/historyasamirror/article/details/5778378 同步(synchronous) IO和异步(asynchronou ...
随机推荐
- AutoMapper在MVC中的运用04-string映射各种类型、一个属性映射多个属性等
本篇AutoMapper使用场景: ※ 类型转换,源string类型分别转换成int, DateTime,Type ※ 源和目标都包含复杂类型属性 ※ 把源中的一个属性映射到目标中的多个属性 类型转换 ...
- CListCtrlEx:一个支持文件拖放和实时监视的列表控件——用未公开API函数实现Shell实时监视
一.需求无论何时,当你在Explorer窗口中创建.删除或重命名一个文件夹/文件,或者插入拔除移动存储器时,Windows总是能非常快速地更新它所有的视图.有时候我们的程序中也需要这样的功能,以便当用 ...
- Spring Boot开发之明月千城(一)
原文地址:http://qindongliang.iteye.com/blog/2205633 最近数据分析的项目也即将告一段落了,中间也积累了很多知识,特此记录一下.其中用的最爽的Web组合开发就是 ...
- linux 下查看硬盘分区类型
可以用 df 这个命令 具体 要 man df 仔细看看 实例 [root@localhost mnt]# df -Th文件系统 类型 容量 已用 可用 已用% 挂载点/dev ...
- ArrayList的总结
1.ArrayList的特点 主要特点:按照插入顺序来保存元素,可以利用下标来查找值 2.ArrayList的优点: 按照下标访问元素最快 3.ArrayList的缺点: 在中间插入元素很慢 删除元素 ...
- 盾牌第一至七季/全集The Shield迅雷下载
英文译名The Shield,第1-7季(2002-2008)FX.本季看点:<盾牌>一部极具争议性的连续剧,打破了传统警匪片套路,刻画了性格复杂的警察,他们在与各种罪案做斗争的同时,也面 ...
- Eclipse中输入变量会自动补全上屏的解决方法
我自己在启动Eclipse代码补全后输入感觉确实爽多了,但是每次输入变量后一按下空格,编译器会自己帮你写一个很蛋疼的名字,比如你输入了:String mStr后按下空格,它就变成了mString,十分 ...
- 模型标准化——预测模型标记语言(PMML)
https://www.cnblogs.com/pinard/p/9220199.html 在机器学习用于产品的时候,我们经常会遇到跨平台的问题.比如我们用Python基于一系列的机器学习库训练了一个 ...
- jquery实现相同事件名称,不同命名空间的调用方法
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> ...
- 在此处打开OpenPowershellHere右键 在此处打开命令窗口右键
windows 10 右键添加在本文件夹打开cmd 或prowershell 在此处打开OpenPowershellHere右键 Windows Registry Editor Version 5 ...