信号驱动式I/O的本质就是:进程预先告知内核当某个描写叙述符发生事件时,内核会向该进程发送SIGIO信号通知进程,进程可在信号处理函数中进行处理
进程能够通过fcntl打开O_ASYNC标志或ioctl打开FIOASYNC标志来通知内核,二者的差别是一些系统不支持fcntl,所以应尽量使用ioctl
对于TCP套接字产生SIGIO信号的条件:
1.监听套接字上有新连接请求完毕
2.某个断连请求发起
3.某个断连请求完毕
4.数据到达套接字
5.数据已从套接字发送走(输出缓冲区有空暇空间)
6.发生某个异步错误
对于UDP套接字产生SIGIO信号的条件:
1.数据报到达套接字
2.套接字上发生异步错误
对于套接字而言:TCP套接字和UDP套接字致使内核产生SIGIO信号的条件有所不同,当中TCP可产生该信号的条件较多,而UDP套接字产生该信号的条件仅仅有两个,由于我们无法得知详细是什么事件导致内核产生该信号。
server:
进程能够通过fcntl打开O_ASYNC标志或ioctl打开FIOASYNC标志来通知内核,二者的差别是一些系统不支持fcntl,所以应尽量使用ioctl
对于TCP套接字产生SIGIO信号的条件:
1.监听套接字上有新连接请求完毕
2.某个断连请求发起
3.某个断连请求完毕
4.数据到达套接字
5.数据已从套接字发送走(输出缓冲区有空暇空间)
6.发生某个异步错误
对于UDP套接字产生SIGIO信号的条件:
1.数据报到达套接字
2.套接字上发生异步错误
对于套接字而言:TCP套接字和UDP套接字致使内核产生SIGIO信号的条件有所不同,当中TCP可产生该信号的条件较多,而UDP套接字产生该信号的条件仅仅有两个,由于我们无法得知详细是什么事件导致内核产生该信号。
。对于UDP套接字产生该信号条件的推断就简单的多。这也是信号驱动式I/O主要用于UDP套接字的原因。
套接字使用信号驱动式I/O的步骤:
1.建立SIGIO信号处理函数
2.设置设置套接字属主(使用fcntl的F_SETOWN命令)
3.开启信号驱动式I/O(fcntl打开O_ASYNC 或 ioctl打开FIOASYNC)
实例:
客户每隔1秒钟将系统时间发送到server,server通过SIGIO信号处理函数中接收数据
net.h
#ifndef MY_NET_H #define MY_NET_H #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <arpa/inet.h> #include <unistd.h> #include <time.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <signal.h> #include <iostream> #include <sys/stat.h> #include <fcntl.h> #include <net/if.h> #include <net/if_arp.h> #include <sys/ioctl.h> #include <sys/ioctl.h> using namespace std; char recvBuf[1025]; int listenfd; static void sigio_handler(int signo) { if (signo != SIGIO) return; int ret; while (1) { ret = recvfrom(listenfd, recvBuf, sizeof(recvBuf), 0, NULL, NULL); if (ret < 0) { if (errno == EWOULDBLOCK) break; perror("recvfrom error"); exit(-1); } cout << recvBuf << endl; } } void init(int skfd) { int ret; //1.建立SIGIO信号的处理函数 signal(SIGIO, sigio_handler); //2.设置该套接字的属主 ret = fcntl(skfd, F_SETOWN, getpid()); if (ret < 0) { perror("fcntl error"); exit(-1); } //3.开启该套接字的信号驱动式I/O int on = 1; ret = ioctl(skfd, FIOASYNC, &on); if (ret < 0) { perror("ioctl error"); exit(-1); } } int prepare() { int skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) return -1; struct sockaddr_in saddr; bzero(&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(9999); saddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(skfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) return -1; return skfd; } #endif
客户:
#include "net.h" int main() { int skfd; skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd < 0) { perror("socket error"); exit(-1); } int ret; time_t tm; struct sockaddr_in desAddr; bzero(&desAddr, sizeof(desAddr)); desAddr.sin_family = AF_INET; desAddr.sin_port = htons(9999); desAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); while (1) { time(&tm); ret = sendto(skfd, ctime(&tm), strlen(ctime(&tm)), 0, (struct sockaddr*)&desAddr, sizeof(desAddr)); if (ret < 0) { perror(""); exit(-1); } sleep(1); } return 0; }
server:
#include "net.h" int main() { listenfd = prepare(); if (listenfd < 0) { perror("prepare error"); exit(-1); } init(listenfd); while(1); return 0; }