【编程】信号驱动式I/O

信号驱动式的I/O对于TCP套接字近乎无用。问题在于该信号产生的太频繁,并且它的出现没有告诉我们发生了什么事情。
在UDP上使用信号驱动式I/O是简答的。SIGIO信号发生在以下情况:
数据报到时套接字
套接字上发生异步错误

signal_io.c
#include "unp.h"

static int sockfd;
#define QSIZE 8
#define MAXDG 4096

typedef struct{
    void *dg_data;
    size_t dg_len;
    struct sockaddr *dg_sa;
    socklen_t dg_salen;
}DG;
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);

/*
    UDP 使用信号驱动是I/O,SIGIO信号在发生以下时间产生:
    1.数据报到达套接字;
    2.套接字上发生异步错误;( 发生异步错误的前提是UCP套接字已连接 )
*/
static void sig_io (int signo)
{
    ssize_t len;
    int nread;
    DG *ptr;

    printf("sig io\n");
    for (nread = 0; ;)
    {
        if (nqueue >= QSIZE){
            printf("receive overflow\n");
            return ;
        }

        ptr = &dg[iput];
        ptr->dg_salen = clilen;
        len = recvfrom(sockfd, ptr->dg_data, MAXDG, 0, ptr->dg_sa, &ptr->dg_salen);
        if (len < 0)
        {
            if (errno==EWOULDBLOCK)
                break; /*all done ;no more queued to read*/
            else
                perror("recvfrom");
        }

        ptr->dg_len = len;
        nread++;
        nqueue++;
        if (++iput >= QSIZE)
            iput = 0;

    }
    cntread[nread]++; /*histogram of # datagram read per signal*/
}

static void sig_hup(int signo)
{
    int i;
    printf("signal hup\n");
    for (i = 0; i <= QSIZE; i++)
        printf("cntread[%d] = %ld\n", i, cntread[i]);
}

void dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg)
{
    int i;
    const int on = 1;
    sigset_t zeromask, newmask, oldmask;

    sockfd = sockfd_arg;
    clilen = clilen_arg;

    for (i = 0; i < QSIZE; i++){    /*init queue of buffers*/
        dg[i].dg_data = 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);
    ioctl(sockfd, FIONBIO, &on);

    sigemptyset(&zeromask); /*init three signal sets*/
    sigemptyset(&oldmask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGIO);/*Signal we want to block*/

    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    printf("enter for\n");
    for (;;)
    {
        while (nqueue == 0)
        {
            sigsuspend(&zeromask); /*wait for datagram to process*/
        }
        /*unblck sigio*/
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0, dg[iget].dg_sa, dg[iget].dg_salen);
        if (++iget >= QSIZE)
            iget = 0;

        sigprocmask(SIG_BLOCK, &newmask, &oldmask);
        nqueue--;
    }
}

int main()
{
    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_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    bind(sockfd, (SA *)&servaddr, sizeof(servaddr));
    dg_echo(sockfd, (SA *)&cliaddr, sizeof(cliaddr));

    return 0;
}
signal_cli.c 用于测试的客户端程序
#include "unp.h"

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

