使用SIGURG信号发送接收带外数据

本文介绍在Linux环境下,内核通知应用程序带外数据到达的两种方法,即IO复用技术(如select系统调用)和SIGURG信号。还提及服务端、客户端发送带外数据及普通数据的情况,以及使用系统调用发送、接收和检测带外数据等内容。

在Linux环境下,内核通知应用程序带外数据到达主要有两种方法:

  1. IO复用技术,select等系统调用在接收到带外数据时将返回,并向应用程序报告socket上的异常事件。
  2. 使用SIGURG信号

服务端:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <libgen.h>

#define BUF_SIZE            1024
#define BACKLOG             5

static int connfd;
// SIGURG信号的处理函数
void sig_urg(int sig) {
    int save_errno = errno;
    char buffer[BUF_SIZE];
    memset(buffer, '\0', BUF_SIZE);
    int ret = recv(connfd, buffer, BUF_SIZE - 1, MSG_OOB);
    printf("got %d bytes of oob data '%s'\n", ret, buffer);
    errno = save_errno;
}

void addsig(int sig, void (*sig_handler)(int)) {
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

int main(int argc, char*argv[]) {
        if (argc <= 2) {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(sock, BACKLOG);
    assert(ret != -1);

    struct sockaddr_in client;
    socklen_t client_addlen = sizeof(client);
    connfd = accept(sock, (struct sockaddr*)&client, &client_addlen);
    if (connfd < 0) {
        printf("errno is %d\n",errno);
    }
    else {
        addsig(SIGURG, sig_urg);
        // 使用SIGURG信号之前,我们必须设置socket的宿主进程或进程组
        fcntl(connfd, F_SETOWN, getpid());

        char buffer[BUF_SIZE];
        while (1)
        {
            // 循环接收普通数据
            memset(buffer, '\0', BUF_SIZE);
            ret = recv(connfd, buffer, BUF_SIZE - 1, 0);
            if (ret <= 0) {
                printf("ret <= 0\n");
                break;
            }
            printf("got %d bytes of normal data '%s'\n", ret, buffer);
        }
        close(connfd);
    }
    close(sock);
    return 0;
}

客户端发送带外数据、普通数据:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    if (argc <= 2) {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server_address;
    bzero(&server_address, sizeof(struct sockaddr_in));
    server_address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &server_address.sin_addr);
    server_address.sin_port = htons(port);

    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    assert( sockfd >= 0);
    if (connect(sockfd, (struct sockaddr*)&server_address, sizeof(struct sockaddr_in)) < 0) {
        printf("connection failed\n");
    } else {
        const char* oob_data = "abc";
        const char* normal_data = "123";
        send(sockfd, normal_data, strlen(normal_data), 0);
        send(sockfd, oob_data, strlen(oob_data), MSG_OOB);
        send(sockfd, normal_data, strlen(normal_data), 0);
    }
    
    close(sockfd);
    return 0;
}

在这里插入图片描述

带外数据

  • 使用带有MSG_OOB标志的send / recv系统调用来发送 / 接收带外数据
  • 使用IO复用系统调用或SIGURG信号检测带外数据是否到达;
  • 使用int sockatmark(int sockfd)系统调用判断一个socket是否处于带外标记,即下一个被读取到的数据是否是带外数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值