系统运输
上一个地址(使用套接字,当一台服务器完成了与多台客户端对应的小实验时,必须为TCP连接创建新的进程以与新的客户端进行通信。 这意味着1000个客户端有1000个服务器进程。 这显然是不现实的。 如果监听到的文件描述符预先放入一个集合中,一个发生事件(连接、通信)就进行处理。 这样就方便了。 所以,今天我们要学习IO复用。
五种I/O机型
数据块I/O
无阻塞I/O
I/O复用(选择和轮询) )。
信号驱动I/O
异步I/O
区块IO
最常见的I/O模型是块I/O模型,缺省情况下,所有套接字都被阻止。
无阻塞IO
IO复用
信号驱动器IO
异步IO
2 I/O复用
如果满足一个或多个I/O条件(例如,准备读取输入,或者描述符可以接收更多输出),则这种能力称为I/O复用,并由函数select和poll支持。
对于I/O多网络APP应用
当客户处理多个描述符时
一个客户同时处理多个接口
tcp服务器同时处理监听套接字接口和连接套接字接口时
如果服务器同时处理TCP和UDP
选择
/* According to POSIX.1-2001 */
#include sys/select.h
/* accordingtoearlierstandards * /
#include sys/time.h
#include sys/types.h
#include unistd.h
intselect(intnfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct timeval *timeout;
voidFD_clr(intFD,fd_set *set ); //从集合中删除描述词
intFD_isset(intFD,fd_set *set ); //说明字是否在此集合中
voidFD_set(intFD,fd_set *set ); //向集合添加描述符
voidFD_zero(FD_set*set ); //清空描述词集合
角色:使用函数,进程可以指示内核等待多个事件之一的发生,并且只能在一个或多个事件发生或经过指定时间后调用进程
即时响应多个套接字的读/写事件
参数:
nfds :集合中最大的文件描述符1 (指定要测试的描述符的数量。 该值是被测试的最大描述符加1,描述符为0、1、2……。 将测试到NFDS ) )。
readfds :用于检查读取事件的容器
writefds :用于检查写入事件的容器
timeout :超时时间
返回值:返回触发器套接字的数量
中间的三个参数readset、writeset和exceptset指定了测试内核的读取、写入和异常条件所需的描述符
如果对条件不感兴趣,可以将这三个参数的相应参数设置为空指针
timeout参数
时间的结构如下。
struct timeval (
long tv_sec; //秒
long tv_usec; //微秒
);
timeout参数有三种可能性
永远等待—将timeout设置为空指针,以便仅在有准备好进行I/O的描述符时返回
固定等待时间:某些描述符准备返回I/O,但不超过timeout参数中指定的timeval结构中指定的秒数和微秒数
完全不等:检查说明文后马上回来。 这称为轮询。 计时器的值必须为0
fd_set参数
select使用描述符集。 通常是整数数组,描述符对应于每个数的位。
使用进程
要使用select完成以前的套接字测试,请执行以下步骤:
客户端的代码保持不变。
#include sys/types.h
#include sys/socket.h
# includenetinet/in.h//sockaddr _ in
#include stdio.h
#include string.h
//TCP
int main ( ) )
{
int fd;
输入返回;
int addrLen;
char acbuf[20]=;
struct sockaddr_in serAddr={0};
struct sockaddr_in myAddr={0};
//1 .插座();
FD=socket(pf_inet,SOCK_STREAM,0 );
if(FD==-1 ) ) ) )。
{
错误( socket );
返回- 1;
}
//2.connect ) )要连接到服务器的地址
serAddr.sin_family=AF_INET;
seraddr.sin_port=htons(1234;
ser addr.sin _ addr.s _ addr=inet _ addr ( 192.168.159.5 );
ret=connect(FD,( struct sockaddr * ) serAddr,sizeof ) structsockaddr_in );
if(ret==-1 ) ) ) )。
{
perror (连接;
返回- 1;
}
//取得自己的地址
addrlen=sizeof ( struct sockaddr _ in;
ret=getsockname(FD,( struct sockaddr * ) myAddr,addrLen );
if(ret==-1 ) ) ) )。
{
perror(getsockname;
返回- 1;
}
printf(client----IP:%s,port: %d\\\\n,\\\&; quot;
inet_ntoa(myaddr.sin_addr ),ntohs ( myaddr.sin _ port );
//3 .通信
while(1)。
{
打印( send );
闪存( stdout;
Scanf(%s,acbuf );
if(strcmp(ACbuf,exit )==0) ) ) ) ) )。
{
黑;
}
write(FD,acbuf,strlen ) ) acbuf );
}
//4.close ( ) )
close(FD );
返回0;
}
服务器端:
select.c
#include sys/types.h
#include sys/socket.h
# includenetinet/in.h//sockaddr _ in
#include stdio.h
#include string.h
#include signal.h
#include sys/select.h
#include unistd.h
#include sys/time.h
//TCP
int main ( ) )
{
int fd;
int clientfd;
输入返回;
pid_t pid;
int i;
int maxfd; //当前最大套接字
int nEvent;
fd_set set={0}; //监听集合
fd_set oldset={0}; //存储要侦听的所有文件描述符
struct timeval time={0};
int reuse=0;
char acbuf[20]=;
char client_addr[100]=;
struct sockaddr_in addr={0}; //自己的地址
struct sockaddr _ in clientaddr={0}; //连接的客户端的地址
int addrlen=sizeof ( struct sockaddr _ in;
signal(sigchld,SIG_IGN );
//1 .插座() )。
FD=socket(pf_inet,SOCK_STREAM,0 );
if(FD==-1 ) ) ) )。
{
错误( socket );
返回- 1;
}
//仍然存在非活动套接字,禁止绑定端口,并出现错误:使用地址阵列。
由于//TCP套接字TIME_WAIT,bind返回EADDRINUSE,该状态保留2-4分钟
if(setsockopt(FD,SOL_SOCKET,SO_REUSEADDR,reuse,sizeof ) reuse )0) )
{
perror(setsockopeterror(\n;
返回- 1;
}
//2.bind ( ) )。
addr.sin_family=AF_INET;
ADDR.sin_port=htons(1234;
addr.sin _ addr.s _ addr=inet _ addr ( 192.168.159.5 );
ret=bind(FD,( struct sockaddr * ) addr,addrLen );
if(ret==-1 ) ) () ) ) ) ) ) ) ) ) ) )。
{
密码( bind );
返回- 1;
}
//3.listen ( ) )
ret=Listen(FD,10 );
if(ret==-1 ) ) () ) ) ) ) ) ) ) ) ) )。
{
perror (列表);
返回- 1;
}
//创建监听集
FD_zero(oldset );
FD_set(FD,oldset );
//maxfdp1 :当前侦听的最大套接字。 例如,如果当前软盘的值=3,则最大套接字为3
//所以,每次连接客户端时,都尝试比较文件描述符
maxfd=fd;
//select
//select之前,set放置了要监听的所有文件描述符; { 3,4,5 }
//select之后,set中只剩下发生事件的文件描述符。 {3}
while(1)。
{
set=oldset;
打印( before accept.\\& amp ); quot; n );
time.tv_sec=5;
nevent=select(maxfd1,set,NULL,NULL,time ); //返回文件描述符的数量(即事件的数量)
after accept.% d\\ n,nEvent );
if(nevent==-1 ) ) )。
{
perror (选择;
返回- 1;
}
elseif(nevent==0)//超时
{
printf (时间输出;
返回1;
}
else
{
//发生了事件
//判断是否是客户端发生的事件
for(I=0; i=maxfd; I )
{
if(FD_isset(I,set ) )
{
if(I==FD ) )。
{
clientFD=accept(FD,( struct sockaddr * ) clientAddr,addrLen );
FD_set(clientFD,oldset );
printf(clientIP:%s,port:\\n,inet_ntoa ) clientaddr.sin_addr,ntoHS ) clientaddr.sin_port )
客户端fdmaxfd ( if ) )。
{
maxfd=clientfd;
}
}
else
{
memset(ACbuf,0,20 );
if (读( I,acbuf,20 )==0)//客户端退出
{
close(I;
//再从收藏中删除
FD_clr(I,oldset );
}
else
printf(receive:%s\\\n,acbuf );
}
}
}
}
}
返回0;
}
电子邮件
在epoll中使用的函数如下。
#include sys/epoll.h
intepoll_create(intsize; 创建电子表格
intepoll_CTL(intepfd,int op,int fd,struct epoll_event *event ); //操作函数
intepoll_wait(intepfd,struct epoll_event *events,
int maxevents,int timeout;
事件集合的结构:
(请注意,其中epoll的超时参数为int,单位为us。)
使用进程
#include sys/types.h
#include sys/socket.h
# includenetinet/in.h//sockaddr _ in
#include stdio.h
#include string.h
#include signal.h
#include sys/epoll.h
//epoll
//epoll_wait(epoll_creat ) ( epoll_ctl ) )
//TCP
int main ( ) )
{
int fd;
int clientfd;
输入返回;
pid_t pid;
int i;
int epfd;
int nEvent;
struct epoll_event event={0};
struct epoll _ event RTL _ events [ 20 ]={0}; //事件结果集
int reuse=0;
char acbuf[20]=;
char client_addr[100]=;
struct sockaddr_in addr={0}; //自己的地址
struct sockaddr _ in clientaddr={0}; //连接的客户端的地址
int addrlen=sizeof ( struct sockaddr _ in;
signal(sigchld,SIG_IGN );
//1 .插座() )。
FD=socket(pf_inet,SOCK_STREAM,0 );
if(FD==-1 ) ) ) )。
{
错误( socket );
返回- 1;
}
//仍然存在非活动套接字,禁止绑定端口,并出现错误:使用地址阵列。
由于//TCP套接字TIME_WAIT,bind返回EADDRINUSE,该状态保留2-4分钟
if(setsockopt(FD,SOL_SOCKET,SO_REUSEADDR,reuse,sizeof ) reuse )0) )
{
perror(setsockopeterror(\n;
返回- 1;
}
//2.bind ( ) )。
addr.sin_family=AF_INET;
ADDR.sin_port=htons(1234;
addr.sin _ addr.s _ addr=inet _ addr ( 192.168.159.5 );
ret=bind(FD,( struct sockaddr * ) addr,addrLen );
if(ret==-1 ) ) ) )。
{
密码( bind );
返回- 1;
}
//3.listen ( ) )
ret=Listen(FD,10 );
if(ret==-1 ) ) () ) ) ) ) ) ) ) ) ) )。
{
perror (列表);
返回- 1;
}
epfd=epoll_create(1000; //同时侦听的文件描述符
event.data.fd=fd;
event.events=EPOLLIN; //阅读
epoll_CTL(epfd,EPOLL_CTL_ADD,fd,event );
while(1)。
{
//nevent=epoll_wait(epfd,rtl_events,20,-1); //-1:块0 :非块
nevent=epoll_wait(epfd,rtl_events,20,5000 );
if(nevent==-1 ) ) )。
{
perror(epoll_wait );
返回- 1;
}
elseif(nevent==0) )。
{
打印( time out.);
}
else
{
//一旦发生事件,立即处理
for(I=0; i nEvent; I )
{
//对于服务器fd
if(RTL_events[I].data.FD==FD ( ) ) ) ) ) ) ) )。
{
clientFD=accept(FD,( struct sockaddr * ) clientAddr,addrLen );
//添加
event.data.fd=clientfd;
event.events=EPOLLIN; //阅读
epoll_CTL(epfd,EPOLL_CTL_ADD,clientfd,event );
printf(clientIP:%s,port:\\n,inet_ntoa ) clientaddr.sin_addr,ntoHS ) clientaddr.sin_port )
}
else
{
//否则客户端软盘
memset(ACbuf,0,20 );
ret=read(RTL_events[I].data.FD,acbuf,20 );
printf(%d\\\n,ret );
if(ret==0)//客户端退出
{
close(RTL_events[I].data.FD );
//从收藏中删除
epoll_CTL(epfd,EPOLL_CTL_DEL,rtl_events[i].data.fd,NULL );
}
else
printf(receive:%s\\\n,acbuf );
}
}
}
}
返回0;
}
运行结果和以前一样,正常收发。
详情请访问云服务器、域名注册、虚拟主机的问题,请访问西部数码代理商官方网站: www.chenqinet.cn