Unix 网络学习之信号驱动IO
信号驱动IO是指:进程预先告知内核,使得 当某个socketfd有events(事件)发生时,内核使用信号通知相关进程。
在UDP上,SIGIO信号会在下面两个事件的时候产生:
1 数据报到达套接字
2 套接字上发上一部错误
因此我们很容易判断SIGIO出现的时候,如果不是发生错误,那么就是有数据报到达了。
而在TCP上,由于TCP是双工的,它的信号产生过于平凡,并且信号的出现几乎没有告诉我们发生了什么事情。因此对于TCP套接字,SIGIO信号是没有什么使用的。
针对一个套接口使用信号驱动i/o(SIGIO)要求进程执行以下3个步骤
1.建立SIGIO信号的信号处理
2.设置该套接口的属主,通常通过fcntl 的F_SETOWN 命令设置
3.开启该套接口的信号驱动i/o,通常通过使用fcntl法人F_SETFL命令打开O命令打开O_ASYNC标志完成
UDP 服务端代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SER_PORT 5000
#define QSIZE 8
#define MAXDG 4096
typedef struct sockaddr SA;
typedef struct
{
void *dgdata;
int dg_len;
struct sockaddr *dg_sa;
socklen_t dg_salen;
}DG;
static int sockfd;
static DG dg[QSIZE];
static long cntread[QSIZE+1];
static int iget;
static int iput;
static int nqueue;
static socklen_t clilen;
static void sig_io(int );
static void sig_hup(int );
void dg_echo(int sockfd_arg,const SA* pcliaddr,socklen_t clilent_arg);
int main(int argc ,char **argv[])
{
int sockfd;
struct sockaddr_in servaddr,cliaddr;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SER_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd,(SA* )&servaddr,sizeof(servaddr));
dg_echo(sockfd,(SA*)&servaddr,sizeof(servaddr));
}
void dg_echo(int sockfd_arg,const SA* pcliaddr,socklen_t clilent_arg)
{
int i;
const int on = 1;
sigset_t zeromask,newmask,oldmask;
sockfd = sockfd_arg;
clilen = clilent_arg;
for(i = 0;i < QSIZE; i++ )
{
dg[i].dgdata = malloc(MAXDG);
dg[i].dg_sa =malloc(clilen);
dg[i].dg_salen = clilen;
}
iget = iput = nqueue = 0;
signal(SIGHUP,sig_hup);
signal(SIGIO,sig_io);
fcntl(sockfd,F_SETOWN,getpid()); // 设置套接口的属主
ioctl(sockfd,FIOASYNC,&on); //设置信号驱动i/o
ioctl(sockfd,FIONBIO,&on); // 设置非阻塞i/o
sigemptyset(&zeromask);
sigemptyset(&oldmask);
sigemptyset(&newmask);
sigaddset(&newmask,SIGIO);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
for(;;)
{
while(nqueue == 0)
sigsuspend(&zeromask);
sigprocmask(SIG_SETMASK,&oldmask,NULL);
sendto(sockfd,dg[iget].dgdata,dg[iget].dg_len,0,dg[iget].dg_sa,dg[iget].dg_salen);
if(++iget >= QSIZE)
iget = 0;
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
nqueue--;
}
}
static void sig_io(int signo)
{
ssize_t len;
int nread;
DG *ptr;
for(nread =0;;)
{
if(nqueue >= QSIZE)
{
printf("*****************************recvive overflow");
}
ptr = &dg[iput];
ptr->dg_salen = clilen;
len = recvfrom(sockfd,ptr->dgdata,MAXDG,0,ptr->dg_sa,&ptr->dg_salen);
if(len < 0)
{
if(errno = EWOULDBLOCK)
break;
else
printf("recvfrom error");
}
ptr->dg_len = len;
nread++;
nqueue++;
if(++iput >= QSIZE)
iput = 0;
}
cntread[nread]++;
}
static void sig_hup(int sino)
{
int i;
for(i = 0; i <= QSIZE;i++)
{
printf("nread[%d] = %ld \n",i,cntread[i]);
}
}
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct sockaddr SA;
#define MAXLINE 1024
#define SER_PORT 5000
void dg_cli(FILE *fp,int sockfd,const SA* pservaddr,socklen_t servlen);
int main(int argc ,char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
{
printf("usage :udpcli ");
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SER_PORT);
inet_pton(AF_INET,argv[1],(void *)&servaddr.sin_addr);
sockfd = socket(AF_INET,SOCK_DGRAM,0);
dg_cli(stdin,sockfd,(SA*)&servaddr,sizeof(servaddr));
exit(0);
}
void dg_cli(FILE *fp,int sockfd,const SA* pservaddr,socklen_t servlen)
{
int n;
char sendline[MAXLINE],recvline[MAXLINE+1];
while(fgets(sendline,MAXLINE,fp) != NULL)
{
sendto(sockfd,sendline,strlen(sendline),0,pservaddr,servlen);
n = recvfrom(sockfd,recvline,MAXLINE,0,NULL,NULL);
recvline[n]=0;
fputs(recvline,stdout);
}
}
阅读(2628) | 评论(0) | 转发(0) |