linux信号驱动i o,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数据报,然后将数据存入一个消息队列,再由应用的主循环从队列中取出数据报进行处理。

/*

UDP服务器

说明:用于在sigio信号处理程序中接收来自数据报客户端发来的数据报,接到的数据报存放

在一个队列中,随后程序主循环将从此队列中读取数据并进行处理。

用法:./server ip portnumber

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define QUESIZE 16 //队列大小

#define BUFSIZE 1024

struct request

{

char *reqstr;     //接收缓存字符串

size_t reqlen;    //字符串长度

struct sockaddr_in *peer;  //客户端地址

socklen_t sklen;

};

staticvoidbail(constchar*on_what)

{

fputs(strerror(errno),stderr);

fputs(": ",stderr);

fputs(on_what, stderr);

fputc('\n',stderr);

exit(1);

}

static struct request req_queue[QUESIZE]; //用户空间的缓存队列

static int idx_in;

static int idx_out;

static int nqueue;

int s;

struct sockaddr_in peer_addr;//服务器套接字

static socklen_t socklen=sizeof(peer_addr);

//sigio信号处理函数

void do_sigio(int signum)

{

long z;

struct request *p_req;

for (; ; )

{

//保存下一个客户端请求数据报的位置索引

p_req=&req_queue[idx_in];

if (nqueue>=QUESIZE)

{

write(STDOUT_FILENO,"request queue is full!\n",23);

return;

}

z=recvfrom(s, p_req->reqstr, BUFSIZE, 0, (struct sockaddr*)p_req->peer, &socklen);

if (z<0)

{

//设置服务器套接字工作于非阻塞模式

if(errno==EWOULDBLOCK)

break;

else

{

write(STDOUT_FILENO, "recvfrom error!\n", 16);

exit(1);

}

}

p_req->reqstr[z]=0;

p_req->reqlen=z;

nqueue++;

if (++idx_in>=QUESIZE)

idx_in=0;

}

}

//初始化队列

void init_queue()

{

for (int i=0; i

{

if ((req_queue[i].reqstr=(char *)malloc(BUFSIZE))==NULL)

bail("init_queue");

if((req_queue[i].peer=(struct sockaddr_in *)malloc(sizeof(sockaddr_in)))==NULL)

bail("init_queue");

req_queue[i].sklen=socklen;

}

idx_in=idx_out=nqueue=0;

}

//注册sigio信号处理程序

static void install_sigio()

{

struct sigaction sigio_action;

memset(&sigio_action, 0, sizeof(sigio_action));

sigio_action.sa_flags=0;

sigio_action.sa_handler=do_sigio;

if (sigaction(SIGIO, &sigio_action, NULL)==-1)

perror("failed to set SIGIO");

}

//设置套接字为信号驱动I/O和非阻塞模式

void set_sockopt(int s,int flags)

{

fcntl(s, F_SETOWN,getpid());

if ((flags=fcntl(s, F_GETFL,0))<0)

bail("F_GETFL error");

flags|=O_ASYNC|O_NONBLOCK;

if (fcntl(s, F_SETFL,flags)<0)

bail("F_SETFL error");

}

int main(int argc,char **argv)

{

long z;

char *srvr_addr=NULL;

int len_inet;

int portnumber;

int flags;

struct sockaddr_in srvaddr;//服务器地址

char dtfmt[BUFSIZE]; //日期-时间结果

time_t td; //当前时间和日期

struct tm tv; // 日期时间结构体

sigset_t zeromask,newmask,oldmask;

struct request *p_req;

/*

若命令行提供了作为服务器地址和端口的参数,则使用参数作为地址和端口,否则使用默认的地址和端口

*/

if (argc>2)

{

srvr_addr=argv[1];

if ((portnumber=atoi(argv[2]))<0)

{

fprintf(stderr, "port error");

exit(1);

}

}

else

{

srvr_addr="0";

portnumber=9000;

}

