I/O复用的系统调用——select

I/O复用使得程序能监听多个文件描述符,这对提高程序的性能至关重要。

I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的,并且当多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依次处理其中的每一个文件描述符,这使得服务器程序看起来像是串行工作的。如果要实现并发,只能使用多进程或多线程等编程手段。

Linux下实现I/O复用的系统调用主要有select、poll、epoll。


select系统调用

select系统调用的用途是:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。

原型如下:

#include<sys/select.h>

int select(int nfds, fd_set *readfds, fd_set* writefds, fd_set* exceptfds, srtuct timeval *timeout);


nfds: 最大文件描述符+1

readfds、writefds和exceptfds参数分别表示可读、可写和异常等事件对应的文件描述符的集合。应用程序调用select函数时,通过这三个参数传入自己感兴趣的文件描述符。select调用返回时,内核将修改他们来通知应用程序哪些文件描述符已经就绪。这三个参数是fd_set结构指针类型,定义如下:

FD_ZREO(fd_set *fdset);

FD_SET(int fd, fd_set *fdset);

FD_CLR(int fd,fdJ_set *fdset);

int FD_ISSET(int fd, fd_set *fdset);      

timeout参数用来设置select函数的超时时间,它是一个timeval结构体指针,定义如下:

struct timeval

{

     long tv_sec;        //秒数

     long tv_usec;       //微妙数

}   


如果给timeout变量的所有成员都传递0,则select将立即返回。如果给timeout传递NULL,则select将一直阻塞,直到某个文件描述符就绪。

  select成功时返回就绪文件描述符的总数。如果在超时时间内没有任何文件描述符就绪,select将返回0。失败返回-1并设置error。如果在select等待期间,程序接收到信号,则select立即返回-1,并设置error为EINTR。

  下面来看实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

void main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(sockfd != -1);

	struct sockaddr_in ser, cli;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("192.168.101.250");

	int res = bind(sockfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	listen(sockfd, 5);

	int nfds = sockfd + 1;
	fd_set read;

	int fds[128];
	int i=0;
	for(;i<128;i++)
	{
		fds[i]=-1;
	}
	fds[0] = sockfd;

	while(1)
	{
		FD_ZERO(&read);     //清空
		int i = 0;
		for(; i < 128; ++i)
		{
			if(fds[i] != -1)
			{
				FD_SET(fds[i], &read);
			}
		}
		int n = select(nfds, &read, NULL, NULL, NULL);
		if(n < 0)
		{
			printf("error\n");
			exit(0);
		}
		if(n == 0) //  超时
		{
			printf("time out\n");
			continue;
		}
		 i = 0;
		for(; i < 128; ++i)
		{
			if(fds[i] == -1)
				continue;

			if(FD_ISSET(fds[i], &read))
			{
				if(fds[i] == sockfd)
				{
					int len = sizeof(cli);
					int c = accept(sockfd, (struct sockaddr*)&cli, &len);
					printf("one client link \n");
					int i = 0;
					for(; i < 128; ++i)
					{
						if(fds[i] == -1)
						{
							fds[i] = c;
							if((nfds-1)<c)
							{
								nfds=c+1;
							}
							break;
						}
					}
				}
				else
				{
					char buff[128] = {0};
					int n = recv(fds[i], buff, 128, 0);
					if(n <= 0)
					{
						close(fds[i]);
						fds[i] = -1;
						continue;
					}

					printf("%s\n", buff);
					send(fds[i], "ok", 2, 0);
				}
			}
		}

	}
}

运行结果如下:

服务器:


客户端:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值