【问题标题】:Size of the data buffer in Unix Domain stream socketUnix 域流套接字中数据缓冲区的大小
【发布时间】:2014-10-13 22:00:19
【问题描述】:

我有一个使用 Unix 域套接字实现的客户端和一个服务器。

我想看看客户端可以在服务器不读取数据的情况下将多少数据推送到套接字。我希望客户端能够在被阻止之前发送大约 20-30KB 的数据。我检查了 net.core.rmem_default、net.core.wmem_default、net.core.rmem_max、net.core.wmem_max 和 net.unix.max_dgram_qlen sysctl 选项,并确定我没有达到这些值。我还增加了 net.unix.max_dgram_qlen 值,但似乎没有帮助。

看到我能够发送的固定大小的消息数量约为 138 条,我感到非常惊讶。即使我减小了消息的大小,这个数字仍然保持不变。

在客户端,我进入一个循环并写入 1024 条消息。

客户代码

void write_text (int socket_fd, char* text)
{
    int length = strlen (text) + 1;

    send (socket_fd, &length, sizeof (length),0);
    /* Write the string. */
    send (socket_fd, text, length,0);
}

int main (int argc, char* const argv[])
{
   const char* const socket_name = argv[1];
   char message[100];
   int socket_fd;
   int loop = 0;
   struct sockaddr_un name;
   /* Create the socket. */
   socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0);
   /* Store the server's name in the socket address. */
   name.sun_family = AF_UNIX;
   strcpy (name.sun_path, socket_name);
   /* Connect the socket. */
   connect (socket_fd, (struct sockaddr *) &name, SUN_LEN (&name));

   for (loop=0;loop<1024;loop++)
   {
      sprintf (message, "message number %d coming from the client", loop);
      /* Write the text on the command line to the socket. */
       write_text (socket_fd, message);
    }

    close (socket_fd);
    return 0;   
}

服务器端代码:

unsigned int global_flag = 0;
int client_socket_fd = 0;

int server (int client_socket)
{    

  while (1) 
  {
    int length;
    char* text;

    if (read (client_socket, &length, sizeof (length)) == 0)
        return 0;

    text = (char*) malloc (length);

    read (client_socket, text, length);
    printf ("length %d %s\n", length, text);

    if (global_flag<5) break;

    free (text);
   }

    return 0;
}

int main (int argc, char* const argv[])
{
const char* const socket_name = argv[1];

int socket_fd;
struct sockaddr_un name;
int client_sent_quit_message;
socklen_t socket_length = sizeof(struct sockaddr_un);
int result;
int len = sizeof (int);
int data = 0;

socket_fd = socket (AF_LOCAL, SOCK_STREAM, 0);

name.sun_family = AF_UNIX;
strcpy (name.sun_path, socket_name);
socket_length = strlen(name.sun_path) + sizeof (name.sun_family);

bind (socket_fd, (struct sockaddr *) &name, socket_length);

listen (socket_fd, 5);

while (1)
{
        struct sockaddr_un client_name;
        socklen_t client_name_len;

        /* Accept a connection. */
        client_socket_fd = accept (socket_fd, (struct sockaddr *) &client_name, &client_name_len);

        client_sent_quit_message = server (client_socket_fd);            
}

/* Remove the socket file. */
close (socket_fd);
unlink (socket_name);
return 0;
}

在服务器端代码中,global_flag 始终小于 0,因此服务器执行一次读取并输出。服务器不再进行读取。客户端同时在套接字上推送数据。

我在客户端上做了一个 strace 并得到了这个:

VirtualBox:~/code/linux$ strace ./unix-client /tmp/unixtest

execve("./unix-client", ["./unix-client", "/tmp/unixtest"], [/* 44 vars */]) = 0
brk(0)                                  = 0x1245000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =    0x7f3e12b63000
fstat(3, {st_mode=S_IFREG|0644, st_size=68001, ...}) = 0
mmap(NULL, 68001, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3e12b52000
close(3)                                = 0

fstat(3, {st_mode=S_IFREG|0755, st_size=1815224, ...}) = 0
mmap(NULL, 3929304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f3e12583000
[clipped]
socket(PF_FILE, SOCK_STREAM, 0)         = 3
connect(3, {sa_family=AF_FILE, path="/tmp/unixtest"}, 15) = 0
sendto(3, "(\0\0\0", 4, 0, NULL, 0)     = 4
sendto(3, "message number 0 coming from the"..., 40, 0, NULL, 0) = 40
sendto(3, "(\0\0\0", 4, 0, NULL, 0)     = 4
sendto(3, "message number 1 coming from the"..., 40, 0, NULL, 0) = 40
sendto(3, "(\0\0\0", 4, 0, NULL, 0)     =  4
sendto(3, "message number 2 coming from the"..., 40, 0, NULL, 0) = 40
sendto(3, "(\0\0\0", 4, 0, NULL, 0)     = 4

..

sendto(3, "message number 138 coming from t"..., 42, 0, NULL, 0) = 42
sendto(3, "*\0\0\0", 4, 0, NULL, 0)     = 4
sendto(3, "message number 139 coming from t"..., 42, 0, NULL, 0) = 42
sendto(3, "*\0\0\0", 4, 0, NULL, 0

[和这里的客户块]

知道为什么当服务器停止从套接字排出消息时客户端在发送 139 条消息后会被阻塞?

我假设如果我减小消息的大小,客户端可以在套接字上发送的消息数量会增加。但是,我看到它保持不变。无论消息的大小如何,客户端都无法在套接字上发送超过 139 条消息而不会被阻塞。

【问题讨论】:

  • 问题是?
  • 只是一个注释,实际上与您的问题无关:sizeof 是一个运算符,而不是一个函数。 sizeof 的参数应该是变量、表达式或带括号的类型名称。所以在你有sizeof (length)的两个地方,它真的应该是sizeof length,或者sizeof (int)
  • 问题是为什么客户端在发送139条消息后不管消息的大小都会被阻塞。这个限制来自哪里?

标签: c linux sockets unix


【解决方案1】:

您假设 read() 填充了缓冲区。没有指定这样做,只传输至少一个字节。您需要将返回值存储在一个变量中,检查它是否为 -1,检查它是否为零,否则将其用作接收到的数据的实际长度:如果它小于您的预期,请再次读取。冲洗并重复。

【讨论】:

  • 这与您对Buffering characteristics of Unix Sockets, EJP 的评论有何关系:我看不出有任何理由说明已写入的数据不会立即在任何平台上可用。?
  • @Armali 我在那里解决的是声称它是系统特定的。我在这里要解决的是read() 填充缓冲区的错误信念。 read() 将阻塞直到至少有一个字节可用,然后返回任何可用的字节,即使它小于缓冲区大小。没有不一致的地方。
【解决方案2】:

由于问题是关于在一定数量的send() 调用后阻塞服务器不再进行读取,因此争论read() 毫无意义。
sourcejedi 对“What values may Linux use for the default unix socket buffer size?”问题的回答解释说,e 的开销相当高。 G。 每个数据包 576 字节。虽然我们不习惯考虑与面向流的套接字相关的数据包,但如果我们假设此开销也适用于 SOCK_STREAM 类型的 UNIX 域套接字上的单个发送调用,我们可以为您的观察得出一个解释:默认值163840 字节的缓冲区大小除以 (4+576) 字节和大约 (41+576) 字节的总和得出的数字非常接近您能够发送的消息对数。

【讨论】:

    猜你喜欢
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多