(1)客户端向服务器发出日期时间请求字符串,如:%D %Y %A %T等。
(2)服务器从网络接收到日期时间请求字符串后,根据字符串格式生成对应的日期时间值返回给客户端。
为了在一个套接字上使用信号驱动 I/O 操作,下面这三步是所必须的。
(1)一个和 SIGIO信号的处理函数必须设定。
(2)套接字的拥有者必须被设定。一般来说是使用 fcntl 函数的 F_SETOWN 参数来
进行设定拥有者。
(3)套接字必须被允许使用异步 I/O。一般是通过调用 fcntl 函数的 F_SETFL 命令,O_ASYNC为参数来实现。虽然设定套接字为异步 I/O 非常简单,但是使用起来困难的部分是怎样在程序中断定产生 SIGIO信号发送给套接字属主的时候,程序处在什么状态。
在 UDP 协议上使用异步 I/O 非常简单.这个信号将会在这个时候产生:
(1)套接字收到了一个数据报的数据包。
(2)套接字发生了异步错误。
当我们在使用 UDP 套接字异步 I/O 的时候,我们使用 recvfrom()函数来读取数据报数据或是异步 I/O 错误信息。
2.TCP 套接字的 SIGIO 信号
不幸的是,异步 I/O 几乎对 TCP 套接字而言没有什么作用。因为对于一个 TCP 套接字来说,SIGIO 信号发生的几率太高了,所以 SIGIO 信号并不能告诉我们究竟发生了什么事情。
(1)在 TCP 连接中, SIGIO 信号将会在这个时候产生:
(2)在一个监听某个端口的套接字上成功的建立了一个新连接。
(3)一个断线的请求被成功的初始化。
(4)一个断线的请求成功的结束。
(5)套接字的某一个通道(发送通道或是接收通道)被关闭。
(6)套接字接收到新数据。
(7)套接字将数据发送出去。
(8)发生了一个异步 I/O 的错误。
二、设置套接字工作于信号驱动I/O模式
为了让套接字描述符可以工作于信号驱动I/O模式,应用进程必须完成如下三步设置:
(1)注册SIGIO信号处理程序
(2)使用fcntl的F_SETOWN命令,设置套接字所有者为当前进程
(3)使用fcntld的F_SETFL命令,置O_ASYNC标志,允许套接字使用信号驱动I/O。
注意:必须保证在设置套接字所有者之前,向系统注册信号处理程序,否则就有可能在fcntl调用后,信号处理程序注册前内核向应用交付SIGIO信号,导致应用丢失此信号,下面的程序片段描述了怎么样为套接字设置信号驱动I/O。
void do_sigio(int sig)
{
//SIGIO处理程序代码
}
...
int sockfd;//套接字
struct sigaction sigio_action;
...
sigio_action.sa_handler=do_sigio;//信号处理程序
if(sigaction(SIGIO,&sigio_action,NULL)==-1)
{
//出错
}
//设置套接字所有者为当前进程
if((flags=fcntl(sockfd,F_SETOWN,getpid()))<0)
{
//出错
}
//获取当前套接字的flags
if((flags=fcntl(sockfd,F_GETFL,0))<0)
{
//出错
}
//设置信号驱动和非阻塞模式
//注意:在设置套接字工作于非驱动模式时,要先取得原有状态标志,然后通过逻辑或来增加新的标志,\
不能直接设置套接字为信号驱动和非阻塞模式,这样会清除套接字的原有其它工作模式
flags|=O_ASYNC|O_NONBLOCK;
if (fcntl(s, F_SETFL,flags)<0)
{
//出错
}
使用信号驱动模式重写了前面的时间服务器程序,在信号处理程序中读取套接字接收到的UDP数据报,然后将数据存入一个消息队列,再由应用的主循环从队列中取出数据报进行处理。
-
/*
UDP服务器
说明:用于在sigio信号处理程序中接收来自数据报客户端发来的数据报,接到的数据报存放
在一个队列中,随后程序主循环将从此队列中读取数据并进行处理。
用法:./server ip portnumber
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#define QUESIZE 16 //队列大小
#define BUFSIZE 1024
struct request
{
char *reqstr; //接收缓存字符串
size_t reqlen; //字符串长度
struct sockaddr_in *peer; //客户端地址
socklen_t sklen;
};
static void bail(const char *on_what)
{
fputs(strerror(errno),stderr);
fputs(": ",stderr);
fputs(on_what, stderr);
fputc('\n',stderr);
exit(1);