Linux网络编程---信号驱动I/O

实现一个基本的流式套接字客户端/服务器通信程序,客户端和服务器按如下步骤交互:
(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数据报,然后将数据存入一个消息队列,再由应用的主循环从队列中取出数据报进行处理。


  1.  
  2. /*

    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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值