int main (int argc, char *argv[])
{

    int sockfd;
    struct sockaddr_in servaddr;

    if (argc != 2)
    {
        printf("usage:udpcli <IPaddress>\n");
        return 0;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    dg_cli(stdin, sockfd, (SA*)&servaddr, sizeof(servaddr));

    return 0;
}
运行结果:

这里写图片描述

附:unp.h
    /* ************************************************************************
 *       Filename:  unp.h
 *    Description:  
 *        Version:  1.0
 *        Created:  2018年08月15日 02时44分11秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (), 
 *        Company:  
 * ************************************************************************/



/* include unph */
/* Our own header.  Tabs are set for 4 spaces, not 8 */

#ifndef __unp_h
#define __unp_h

//#include  "../config.h"   /* configuration options for current OS */
                            /* "../config.h" is generated by configure */

/* If anything changes in the following list of #includes, must change
   acsite.m4 also, for configure's tests. */

#include    <sys/types.h>   /* basic system data types */
#include    <sys/socket.h>  /* basic socket definitions */
#if TIME_WITH_SYS_TIME
#include    <sys/time.h>    /* timeval{} for select() */
#include    <time.h>        /* timespec{} for pselect() */
#else
#if HAVE_SYS_TIME_H
#include    <sys/time.h>    /* includes <time.h> unsafely */
#else
#include    <time.h>        /* old system? */
#endif
#endif
#include    <netinet/in.h>  /* sockaddr_in{} and other Internet defns */
#include    <arpa/inet.h>   /* inet(3) functions */
#include    <errno.h>
#include    <fcntl.h>       /* for nonblocking */
#include    <netdb.h>
#include    <signal.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <sys/stat.h>    /* for S_xxx file mode constants */
#include    <sys/uio.h>     /* for iovec{} and readv/writev */
#include    <unistd.h>
#include    <sys/wait.h>
#include    <sys/un.h>      /* for Unix domain sockets */

#ifdef  HAVE_SYS_SELECT_H
# include   <sys/select.h>  /* for convenience */
#endif

#ifdef  HAVE_SYS_SYSCTL_H
#ifdef  HAVE_SYS_PARAM_H
# include   <sys/param.h>   /* OpenBSD prereq for sysctl.h */
#endif
# include   <sys/sysctl.h>
#endif

#ifdef  HAVE_POLL_H
# include   <poll.h>        /* for convenience */
#endif

#ifdef  HAVE_SYS_EVENT_H
# include   <sys/event.h>   /* for kqueue */
#endif

#ifdef  HAVE_STRINGS_H
# include   <strings.h>     /* for convenience */
#endif

/* Three headers are normally needed for socket/file ioctl's:
 * <sys/ioctl.h>, <sys/filio.h>, and <sys/sockio.h>.
 */
//#ifdef    HAVE_SYS_IOCTL_H
# include   <sys/ioctl.h>
//#endif
#ifdef  HAVE_SYS_FILIO_H
# include   <sys/filio.h>
#endif
#ifdef  HAVE_SYS_SOCKIO_H
# include   <sys/sockio.h>
#endif

#ifdef  HAVE_PTHREAD_H
# include   <pthread.h>
#endif

#ifdef HAVE_NET_IF_DL_H
# include   <net/if_dl.h>
#endif

#ifdef HAVE_NETINET_SCTP_H
#include    <netinet/sctp.h>
#endif

#include <time.h>


#define LISTENQ     1024    /* 2nd argument to listen() */

/* Miscellaneous constants */
#define MAXLINE     4096    /* max text line length */
#define BUFFSIZE    8192    /* buffer size for reads and writes */

/* Define some port number that can be used for our examples */
#define SERV_PORT        9877           /* TCP and UDP */
#define SERV_PORT_STR   "9877"          /* TCP and UDP */
/* Following shortens all the typecasts of pointer arguments: */
#define SA  struct sockaddr

#define min(a,b)    ((a) < (b) ? (a) : (b))
#define max(a,b)    ((a) > (b) ? (a) : (b))

void Listen(int fd, int backlog);

#ifdef  HAVE_POLL
int Poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
#endif
ssize_t Recv(int fd, void *ptr, size_t nbytes, int flags);
ssize_t Recvfrom(int fd, void *ptr, size_t nbytes, int flags,
         struct sockaddr *sa, socklen_t *salenptr);
ssize_t Recvmsg(int fd, struct msghdr *msg, int flags);
int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       struct timeval *timeout);
void Send(int fd, const void *ptr, size_t nbytes, int flags);

void Sendto(int fd, const void *ptr, size_t nbytes, int flags,
       const struct sockaddr *sa, socklen_t salen);

void
Sendmsg(int fd, const struct msghdr *msg, int flags);

void
Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen);

void
Shutdown(int fd, int how);

int
Sockatmark(int fd);

int
Socket(int family, int type, int protocol);

int
Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);

const char *
Inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
ssize_t writen(int fd, const void *vptr, size_t n);
void
Writen(int fd, void *ptr, size_t nbytes);
void Close(int fd);
void
Bind(int fd, const struct sockaddr *sa, socklen_t salen);
pid_t Fork(void);

void
Connect(int fd, const struct sockaddr *sa, socklen_t salen);

void
Inet_pton(int family, const char *strptr, void *addrptr);

void
str_cli(FILE *fp, int sockfd);

void
Fclose(FILE *fp);
FILE *
Fdopen(int fd, const char *type);
char *
Fgets(char *ptr, int n, FILE *stream);
FILE *
Fopen(const char *filename, const char *mode);
void
Fputs(const char *ptr, FILE *stream);

ssize_t
Readline(int fd, void *ptr, size_t maxlen);
void
Write(int fd, void *ptr, size_t nbytes);

ssize_t
Read(int fd, void *ptr, size_t nbytes);



#endif  /* __unp_h */

猜你喜欢

转载自blog.csdn.net/u012335044/article/details/81704075