陈奇网络工作室

套接字-io复用技术

系统运输

上一个地址(使用套接字,当一台服务器完成了与多台客户端对应的小实验时,必须为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

相关推荐

后台-系统设置-扩展变量-手机广告位-内容页底部广告位3