【linux高级程序设计】(第十五章)UDP网络编程应用 4

socket信号驱动

为了使一个套接字能够使用信号驱动I/O,至少需要以下3步操作。

  • 1.安装SIGIO信号
  • 2.套接字的拥有者设定为当前进程。因为SIGIO信号只会送到socket拥有者进程. 通过fcntl的F_SETOWN
  • 3.套接字必须被允许使用异步I/O。 通过fcntl的F_SETFL,设置为O_ASYNC

在UDP通信中,下面情况会产生SIGIO信号

在TCP通信中,下面情况会产生SIGIO信号

例子:

下面的代码好奇怪,说是UDP的,但是发送接收用的是send, recv 而且客户端还跟服务器连接了;说是TCP,但是socket建立的时候用的是SOCK_DGRAM.

而且代码是可以跑通的。

服务器:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

//针对SIGIO信号处理
static int nqueue = 0; 
void sigio_handler(int signum)
{
    if(signum == SIGIO)
        nqueue++;
    printf("signum = %d, nqueue = %d
", signum, nqueue); //打印信号值
    return;
}
static recv_buf[MAX_LENTH];

int main(int argc, char *argv[])
{
    int sockfd, on = 1;
    struct sigaction action;
    sigset_t newmask, oldmask;
    struct sockaddr_in ser_addr;
    if(argc != 3)
    {
        printf("use: %s ip_add port
", argv[0]);
        exit(EXIT_FAILURE);
    }
    
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;  //使用IPv4
    ser_addr.sin_port = htons(atoi(argv[2]));
    if(inet_aton(argv[1], (struct in_addr *)&ser_addr.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    //创建socket
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    //绑定IP地址
    if(bind(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    memset(&action, 0, sizeof(action));
    action.sa_handler = sigio_handler;
    action.sa_flags = 0;
    //安装信号
    sigaction(SIGIO, &action, NULL);
    //设置socket拥有者
    if(fcntl(sockfd, F_SETOWN, getpid()) == -1)
    {
        perror("fcntl F_SETOWN");
        exit(EXIT_FAILURE);
    }
    //设置socket为信号驱动型
    if(ioctl(sockfd, FIOASYNC, &on) == -1)
    {
        perror("ioctl FIOASYNC");
        exit(EXIT_FAILURE);
    }
    sigemptyset(&oldmask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGIO);
    printf("get ready
");
    while(1)
    {
        int len;
        //设置当前阻塞的信号
        sigprocmask(SIG_BLOCK, &newmask, &oldmask);
        //等待信号
        while(nqueue == 0)
            sigsuspend(&oldmask);
        memset(recv_buf, '', MAX_LENTH);
        //非阻塞接收数据
        len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
        if(len == -1 && errno == EAGAIN)
            nqueue = 0;
        //修改进程阻塞的信号
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        if(len >= 0)
            printf("recv %d byte, msg is %s
", len, recv_buf);
    }
    
}

客户端

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

int main(int argc, char *argv[])
{
    struct sockaddr_in addr;
    int sock_fd, ret;
    char snd_buf[MAX_LENTH];
    if(argc != 3)  //参数需要服务器的IP和端口
    {
        printf("use: %s ip_add port
", argv[0]);
        exit(EXIT_FAILURE);
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    if(inet_aton(argv[1], (struct in_addr *)&addr.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    addr.sin_port = htons(atoi(argv[2]));
    //创建socket
    if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    //向服务器发起连接 ??这不是TCP的么
    if(ret = connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        perror("connect");
        exit(EXIT_FAILURE);
    }
    while(1)
    {
        printf("input msg to send:");
        memset(snd_buf, '', MAX_LENTH);
        fgets(snd_buf, MAX_LENTH - 1, stdin);
        write(sock_fd, snd_buf, MAX_LENTH - 1);
    }
}

原文地址:https://www.cnblogs.com/dplearning/p/4708853.html