简体   繁体   English

SOCK_SEQPACKET Unix套接字上的空数据包

[英]Empty packet on SOCK_SEQPACKET Unix Socket

I'm playing with SOCK_SEQPACKET type on Unix sockets. 我正在Unix套接字上使用SOCK_SEQPACKET类型。

The code I'm using for reading is the classic 我用来阅读的代码是经典的

ssize_t recv_size = recv(sd, buffer, sizeof(buffer), 0);
if (recv_size < 0) {
    handle_error("recv", errno);
} else if (recv_size > 0) {
    handle_packet(buffer, recv_size);
} else {
    // recv_size == 0 => peer closed socket.
    handle_end_of_stream();
}

While this works just fine, I noticed it is not able to distinguish between a socket closure and a message of size 0. In other words, if on the other end I issue a sequence of calls like this: 尽管这很好用,但我注意到它无法区分套接字关闭和大小为0的消息。换句话说,如果在另一端,我发出如下调用序列:

send(sd, "hello", strlen("hello"), 0);
send(sd, "", 0, 0);
send(sd, "world", strlen("world"), 0);

…the reader will only receive "hello" and react to the second message with a socket closure, missing the "world" message entirely. …读者将只收到"hello"并用套接字关闭对第二条消息做出反应,完全忽略了"world"消息。

I was wondering if there's some way to disambiguate between the two situations. 我想知道这两种情况之间是否存在某种歧义。

What if you make some sort of "confirmation" function on both ends. 如果您在两端都进行某种“确认”功能怎么办。 For example instead of handle_end_of_stream, make something like this: 例如,代替handle_end_of_stream,执行以下操作:

->send(xx, "UNIQUE_MESSAGE", strlen("UNIQUE_MESSAGE"), 0);

< you receive "UNIQUE_RESPONSE" if connection is still up, if you don't, you know for sure that the other end is closed. <如果连接仍在,您将收到“ UNIQUE_RESPONSE” ,否则,您将确定另一端已关闭。 Just filter out some sort of "UNIQUE_MESSAGE" and "UNIQUE_RESPONSE" inf your "confirmation" function. 只需在“确认”功能中过滤掉某种“ UNIQUE_MESSAGE”和“ UNIQUE_RESPONSE”即可。

As I mentioned in a comment, zero-length seqpackets (as well as zero-length datagrams) can behave oddly, typically mistaken for disconnects; 正如我在评论中提到的那样,零长度的seqpackets(以及零长度的数据报)的行为可能很奇怪,通常会误认为是断开连接。 and for that reason, I definitely recommend against using zero-length seqpackets or datagrams for any purpose. 因此,我绝对建议不要出于任何目的使用零长度seqpackets或数据报。

To illustrate the main problem, and explore the details, I created two test programs. 为了说明主要问题并探索细节,我创建了两个测试程序。 First is receive.c , which listens on an Unix domain seqpacket socket, accepts one connection, and describes what it receives: 首先是receive.c ,它在Unix域seqpacket套接字上侦听,接受一个连接,并描述接收到的内容:

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    done = 1;
}

static int install_done(int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

static inline unsigned int digit(const int c)
{
    switch (c) {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'A': case 'a': return 10;
    case 'B': case 'b': return 11;
    case 'C': case 'c': return 12;
    case 'D': case 'd': return 13;
    case 'E': case 'e': return 14;
    case 'F': case 'f': return 15;
    default:  return 16;
    }
}

static inline unsigned int octbyte(const char *src)
{
    if (src) {
        const unsigned int  o0 = digit(src[0]);
        if (o0 < 4) {
            const unsigned int  o1 = digit(src[1]);
            if (o1 < 8) {
                const unsigned int  o2 = digit(src[2]);
                if (o2 < 8)
                    return o0*64 + o1*8 + o2;
            }
        }
    }
    return 256;
}

static inline unsigned int hexbyte(const char *src)
{
    if (src) {
        const unsigned int  hi = digit(src[0]);
        if (hi < 16) {
            const unsigned int  lo = digit(src[1]);
            if (lo < 16)
                return 16*hi + lo;
        }
    }
    return 256;
}

size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
    char         *dst = addr->sun_path;
    char *const   end = addr->sun_path + sizeof (addr->sun_path) - 1;
    unsigned int  byte;

    if (!src || !addr)
        return 0;

    memset(addr, 0, sizeof *addr);
    addr->sun_family = AF_UNIX;
    while (*src && dst < end)
        if (*src == '\\')
            switch (*(++src)) {
            case '0':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else {
                    *(dst++) = '\0';
                    src++;
                }
                break;
            case '1':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case '2':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case '3':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case 'x':
                byte = hexbyte(src + 1);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case 'a':  *(dst++) = '\a'; src++; break;
            case 'b':  *(dst++) = '\b'; src++; break;
            case 't':  *(dst++) = '\t'; src++; break;
            case 'n':  *(dst++) = '\n'; src++; break;
            case 'v':  *(dst++) = '\v'; src++; break;
            case 'f':  *(dst++) = '\f'; src++; break;
            case 'r':  *(dst++) = '\r'; src++; break;
            case '\\': *(dst++) = '\\'; src++; break;
            default:   *(dst++) = '\\';
            }
        else
            *(dst++) = *(src++);

    *(dst++) = '\0';

    return (size_t)(dst - (char *)addr);
}


