加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

套接字 – 在阻塞管道或套接字上是否可以使用select()非阻塞writ

发布时间:2020-12-15 21:00:57 所属栏目:安全 来源:网络整理
导读:情况是我有一个阻塞管道或套接字fd我想写()没有阻塞,所以我先做一个select(),但仍然不能保证write()不会阻塞. 这是我收集的数据.即使select()表示 写入是可能的,写入超过PIPE_BUF字节可以阻止. 但是,写入最多PIPE_BUF字节似乎并未阻止 练习,但POSIX spec没有
情况是我有一个阻塞管道或套接字fd我想写()没有阻塞,所以我先做一个select(),但仍然不能保证write()不会阻塞.

这是我收集的数据.即使select()表示
写入是可能的,写入超过PIPE_BUF字节可以阻止.
但是,写入最多PIPE_BUF字节似乎并未阻止
练习,但POSIX spec没有强制要求.

这只是指定原子行为. Python(!) documentation指出:

Files reported as ready for writing by select(),poll() or similar
interfaces in this module are guaranteed to not block on a write of up
to PIPE_BUF bytes. This value is guaranteed by POSIX to be at least
512.

在以下测试程序中,将BUF_BYTES设置为100000以阻止
成功选择后,在Linux,FreeBSD或Solaris上写().一世
假设命名管道具有与匿名管道类似的行为.

不幸的是,阻塞套接字会发生同样的情况.呼叫
main()中的test_socket()并使用较大的BUF_BYTES(100000是好的
这里也).目前还不清楚是否有安全的缓冲区大小
PIPE_BUF用于套接字.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>

#define BUF_BYTES PIPE_BUF
char buf[BUF_BYTES];

int
probe_with_select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds)
{
    struct timeval timeout = {0,0};
    int n_found = select(nfds,readfds,writefds,exceptfds,&timeout);
    if (n_found == -1) {
        perror("select");
    }
    return n_found;
}

void
check_if_readable(int fd)
{
    fd_set fdset;
    FD_ZERO(&fdset);
    FD_SET(fd,&fdset);
    printf("select() for read on fd %d returned %dn",fd,probe_with_select(fd + 1,&fdset,0));
}

void
check_if_writable(int fd)
{
    fd_set fdset;
    FD_ZERO(&fdset);
    FD_SET(fd,&fdset);
    int n_found = probe_with_select(fd + 1,0);
    printf("select() for write on fd %d returned %dn",n_found);
    /* if (n_found == 0) { */
    /*     printf("sleepingn"); */
    /*     sleep(2); */
    /*     int n_found = probe_with_select(fd + 1,0); */
    /*     printf("retried select() for write on fd %d returned %dn",*/
    /*            fd,n_found); */
    /* } */
}

void
test_pipe(void)
{
    int pipe_fds[2];
    size_t written;
    int i;
    if (pipe(pipe_fds)) {
        perror("pipe failed");
        _exit(1);
    }
    printf("read side pipe fd: %dn",pipe_fds[0]);
    printf("write side pipe fd: %dn",pipe_fds[1]);
    for (i = 0; ; i++) {
        printf("i = %dn",i);
        check_if_readable(pipe_fds[0]);
        check_if_writable(pipe_fds[1]);
        written = write(pipe_fds[1],buf,BUF_BYTES);
        if (written == -1) {
            perror("write");
            _exit(-1);
        }
        printf("written %d bytesn",written);
    }
}

void
serve()
{
    int listenfd = 0,connfd = 0;
    struct sockaddr_in serv_addr;

    listenfd = socket(AF_INET,SOCK_STREAM,0);
    memset(&serv_addr,'0',sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000);

    bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));

    listen(listenfd,10);

    connfd = accept(listenfd,(struct sockaddr*)NULL,NULL);

    sleep(10);
}

int
connect_to_server()
{
    int sockfd = 0,n = 0;
    struct sockaddr_in serv_addr;

    if((sockfd = socket(AF_INET,0)) < 0) {
        perror("socket");
        exit(-1);
    }

    memset(&serv_addr,sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000);

    if(inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr) <= 0) {
        perror("inet_pton");
        exit(-1);
    }

    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) {
        perror("connect");
        exit(-1);
    }

    return sockfd;
}

void
test_socket(void)
{
    if (fork() == 0)  {
        serve();
    } else {
        int fd;
        int i;
        int written;
        sleep(1);
        fd = connect_to_server();

        for (i = 0; ; i++) {
            printf("i = %dn",i);
            check_if_readable(fd);
            check_if_writable(fd);
            written = write(fd,BUF_BYTES);
            if (written == -1) {
                perror("write");
                _exit(-1);
            }
            printf("written %d bytesn",written);
        }
    }
}

int
main(void)
{
    test_pipe();
    /* test_socket(); */
}

解决方法

除非你希望每当select()表示fd已准备好写入时一次发送一个字节,否则实际上没有办法知道你能发送多少,即使理论上它也是可能的(至少在文档中),如果不是在现实世界中)选择说它已准备好写入,然后条件在select()和write()之间改变.

非阻塞发送是此处的解决方案,如果您从使用write()更改为send(),则无需将文件描述符更改为非阻塞模式即可以非阻塞形式发送一条消息.您需要更改的唯一方法是将MSG_DONTWAIT标志添加到发送调用,这将使发送非阻塞而不更改套接字的属性.在这种情况下,您甚至根本不需要使用select(),因为send()调用将在返回代码中为您提供所需的所有信息 – 如果返回的代码为-1且errno为EAGAIN或EWOULDBLOCK然后你知道你不能再发送了.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读