同步I/O与异步I/O对比
POSIX把这两个术语定义如下:
·同步I/O操作(synchronous I/O operation)导致请求进程阻塞,直到I/O操作完成。
·异步I/O(asynchronous I/O operation)不导致请求进程阻塞。
根据上述定义,我们前4种模型----阻塞I/O模型、非阻塞I/O模型、I/O复用模型和信号去驱动I/O模型都是同步I/O模型,因为其中真正的I/O操作(recvfrom)将阻塞进程。只有异步I/O模型与POSIX定义的异步I/O相匹配。
#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; //mircoseconds
}
void FD_ZERO(fd_set *fdset); //从fdset中清除所有的文件描述符
void FD_SET(int fd, fd_set *fdset); //将fd加入到fdset
void FD_CLR(int fd, fd_set *fdset); //将fd从fdset里面清除
int FD_ISSET(int fd, fd_set *fdset); //判断fd是否在fdset集合中
fd_set rset;
FD_ZERO(&rset);
FD_SET(1, &rset);
FD_SET(4 &rset);
FD_SET(5, &rset);
对于可读文件描述符集以下四种情况会导致置位:
1、socket接收缓冲区中的数据量大于或等于当前缓冲区的低水位线.此时对于read操作不会被阻塞并且返回一个正值(读取的字节数).低水位线可以通过SO_RCVLOWAT选项设定,对于Tcp和Udp来说其默认值为1.
2、socket连接的读端被关闭,如shutdown(socket, SHUT_RD)或者close(socket).对应底层此时会接到一个FIN包,read不会被阻塞但会返回0.代表读到socket末端.
3、socket是一个监听socket并且有新连接等待.此时accept操作不会被阻塞.
4、发生socket错误.此时read操作会返回SOCKET_ERROR(-1).可以通过errno来获取具体错误信息.
对于可写文件描述符集以下四种情况会导致置位:
1、socket发送缓冲区中的可用缓冲大小大于或等于发送缓冲区中的低水位线并且满足以下条件之一
(1)、socket已连接
(2)、socket本身不要求连接,典型如Udp
低水位线可以通过SO_SNDLOWAT选项设置.对于Tcp和Udp来说一般为2048.
2、socket连接的写端被关闭,如shutdown(socket, SHUT_WR)或者close(socket).在一个已经被关闭写端的句柄上写数据会得到SIGPIPE的信号(errno).
3、一个非阻塞的connect操作连接成功 或者 connect操作失败.
4、发生socket错误.此时write操作会返回SOCKET_ERROR(-1).可以通过errno来获取具体错误信息.
对于异常文件描述符集只有一种情况(针对带外数据):
当收到带外数据(out-of-band)时或者socket的带外数据标志未被清除.
下面看个具体例子:
server
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8888
#define MAXSIZE 128
int main()
{
int i,nbyte;
int listenfd, confd, maxfd;
char buffer[MAXSIZE];
fd_set global_rdfs, current_rdfs;
struct sockaddr_in addr,clientaddr;
int addrlen = sizeof(struct sockaddr_in);
int caddrlen = sizeof(struct sockaddr_in);
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket error");
exit(-1);
}
else
{
printf("socket successfully!\n");
printf("listenfd : %d\n",listenfd);
}
memset(&addr, 0 ,addrlen);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listenfd,(struct sockaddr *)&addr,addrlen) == -1)
{
perror("bind error");
exit(-1);
}
else
{
printf("bind successfully!\n");
printf("listen port:%d\n",PORT);
}
if(listen(listenfd,5) == -1)
{
perror("listen error");
exit(-1);
}
else
{
printf("listening...\n");
}
maxfd = listenfd;
FD_ZERO(&global_rdfs);
FD_SET(listenfd,&global_rdfs);
while(1)
{
current_rdfs = global_rdfs;
if(select(maxfd + 1,¤t_rdfs, NULL, NULL,0) < 0)
{
perror("select error");
exit(-1);
}
for(i = 0; i <= listenfd + 1; i++)
{
if(FD_ISSET(i, ¤t_rdfs))
{
if(i == listenfd)
{
if((confd = accept(listenfd,(struct sockaddr *)&clientaddr,&caddrlen)) == -1)
{
perror("accept error");
exit(-1);
}
else
{
printf("Connect from [IP:%s PORT:%d]\n",
inet_ntoa(clientaddr.sin_addr),clientaddr.sin_port);
FD_SET(confd,&global_rdfs);
maxfd = (maxfd > confd ? maxfd : confd);
}
}
else
{
if((nbyte = recv(i, buffer, sizeof(buffer),0)) < 0)
{
perror("recv error");
exit(-1);
}
else if(nbyte == 0)
{
close(i);
FD_CLR(i,&global_rdfs);
}
else
{
printf("recv:%s\n",buffer);
send(i, buffer, sizeof(buffer),0);
}
}
}
}
}
return 0;
}
执行结果如下:
fs@ubuntu:~$ cd qiang/select/
fs@ubuntu:~/qiang/select$ ./select2
socket successfully!
listenfd : 3
bind successfully!
listen port:8888
listening...
Connect from [IP:192.168.3.51 PORT:1992]
recv:hello
Connect from [IP:192.168.3.53 PORT:2248]