I/O复用典型使用网络应用场所
1、当客户处理多个描述符(通常是交互式输入和网络套接字)时,必须使用I/O复用,
2、一个客户同时处理多个套接字是可能的
3、如果一个TCP服务器既要处理监听套接字,又要处理已连接套接字
4、一个服务器既要处理TCP,又要处理UDP
5、如果一个服务器要处理多个服务或者多个协议
I/O模型
阻塞式I/O
非阻塞式I/O
I/O复用(select和poll)
信号驱动式I/O(SIGIO)
异步I/O
一个输入操作通常包括两个不同的阶段
1,等待数据准备好
2,从内核向进程复制数据
阻塞式I/O模型
所有套接字都是阻塞的
进程调用recvfrom的时候会阻塞等待数据到来
非阻塞I/O模型
进程调用recvfrom的时候内核会立即返回一个 EWOULDBLOCK错误,等数据准备好了才返回成功
select 函数
#include
#include
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
参数介绍
struct timeval {
long tv_sec ; /*seconds*/
long tv_usec; /* microseconds*/
}
(1)永远等待下去:仅在有一个描述符准备好I/O时才返回,设置参数用空指针
(2)等待一段时间;在有一个描述符准备好I/O时返回,但不超过指定的时间。设置参数为指定时间
(3)根本不等待:检查描述符后立即返回。设置参数中结构体中值为0 ,
还有考虑移植性问题,每次调用select之前都需要对它进行初始化。
void FD_ZERO(fd_set *fdset); /*clear all bits in fdset*/
void FD_SET(int fd,fd_set *fdset); /*turn on the bit for fd in fdset*/
void FD_CLR(int fd,fd_set *fdset); /*turn off the bit for fd in fdset*/
int FD_ISSET(int fd,fd_set *fdset); /*is the bit for fd on in fdset ?*/
maxfdp1参数指定待测试的描述符个数,它的值是待测试的最大描述符加1,
在sys/sekect.h中定义的FD_SETSIZE的常值是fd_set中描述符的总数,通常为1024
描述符就绪条件
一个套接字准备好读,(满足四个条件任何一个)
1、该套接字接收缓冲区中的数据字节大于等于套接字缓冲区低水平位标记的当前大小,不会阻塞,返回一个大于0的值
可以使用SO_RECVLOWAT套接字选项设置该套接字的低水平位,对用tcp或者udp默认值为1
2、该连接的读半部关闭(接收了FIN的tcp连接),不阻塞,返回0(EOF)
3、该套接字是一个监听套接字且已完成的连接数不为0。对这样的套接字的accept通常不会阻塞
4、其上有一个套接字错误待处理。
一个套接字准备好写,(满足四个条件任何一个)
1、该套接字发送缓存区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小,并且或者该套接字已经连,或者该套接字不需要连接(udp套接字)。
2、该连接的写半部关闭。
3、使用非阻塞式connect的套接字已建立连接,或者connect已经以失败告终
4、其上有一个套接字错误待处理。
str_cli函数
#include "unp.h"
void str_cli(FILE *fp,int sockfd)
{ int maxfdp1;
fd_set rset;
char sendline[MAXLINE],recvline[MAXLINE];
FD_ZERO(&rset);
for(;;)
{
FD_SET(fileno(fp),&rset);
FD_SET(sockfd,&rset);
macfdp1 = max(fileno(fp),sockfd) + 1;
Select(maxfdp1,&rset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&rset)){
if(Readline(sockfd,recvline,MAXLINE) == 0)
err_quit();
Fputs(recvline,stdout);
if(FD_ISSET(fileno(fp),&rset)){
if(Fgets(sendline,MAXLINE,fp) == NULL)
return;
Writen(sockfd,sendline,strlen(sendline));
}
}
}
我们需要一种关闭tcp连接其中一半的方法。我们想给服务器发送一个FIN,告诉它我们已经完成了数据发送,但是仍然保持套接字描述符打开以便读取。
未完待续。。。