实验7 I/O 多路复用

实验7

I/O 多路复用

1 要掌握的数据类型

文件描述符集合fd_set

利用宏FD_ZERO(&rset)初始化

利用宏FD_SET(sockfd,&rset)sockfd 加入到rset

利用宏FD_CLR(sockfd,&rset)sockfd rset 中删除

利用宏FD_ISSET(sockfd,&rset)判定sockfd 是否在rset 集合中

2 select 函数

函数原型:

int select(int maxfd,fd_set *readset,fd_set *writeset,fd_set  *exceptset,const struct

timeval *timeout);

struct timeval{

long tv_sec;

long tv_usec;

}

(假定maxfd 足够大,通常取三个集合中文件描述符(int)最大的加一)

select 函数的功能是监视readsetwriteset execptset 中的文件是否准备就绪,

如果timeout 没有超时的话,且三个集合中没有任何文件就绪,则阻塞。如果有

某些文件就绪,则函数返回。

返回时,还在集合中的文件描述符就是已经准备就绪的文件描述符。

3 利用select 函数的程序模型

3.1 简单tcp 服务器的模型

1

2

3

4

调用socket 函数,int fd= socket(…)产生监听套接字fd

设置地址结构体struct sockaddr_in server

绑定server fd

监听

while(1)

{

5 利用accetpt 函数int confd=accept(fd,….)产生连接套接字confd

for(;;)

{

read(confd……);

}

}



select 中,首先要经过select 判断是否有套接字可读,再去读取可用读的套接

fd_set rset;

1 调用socket 函数,int fd= socket(…)产生监听套接字fd

2 设置地址结构体struct sockaddr_in  server

3 绑定server fd

4 监听

while(1)

{

fd 加入文件描述符集合rset;

select(maxfd+1,&rset,NULL,NULL,NULL);

下面开始探测哪个文件描述符是可读的

if fd 可读

{

//说明fd 可读

int confd=accept(fd,…..,)//调用accept 函数产生连接套接字confd

confd 加入rset

}

if 连接套接字可读

{

去找哪个连接套接字可读比如xfd 可读

调用read 函数n= read(xfd,buf,BUFSIZE);

如何n 0 则对方关闭了连接

xfd rset 中删除

}

}

3.2 更加细化的模型(加入数组记录每个客户端的信息)

更加细化的select 程序,本质上有一个监听套接字和N 个连接套接字(N

客户端),其中N 个连接套接字都是由一个监听套接字产生的。

因此,需要一个数组记录这N 个连接套接字的信息(具体包括套接字的文件描

述符,套接字对应的客户端的地址和端口,套接字的通信消息的缓冲区)

另外,需要考虑的是每次select 返回后,都会修改rset 的值,因此用两个文

件描述符集合,在while 循环的开始,rset

程序的伪代码如下:

struct CLIENT

{

char buf[BUFSIZE];

int fd;

struct sockaddr_in addr;

}

struct CLIENT client[MAX_CLIENT];

fs_set rset,allset



1 调用socket 函数,int fd= socket(…)产生监听套接字fd

2 设置地址结构体struct sockaddr_in  server

3 绑定server fd

4 监听

while(1)

{

fd 加入文件描述符集合allset;

Rset=allset

select(maxfd+1,&rset,NULL,NULL,NULL);

if (fd 在集合rset

{

int confd=accept(fd,…..,)//调用accept 函数产生连接套接字confd

confd 加入rset

}

for(i=0;i<MAX_CLIENT;i++)

{

if(client[i].fd rset )

{

调用read 函数n= read(client[i].fd,client[i].buf,BUFSIZE);

if(n==0) {对方关闭了连接, client[i].fd allset 中删除

并且close(client[i].fd)}

否则打印client[i].buf 中的内容

}

}

}

4 文件代码如下:

#include <stdlib.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/stat.h>

#include <netinet/in.h>

#include <unistd.h>

#include <pthread.h>

#include <stdio.h>

#include <string.h>

#include <arpa/inet.h>

#define PORT 5001



#define BUFSIZE 1024

#define CLIENT_SIZE 50

#define MAX_CLIENT 100

void printbi8(fd_set a)

{

int i;

char k=*((char*)(&a));

for(i=0;i<8;i++)

{

if((k&1)==1)printf("1");

else printf("0");

k=k>>1;

}

printf("\r\n");

}

struct CLIENT

{

char buf[BUFSIZE];

int fd ;

struct sockaddr_in addr;

};

struct CLIENT client[MAX_CLIENT];

int main()

{

int i;

for(i=0;i<MAX_CLIENT;i++)

client[i].fd=-1;

int fd=socket(AF_INET,SOCK_STREAM,0);

struct sockaddr_in server;

struct sockaddr_in cli;

inet_aton("0.0.0.0",&server.sin_addr);

server.sin_port=htons(PORT);

server.sin_family=AF_INET;

int bret=bind(fd,(struct sockaddr*)&server,sizeof server);

if(bret<0){printf("error bind!\r\n");exit(-1);};

listen(fd,128);

fd_set rset,allset;

FD_ZERO(&allset);



FD_SET(fd,&allset);

int maxfd=fd;

while(1)

{

rset=allset;

//printbi8(rset);

int rs=select(maxfd+1,&rset,NULL,NULL,NULL);

//printbi8(rset);

if(rs<0){printf("error select!\r\n");exit(-1);};

if(FD_ISSET(fd,&rset))

{

//监听套接字可读

int addrlen=sizeof(cli);

int confd=accept(fd,(struct  sockaddr*)&cli,&addrlen);

if(confd<0){printf("error  accept!\r\n");exit(-1);};

//去找到一个可使用的client[x]

for(i=0;i<MAX_CLIENT;i++)

{

if(client[i].fd==-1)

{

client[i].fd=confd;

client[i].buf[0]=0;

memcpy(&client[i].addr,&cli,sizeof  cli);

FD_SET(confd,&allset);//加入文件描述符集合,

maxfd=confd>maxfd?confd:maxfd;

printf("72 hello!\r\n");

break;

}

}

if(i==MAX_CLIENT){printf("TOO MANY Clients!\r\n");exit(-1);};

if(--rs==0)continue;// 如果只有一个文件是可读的,则不用去查看

client 数组中的文件是否可读了

}

for(i=0;i<MAX_CLIENT;i++)

{

if(FD_ISSET(client[i].fd,&rset))

{



int k=read(client[i].fd,client[i].buf,BUFSIZE);

if(k==0)

{//客户端退出

printf("client[%d]from%s:%d

quit!\r\n",i,inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port));

FD_CLR(client[i].fd,&allset);

close(fd);

client[i].fd=-1;

if(--rs==0)break;//没有可读文件描述符了

continue;

}

if(k==-1){printf("error  in read!\r\n");exit(-1);};

client[i].buf[k]=0;

printf("client[%d]from%s:%dsend

message:%s\r\n",i,inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port),clie

nt[i].buf);

if(--rs==0)break;

}

}//end of for

}//end of while

}




你可能感兴趣的:(网络编程)