Seclet多路I/O复用之TCP网络编程

多路复用I/O

  • 应用程序中同时处理多路输入输出流,若采用阻塞模式,将得不到预期的目的;
  • 若采用非阻塞模式,对多个输入进行轮询,又太浪费CPU时间;
  • 若设置多个进程,分别处理一条数据通路,将新产生进程间同步与通信问题,使程序变得更加复杂;
  • 比较好的方法是使用I/O多路复用。其基本思想是:
    1)先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
    2)函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

Select使用原理

  • 把关心的文件描述符加入到集合中(fd_set)
void FD_ZERO(fd_set * fdset);            //对集合清零
void FD_SET(int fd,fd_set * fdset);      //把fd加入到集合
void FD_CLR(int fd , fd_set * fdset);    //从集合中清除fd
int  FD_ISSET(int fd ,fd_set * fdset);   //判断fd是否在集合中
  • 调用select()函数去监控集合fd_set中哪些文件描述符(阻塞),等待集合中一个或多个文件描述符有数据
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
/*
参数:
	nfds:     监控的文件描述符里最大文件描述符加一,此参数会告诉内核检测前多少个文件描述符的状态。
	readfds:  监控有读数据到达文件描述符集合,传入传出参数
	writefds: 监控有写数据到达文件描述符集合,传入传出参数
	exceptfds:监控异常发生到达文件描述符集合,如带外数据到达异常,传入传出参数
	timeout:  定时阻塞监控时间,有3种情况。
			   1)NULL,永远等待下去
			   2)设置timeval,等待固定时间
			   3)设置timeval里的时间均为0,不等待,轮询
返回值:
	大于0,就绪的文件描述符
	等于0,超时
	等于-1,出错
*/	
struct timeval{
	long tv_sec;   //seconds
	long tv_usec;  //microseconds微秒
	}
  • 当有数据时,退出select()阻塞
    #)select()退出后,集合表示有数据的集合
  • 依次判断哪个文件描述符有数据,依次处理有数据的文件描述符的数据
1)若是监听套接字上有数据,则有新的客户端连接,则accept(),并且加入集合
if(FD_ISSET(fd,&fd_set))
{...}
FD_SET(fd,&fd_set)
2)若是已连接的套接字上有数据,则处理数据

服务端代码

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, const char *argv[])
{
	int listenfd,maxfd;
	listenfd=socket(AF_INET,SOCK_STREAM,0);
	struct sockaddr_in server_addr;
	server_addr.sin_family=AF_INET;
	server_addr.sin_port = htons(8888);
	server_addr.sin_addr.s_addr=inet_addr("192.168.9.10");
	bind(listenfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
	listen(listenfd,10);
	fd_set setfd;
	maxfd=listenfd;
	int nread;
	struct timeval time;
	struct sockaddr_in cliaddr;
	socklen_t len = sizeof(cliaddr);
	int cfd;
	char ipv4_addr[16];
	int client[1024];
	int i,maxi,sockfd;
	maxi=-1;
	for(i=0;i<1024;i++)
	{
		client[i]=-1;
	}
	char buf[20];
	while(1)
	{
		FD_ZERO(&setfd);
		FD_SET(0,&setfd);
		FD_SET(1,&setfd);
		FD_SET(listenfd,&setfd);
	 	time.tv_sec = 5;
		time.tv_usec =0;
		nread=select(maxfd+1,&setfd,NULL,NULL,&time);
		if(nread<0)
		{
			perror("select");
			exit(0);
		}
		if(nread==0)
		{
			continue;
		}
		if(FD_ISSET(listenfd,&setfd))
		{
			cfd=accept(listenfd,(struct sockaddr*)&cliaddr,&len);
			inet_ntop(AF_INET,&cliaddr.sin_addr,ipv4_addr,sizeof(ipv4_addr));
			printf("client is connect,addr is %s,port is %d\n",ipv4_addr,ntohs(cliaddr.sin_port));
			FD_SET(cfd,&setfd);
			for(i=0;i<1024;i++)
			{
				if(client[i]<0)
				{
					client[i]=cfd;
					break;
				}
			}
			if(i==1024)
			{
				printf("too many client");
				exit(0);
			}
			maxi=i;
			FD_CLR(listenfd,&setfd);
		}
		for(i=0;i<maxi+1;i++)
		{
			sockfd=client[i];
			if(client[i]<0)
			{
				continue;
			}
			if(FD_ISSET(sockfd,&setfd))
			{
				while(1)
				{
					memset(buf,0, sizeof(buf));
					read(sockfd,buf,sizeof(buf));
					printf("client_addr is %s,port is %d,Client has data is %s\n",ipv4_addr,ntohs(cliaddr.sin_port),buf);
					if(strcmp(buf,"stop")==0)
					{
						printf("Client is quit ,addr is %s,port is %d\n",ipv4_addr,ntohs(cliaddr.sin_port));
						break;
					}
				}
				FD_CLR(sockfd,&setfd);
				close(sockfd);
				client[i]=-1;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值