//创建数据报套接字

s=socket(AF_INET,SOCK_DGRAM,0);

if (s==-1)

bail("socket()");

init_queue();//初始化应用数据报接收队列

install_sigio();//注册sigio信号处理程序

set_sockopt(s, flags);//设置非阻塞和sigio驱动I/O模式

//初始化套接字地址

memset(&srvaddr, 0, sizeof(srvaddr));

srvaddr.sin_family=PF_INET;

srvaddr.sin_port=htons(portnumber);

if (!inet_aton(srvr_addr, &srvaddr.sin_addr))

bail("bad address.");

len_inet=sizeof(srvaddr);

//绑定套接字到指定地址和端口,于是客户端可以连接到该服务器

z=bind(s, (struct sockaddr*)&srvaddr, len_inet);

if (z==-1)

bail("bind()");

sigemptyset(&zeromask);

sigemptyset(&newmask);

sigemptyset(&oldmask);

sigaddset(&newmask, SIGIO);

//  sigprocmask(SIG_BLOCK, &newmask, &oldmask);

for (; ; )

{

while (nqueue==0)

sigsuspend(&zeromask); //挂起进程,直到被任何信号唤醒

if (idx_out>QUESIZE)

idx_out=0;

p_req=&req_queue[idx_out++];

time(&td);

tv=*localtime(&td);

strftime(dtfmt, sizeof(dtfmt), p_req->reqstr, &tv);

z=sendto(s, dtfmt, strlen(dtfmt), 0, (struct sockaddr*)p_req->peer, p_req->sklen);

if (z<0)

bail("sendto()");

//更新临界变量,必须阻塞sigio信号

sigprocmask(SIG_BLOCK, &newmask, &oldmask);

nqueue--;

sigprocmask(SIG_SETMASK, &oldmask, NULL);

}

return 0;

}

/*

UDP客户端

用法:./client ip port

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFSIZE 1024

staticvoidbail(constchar*on_what)

{

fputs(strerror(errno),stderr);

fputs(": ",stderr);

fputs(on_what, stderr);

fputc('\n',stderr);

exit(1);

}

int main(int argc,char **argv)

{

long z;

int sockfd;

char *srv_addr=NULL;

struct sockaddr_in server_addr;//服务器地址

struct sockaddr_in _addr;

int portnumber;

char dgram[BUFSIZE];

//从命令行获取服务器地址字符串

srv_addr=argv[1];

if((portnumber=atoi(argv[2]))<0)

{

fprintf(stderr,"error");

exit(1);

}

//创建服务器地址

memset(&server_addr, 0, sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);

if (!inet_aton(srv_addr, &server_addr.sin_addr))

bail("bad address");

//创建UDP套接字

sockfd=socket(AF_INET,SOCK_DGRAM, 0);

if (sockfd==-1)

bail("socket()");

for (; ; )

{

fputs("\nEnter format string:",stdout);

if (!fgets(dgram,sizeof(dgram), stdin))

break;

z=strlen(dgram);

if (z>0 && dgram[--z]=='\n')

dgram[z]=0;  //添加NULL结束标记

if(z==0)

continue;

//发送请求字符串

z=sendto(sockfd, dgram, strlen(dgram), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));

if (z<0)

bail("sendto()");

printf("send over...ans dgram=%s\n",dgram);

//如果输入了退出命令‘quit’

if (!strcasecmp(dgram,"QUIT"))

break;

//等待服务器应答

socklen_t size=sizeof(_addr);

z=recvfrom(sockfd, dgram, sizeof(dgram), 0, (struct sockaddr*)&_addr, &size);

if (z<0)

bail("recvfrom()");

dgram[z]=0;

printf("result from %s port %u:\n\t '%s'\n",inet_ntoa(_addr.sin_addr),ntohs(_addr.sin_port),dgram);

}

printf("\n exits from loop.\n");

close(sockfd);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值