套接字在写入操作时不阻塞:OpenSolaris

发布于 2024-12-01 13:47:08 字数 2539 浏览 2 评论 0原文

我有一个单元测试,用于检查阻塞和非阻塞套接字的行为 - 服务器写入一个很长的响应,在某些时候它不应该能够再写入,并且它 写入时阻塞。

基本上一侧写入,另一侧不读取。

在 Solaris 下,有时我会收到错误“空间不足”(写入 75MB 后),而不是写入时阻塞:

重现问题的程序:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>

char const *address = "127.0.0.1";
#define check(x) do { if( (x) < 0) { perror(#x) ; exit(1); } } while(0)

int main()
{
    signal(SIGPIPE,SIG_IGN);
    struct sockaddr_in inaddr = {};
    inaddr.sin_family = AF_INET;
    inaddr.sin_addr.s_addr = inet_addr(address);
    inaddr.sin_port = htons(8080);

    int res = fork();
    if(res < 0) {
        perror("fork");
        exit(1);
    }
    if(res > 0) {
        int fd = -1;
        int status;
        sleep(1);   
        check(fd = socket(AF_INET,SOCK_STREAM,0));
        check(connect(fd,(sockaddr*)&inaddr,sizeof(inaddr)));
        sleep(5);
        close(fd);

        wait(&status);
        return 0;
    }
    else {
        int acc,fd;
        check(acc = socket(AF_INET,SOCK_STREAM,0));
        int yes = 1;
        check(setsockopt(acc,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
        check(bind(acc,(sockaddr*)&inaddr,sizeof(inaddr)));
        check(listen(acc,10));
        check(fd = accept(acc,0,0));

        char buf[1000];
        long long total= 0;
        do {
            int r = send(fd,buf,sizeof(buf),0);
            if(r < 0) {
                printf("write %s\n",strerror(errno));
                return 0;
            }
            else if(r==0) {
                printf("Got eof\n");
                return 0;
            }
            total += r;
            if(total > 100*1024*1024) {
                printf("Too much!!!!\n");
                return 0;
            }
            printf("%lld\n",total);
        }while(1);
    }
    return 0;
}

Solaris 上的输出(最后两行)

75768000
write Not enough space

Linux 上的预期输出(最后两行)

271760
write Connection reset by peer

仅当另一端关闭套接字时才会发生这种情况。

有什么想法为什么以及如何修复它,要设置哪些选项?

PS:它是 OpenSolaris 2009.06,x86

编辑

  • 添加了重现问题的完整 C 代码

答案:

这似乎是特定版本的 Solaris 内核、libc 库中的错误。

I have a unit test that checks behavior on blocking and non-blocking sockets - the server writes a long response and at some point it should not be able to write any more and it
blocks on write.

Basically one side writes and other side does not reads.

Under Solaris at some point I get a error "Not enough space" (after writing 75MB) instead of blocking on write:

Program that reproduces the problem:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>

char const *address = "127.0.0.1";
#define check(x) do { if( (x) < 0) { perror(#x) ; exit(1); } } while(0)

int main()
{
    signal(SIGPIPE,SIG_IGN);
    struct sockaddr_in inaddr = {};
    inaddr.sin_family = AF_INET;
    inaddr.sin_addr.s_addr = inet_addr(address);
    inaddr.sin_port = htons(8080);

    int res = fork();
    if(res < 0) {
        perror("fork");
        exit(1);
    }
    if(res > 0) {
        int fd = -1;
        int status;
        sleep(1);   
        check(fd = socket(AF_INET,SOCK_STREAM,0));
        check(connect(fd,(sockaddr*)&inaddr,sizeof(inaddr)));
        sleep(5);
        close(fd);

        wait(&status);
        return 0;
    }
    else {
        int acc,fd;
        check(acc = socket(AF_INET,SOCK_STREAM,0));
        int yes = 1;
        check(setsockopt(acc,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
        check(bind(acc,(sockaddr*)&inaddr,sizeof(inaddr)));
        check(listen(acc,10));
        check(fd = accept(acc,0,0));

        char buf[1000];
        long long total= 0;
        do {
            int r = send(fd,buf,sizeof(buf),0);
            if(r < 0) {
                printf("write %s\n",strerror(errno));
                return 0;
            }
            else if(r==0) {
                printf("Got eof\n");
                return 0;
            }
            total += r;
            if(total > 100*1024*1024) {
                printf("Too much!!!!\n");
                return 0;
            }
            printf("%lld\n",total);
        }while(1);
    }
    return 0;
}

The output on Solaris (last two lines)

75768000
write Not enough space

The expected output on Linux (last two lines)

271760
write Connection reset by peer

Which happens only when the other side closes the socket.

Any ideas why and how can I fix it, what options to set?

P.S.: It is OpenSolaris 2009.06, x86

Edits

  • Added full C code that reproduces the problem

Answer:

This seems like a bug in specific version of Solaris kernel, libc library.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

就是爱搞怪 2024-12-08 13:47:08

从 OpenSolaris 源代码来看,恐怕不支持 SO_SNDTIMEO 选项: https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/uts/common/inet/sockmods/socksctp.c#l1233

From OpenSolaris source code, I'm afraid the SO_SNDTIMEO option is unsupported: https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/uts/common/inet/sockmods/socksctp.c#l1233

一抹苦笑 2024-12-08 13:47:08

如果您想在没有可用空间的情况下进行阻止,则需要编写代码来执行此操作。
POSIX 非常清楚,套接字上的 write 相当于不带选项的 send,并且 send "如果...系统中没有足够的资源来执行该操作,则可能会失败。"

If you want to block if there's no space available, you need to write code to do that.
POSIX is pretty clear that write on a socket is equivalent to send with no options, and that send "may fail if ... [i]nsufficient resources were available in the system to perform the operation."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文