int main(int argc, char *argv[])
{
    struct sockaddr_un  addr, conn;
    socklen_t           addrlen, connlen;
    int                 socketfd, connfd;

    if (argc != 2 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s SOCKET_PATH\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    if (install_done(SIGINT) ||
        install_done(SIGHUP) ||
        install_done(SIGTERM)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    socketfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    if (socketfd == -1) {
        fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    addrlen = set_unix_path(argv[1], &addr);
    if (bind(socketfd, (const struct sockaddr *)&addr, addrlen) == -1) {
        fprintf(stderr, "Cannot bind to %s: %s.\n", argv[1], strerror(errno));
        close(socketfd);
        return EXIT_FAILURE;
    }

    if (listen(socketfd, 1) == -1) {
        fprintf(stderr, "Cannot listen for incoming connections: %s.\n", strerror(errno));
        close(socketfd);
        return EXIT_FAILURE;
    }

    memset(&conn, 0, sizeof conn);
    connlen = sizeof conn;
    connfd = accept(socketfd, (struct sockaddr *)&conn, &connlen);
    if (connfd == -1) {
        close(socketfd);
        fprintf(stderr, "Canceled.\n");
        return EXIT_SUCCESS;
    }

    if (connlen > 0)
        fprintf(stderr, "Connected, peer address size is %d.\n", (int)connlen);
    else
        fprintf(stderr, "Connected; no peer address.\n");

    while (!done) {
        char     buffer[65536];
        ssize_t  n;
        int      r;

        n = recv(connfd, buffer, sizeof buffer, 0);
        if (n > 0)
            fprintf(stderr, "Received %zd bytes.\n", n);
        else
        if (n == 0) {
            struct pollfd  fds[1];

            fds[0].fd = connfd;
            fds[0].events = 0;
            fds[0].revents = 0;
            r = poll(fds, 1, 0);
            if (r > 0 && (fds[0].revents & POLLHUP)) {
                fprintf(stderr, "Disconnected (revents = %d).\n", fds[0].revents);
                break;
            } else
            if (r > 0)
                fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents); 
            else
            if (r == 0)
                fprintf(stderr, "Received a zero-byte seqpacket.\n");
            else
                fprintf(stderr, "recv() == 0, poll() == %d, revents == %d\n", r, fds[0].revents);
        }
    }

    close(connfd);
    close(socketfd);
    return EXIT_SUCCESS;
}

You can compile the above using eg gcc -Wall -O2 receive.c -o receive . 您可以使用例如gcc -Wall -O2 receive.c -o receive编译以上内容。 To run, give it the Unix domain address to listen on. 要运行,请给它提供Unix域名地址以进行监听。 In Linux, you can use the abstract namespace by prepending \\0 to the address; 在Linux中,可以通过在地址前面加上\\0来使用抽象名称空间; for example, by running ./receive '\\0example' . 例如,通过运行./receive '\\0example' Otherwise, the socket address will be visible in the filesystem, and you'll need to remove it (as if it was a file, using rm ) before running ./receive again with the same socket address. 否则,套接字地址将在文件系统中可见,并且您需要在使用相同的套接字地址再次运行./receive之前,将其删除(就好像它是一个文件,使用rm )。

We also need an utility to send seqpackets. 我们还需要一个实用程序来发送seqpackets。 The following send.c is very similar (reuses much of the same code). 下面的send.c非常相似(重复使用许多相同的代码)。 You specify the Unix domain address to connect to, and one or more seqpacket lengths. 您指定要连接的Unix域地址,以及一个或多个seqpacket长度。 You can also specify delays in milliseconds (just prepend a - ; ie, negative integers are delays in milliseconds): 您还可以指定延迟(以毫秒为单位)(只需在前面加上- ;即,负整数是延迟(以毫秒为单位)):

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <string.h>
#include <poll.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    done = 1;
}

static int install_done(int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

static inline unsigned int digit(const int c)
{
    switch (c) {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'A': case 'a': return 10;
    case 'B': case 'b': return 11;
    case 'C': case 'c': return 12;
    case 'D': case 'd': return 13;
    case 'E': case 'e': return 14;
    case 'F': case 'f': return 15;
    default:  return 16;
    }
}

static inline unsigned int octbyte(const char *src)
{
    if (src) {
        const unsigned int  o0 = digit(src[0]);
        if (o0 < 4) {
            const unsigned int  o1 = digit(src[1]);
            if (o1 < 8) {
                const unsigned int  o2 = digit(src[2]);
                if (o2 < 8)
                    return o0*64 + o1*8 + o2;
            }
        }
    }
    return 256;
}

static inline unsigned int hexbyte(const char *src)
{
    if (src) {
        const unsigned int  hi = digit(src[0]);
        if (hi < 16) {
            const unsigned int  lo = digit(src[1]);
            if (lo < 16)
                return 16*hi + lo;
        }
    }
    return 256;
}

size_t set_unix_path(const char *src, struct sockaddr_un *addr)
{
    char         *dst = addr->sun_path;
    char *const   end = addr->sun_path + sizeof (addr->sun_path) - 1;
    unsigned int  byte;

    if (!src || !addr)
        return 0;

    memset(addr, 0, sizeof *addr);
    addr->sun_family = AF_UNIX;
    while (*src && dst < end)
        if (*src == '\\')
            switch (*(++src)) {
            case '0':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else {
                    *(dst++) = '\0';
                    src++;
                }
                break;
            case '1':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case '2':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case '3':
                byte = octbyte(src);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case 'x':
                byte = hexbyte(src + 1);
                if (byte < 256) {
                    *(dst++) = byte;
                    src += 3;
                } else
                    *(dst++) = '\\';
                break;
            case 'a':  *(dst++) = '\a'; src++; break;
            case 'b':  *(dst++) = '\b'; src++; break;
            case 't':  *(dst++) = '\t'; src++; break;
            case 'n':  *(dst++) = '\n'; src++; break;
            case 'v':  *(dst++) = '\v'; src++; break;
            case 'f':  *(dst++) = '\f'; src++; break;
            case 'r':  *(dst++) = '\r'; src++; break;
            case '\\': *(dst++) = '\\'; src++; break;
            default:   *(dst++) = '\\';
            }
        else
            *(dst++) = *(src++);

    *(dst++) = '\0';

    return (size_t)(dst - (char *)addr);
}

static inline long sleep_ms(const long ms)
{
    struct timespec  t;

    if (ms > 0) {
        t.tv_sec = ms / 1000;
        t.tv_nsec = (ms % 1000) * 1000000;
        if (nanosleep(&t, &t) == -1 && errno == EINTR)
            return 1000 * (unsigned long)(t.tv_sec)
                 + (unsigned long)(t.tv_nsec / 1000000);
        return 0;
    } else
        return ms;
}

static int parse_long(const char *src, long *dst)
{
    const char *end = src;
    long        val;

    if (!src || !*src)
        return errno = EINVAL;

    errno = 0;
    val = strtol(src, (char **)&end, 0);
    if (errno)
        return errno;

    if (end == src)
        return errno = EINVAL;

    while (*end == '\t' || *end == '\n' || *end == '\v' ||
           *end == '\f' || *end == '\r' || *end == ' ')
        end++;

    if (*end)
        return errno = EINVAL;

    if (dst)
        *dst = val;

    return 0;
}

int main(int argc, char *argv[])
{
    char                buffer[65536];
    struct sockaddr_un  conn;
    socklen_t           connlen;
    int                 connfd, arg;
    ssize_t             n;
    long                val, left;

    if (argc < 3 || !argv[1][0] || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s SOCKET_PATH [ LEN | -MS ] ...\n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "All arguments except the first one, SOCKET_PATH, are integers.\n");
        fprintf(stderr, "A positive integer causes a seqpacket of that length to be sent,\n");
        fprintf(stderr, "a negative value causes a delay (magnitude in milliseconds).\n");
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    if (install_done(SIGINT) ||
        install_done(SIGHUP) ||
        install_done(SIGTERM)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Fill buffer with some data. Anything works. */
    {
        size_t  i = sizeof buffer;
        while (i-->0)
            buffer[i] = (i*i) ^ i;
    }

    connfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
    if (connfd == -1) {
        fprintf(stderr, "Cannot create an Unix domain seqpacket socket: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    connlen = set_unix_path(argv[1], &conn);
    if (connect(connfd, (const struct sockaddr *)&conn, connlen) == -1) {
        fprintf(stderr, "Cannot connect to %s: %s.\n", argv[1], strerror(errno));
        close(connfd);
        return EXIT_FAILURE;
    }

    /* To avoid output affecting the timing, fully buffer stdout. */
    setvbuf(stdout, NULL, _IOFBF, 65536);

    for (arg = 2; arg < argc; arg++)
        if (parse_long(argv[arg], &val)) {
            fprintf(stderr, "%s: Not an integer.\n", argv[arg]);
            close(connfd);
            return EXIT_FAILURE;
        } else
        if (val > (long)sizeof buffer) {
            fprintf(stderr, "%s: Seqpacket size too large. Current limit is %zu.\n", argv[arg], sizeof buffer);
            close(connfd);
            return EXIT_FAILURE;
        } else
        if (val >= 0) {
            n = send(connfd, buffer, (size_t)val, 0);
            if (n == (ssize_t)val)
                printf("Sent %ld-byte seqpacket successfully.\n", val);
            else
            if (n != (ssize_t)val && n >= 0)
                fprintf(stderr, "Sent %zd bytes of a %ld-byte seqpacket.\n", n, val);
            else
            if (n < -1) {
                fprintf(stderr, "C library bug: send() returned %zd.\n", n);
                close(connfd);
                return EXIT_FAILURE;
            } else
            if (n == -1) {
                fprintf(stderr, "Send failed: %s.\n", strerror(errno));
                close(connfd);
                return EXIT_FAILURE;
            }
        } else {
            left = sleep_ms(-val);
            if (left)
                fprintf(stderr, "Slept %ld milliseconds (out of %ld ms).\n", -val-left, -val);
            else
                printf("Slept %ld milliseconds.\n", -val);
        }

    if (close(connfd) == -1) {
        fprintf(stderr, "Error closing connection: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    printf("All done, connection closed.\n"); 
    fflush(stdout);

    return EXIT_SUCCESS;
}

Compile this using eg gcc -Wall -O2 send.c -o send . 使用gcc -Wall -O2 send.c -o send编译。

For testing, I recommend you use two terminal windows. 为了进行测试,建议您使用两个终端窗口。 Run send in one, and receive in the other. 运行send一个, receive另一个。 For simplicity, I'll show the corresponding commands and outputs side-by-side. 为简单起见,我将并排显示相应的命令和输出。 The machine this runs on is a Core i5 7200U laptop (HP EliteBook 830), running Ubuntu 16.04.4 LTS, 64-bit Linux kernel version 4.15.0-24-generic, and binaried compiled using GCC-5.4.0 20160609 (5.4.0-6ubuntu1~16.04.10) and the abovementioned commands ( gcc -Wall -O2 ). 运行该计算机的计算机是Core i5 7200U笔记本电脑(HP EliteBook 830),运行Ubuntu 16.04.4 LTS,64位Linux内核版本4.15.0-24(通用),并使用GCC-5.4.0 20160609(5.4 .0-6ubuntu1〜16.04.10)和上述命令( gcc -Wall -O2 )。

When we use a small delay before the final send, everything seems to work just fine: 当我们在最终发送之前使用一小段延迟时,一切似乎都可以正常工作:

$ ./send '\0example' 1 0 3 0 0 -1 6   │   $ ./receive '\0example'
                                      │   Connected, peer address size is 2.
Sent 1-byte seqpacket successfully.   │   Received 1 bytes.
Sent 0-byte seqpacket successfully.   │   Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully.   │   Received 3 bytes.
Sent 0-byte seqpacket successfully.   │   Received a zero-byte seqpacket.
Sent 0-byte seqpacket successfully.   │   Received a zero-byte seqpacket.
Slept 1 milliseconds.                 │  
Sent 6-byte seqpacket successfully.   │   Received 6 bytes.
All done, connection closed.          │   Disconnected (revents = 16).

However, when the sender sends the final few seqpackets (starting with a zero-length one) without any delays in between, I observe this: 但是,当发送方发送最后几个seqpackets(从零长度的seqpackets开始)之间没有任何延迟时,我观察到以下情况:

$ ./send '\0example' 1 0 3 0 0 6      │   ./receive '\0example'
                                      │   Connected, peer address size is 2.
Sent 1-byte seqpacket successfully.   │   Received 1 bytes.
Sent 0-byte seqpacket successfully.   │   Received a zero-byte seqpacket.
Sent 3-byte seqpacket successfully.   │   Received 3 bytes.
Sent 0-byte seqpacket successfully.   │   
Sent 0-byte seqpacket successfully.   │   
Sent 6-byte seqpacket successfully.   │   
All done, connection closed.          │  Disconnected (revents = 16).

See how the two zero-byte seqpackets and the 6-byte seqpacket are missed (because poll() returned revents == POLLHUP . ( POLLHUP == 0x0010 == 16, so there were no other flags set either time.) 查看如何丢失两个零字节的seqpackets和6字节的seqpacket(因为poll()返回的revents == POLLHUP 。( POLLHUP == 0x0010 == 16,所以两个时间都没有设置其他标志。)

I am personally not sure if this is a bug or not. 我个人不确定这是否是错误 In my opinion, it is just and indication that using zero-length seqpackets is problematic, and should be avoided. 在我看来,这是公正和表明使用零长度seqpackets是有问题的,应该避免。

(The peer address length is 2 above, because the sender does not bind to any address, and therefore uses an unnamed Unix domain socket address (as described in the man unix man page). I don't think it is important, but I left it in just in case.) (对等地址长度为2以上,因为发件人未绑定任何地址,因此使用了未命名的Unix域套接字地址(如man unix手册页中所述)。我认为这并不重要,但是我留以防万一。)

There was a discussion on one possible solution to the problem at hand, via MSG_EOR (since recvmsg() should add MSG_EOR to the msg_flags field in the msghdr structure; and since it "should be set for all seqpackets", even zero-length ones, it would be a reliable way to detect zero-length seqpackets from end-of-input/read-side shutdown/disconnect) in the Linux Kernel Mailing List (and linux-netdev list) in May 2007. (The archived thread at Marc.info is here ) However, in Linux Unix domain seqpacket sockets, the MSG_EOR is not set nor passed, according to the initial poster, Sam Kumar. 讨论了通过MSG_EOR解决当前问题的一种可能方法(因为recvmsg()应该将MSG_EOR添加到msghdr结构的msg_flags字段中;并且由于“应该为所有seqpackets设置它”,甚至是零长度的seqpackets) ,这将是2007年5月Linux内核邮件列表(和linux-netdev列表)中从输入结束/读取侧关闭/断开连接中检测零长度seqpackets的可靠方法。( Marc中的存档线程) .info在此处 )但是,根据最初发布者Sam Kumar的MSG_EOR ,在Linux Unix域seqpacket套接字中,未设置或传递MSG_EOR The discussion did not lead anywhere; 讨论没有进行到任何地方。 as I read it, nobody was sure what the expected behaviour even should be. 在我阅读本文时,没有人能确定预期的行为。

Looking at the Linux kernel changelog for Unix domain sockets , there have been no related changes since that thread either (as of 23 July 2018). 查看针对Unix域套接字的Linux内核更改日志,自该线程以来(截至2018年7月23日)也没有任何相关更改。


The above programs were written in one sitting, without review; 以上程序是一站式编写,未经审查; so, they could easily have bugs or thinkos in them. 因此,他们很容易在其中包含错误或想法。 If you notice any, or obtain very different results (but do note timing-based effects are sometimes hard to replicate), do let me know in a comment, so I can check, and fix if necessary. 如果您发现任何问题,或获得了截然不同的结果(但请注意,有时很难复制基于时序的效果),请在评论中告知我,以便我检查并在必要时进行修复。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM