四、信号驱动 I/O 模型
老陈接收到新的信件后,一般的程序是:
打开信封—-掏出信纸 —-阅读信件—-回复信件 ……为了进一步减轻用户负担,小区物业又开发了一种新的技术:住户只要告诉小区物业对信件的操作步骤,小区物业信箱将按照这些步骤去处理信件,不再需要用户亲自拆信 /阅读/回复了!这就是信号驱动I/O模型。
我们也可以用信号,让内核在描述字就绪时发送SIGIO信号通知我们。
首先开启套接口的信号驱动 I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说没被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号,我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它读取数据报。

无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间,进程不被阻塞,主循环可以继续执行,只要不时地等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取。
五、异步非阻塞模式
linux下的asynchronous IO其实用得很少。
与前面的信号驱动模型的主要区别在于:信号驱动 I/O是由内核通知我们何时可以启动一个 I/O操作,而异步 I/O模型是由内核通知我们 I/O操作何时完成 。
先看一下它的流程:

这就是异步非阻塞模式
以read系统调用为例
steps:
a. 调用read;
b. read请求会立即返回,说明请求已经成功发起了。
c. 在后台完成读操作这段时间内,应用程序可以执行其他处理操作。
d. 当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。
/*
* brief
* tcp client
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVPORT 8080
#define MAXDATASIZE 100
#define TFILE "data_from_socket.txt"
int main(int argc, char *argv[])
{
int sockfd, recvbytes;
char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */
char snd_buf[MAXDATASIZE];
struct hostent *host; /* struct hostent
* {
* char *h_name; // general hostname
* char **h_aliases; // hostname's alias
* int h_addrtype; // AF_INET
* int h_length;
* char **h_addr_list;
* };
*/
struct sockaddr_in server_addr;
/* */
fd_set readset, writeset;
int check_timeval = 1;
struct timeval timeout={check_timeval,0}; //阻塞式select, 等待1秒,1秒轮询
int maxfd;
int fp;
int cir_count = 0;
int ret;
if (argc < 3)
{
printf("Usage:%s [ip address] [any string]n", argv[0]);
return 1;
}
*snd_buf = ' ';
strcat(snd_buf, argv[2]);
if ((fp = open(TFILE,O_WRONLY)) < 0) //不是用fopen
{
perror("fopen:");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket:");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVPORT);
inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
memset(&(server_addr.sin_zero), 0, 8);
/* create the connection by socket
* means that connect "sockfd" to "server_addr"
*/
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect");
exit(1);
}
/**/
if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1)
{
perror("send:");
exit(1);
}
printf("send:%sn", snd_buf);
while (1)
{
FD_ZERO(&readset); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sockfd, &readset); //添加描述符
FD_ZERO(&writeset);
FD_SET(fp, &writeset);
maxfd = sockfd > fp ? (sockfd+1) : (fp+1); //描述符最大值加1
ret = select(maxfd, &readset, NULL, NULL, &timeout); // 非阻塞模式
switch( ret)
{
case -1:
exit(-1);
break;
case 0:
break;
default:
if (FD_ISSET(sockfd, &readset)) //测试sock是否可读,即是否网络上有数据
{
recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT);
rcv_buf[recvbytes] = ' ';
printf("recv:%sn", rcv_buf);
if (FD_ISSET(fp, &writeset))
{
write(fp, rcv_buf, strlen(rcv_buf)); // 不是用fwrite
}
goto end;
}
}
timeout.tv_sec = check_timeval; // 必须重新设置,因为超时时间到后会将其置零
cir_count++;
printf("CNT : %d n",cir_count);
}
end:
close(fp);
close(sockfd);
return 0;
}








