Node.js 服务器端使用 `socket.write()`,客户端使用 C 的 `recv()`。

huangapple go评论79阅读模式
英文:

Nodejs socket.write() on server side and C recv() on client side

问题

以下是翻译好的内容:

在服务器端:

socket.write(Buffer.from("123"));

然后在客户端:

recv(socket_fd, buffer, 3, 0);

连接卡住了...

然而,

socket.end(Buffer.from("123"));

不会卡住连接。我明白为什么(至少我认为我明白了)。
但它关闭了客户端的读取套接字(仍然可以发送数据)
因此,您需要再次创建新的套接字来读取数据。

问题:
是否有一种方法

 socket.write(Buffer.from("something"))

并在客户端接收它,而不需要

socket.end();

更新:
客户端的recv():

char *GetData(int socket_fd)
{
    char init[] = {0x17, 0x22};
    int bytes_read, n_reads = 0;
    char *buffer = (char *)malloc(BUFFER_SIZE);
    if (buffer == NULL)
    {
        puts("failed\n");
        exit(EXIT_FAILURE);
    }
    send(socket_fd, init, 2, 0);
    int offset = 0;
    while ((bytes_read = recv(socket_fd, buffer + offset, BUFFER_SIZE, 0)) > 0)
    {
        if (bytes_read == -1)
        {
            puts("recv failed\n");
            exit(EXIT_FAILURE);
        }
        offset += bytes_read;
        char *tmp = realloc(buffer, offset + BUFFER_SIZE);
        if (tmp == NULL)
        {
            puts("realloc failed\n");
            exit(EXIT_FAILURE);
        }
        buffer = tmp;
    }
    return buffer;
}
英文:

This question ask exactly the same but have no answers

When on server side:

socket.write(Buffer.from("123"));

Then on client side:

recv(socket_fd, buffer, 3, 0);

connection stucks..

However

socket.end(Buffer.from("123"));

doesn't stuck the connection. And I understand why ( at least I think that I do ).
But it closes the socket for reading on client side ( You still can send data )
So You need once again create new socket to read data.

Question:
Is there a way to

 socket.write(Buffer.from("something"))

and receive it on client side without

socket.end();

UPD:
client side recv():

char *GetData(int socket_fd)
{
    char init[] = {0x17, 0x22};
    int bytes_read, n_reads = 0;
    char *buffer = (char *)malloc(BUFFER_SIZE);
    if (buffer == NULL)
    {
        puts("failed\n");
        exit(EXIT_FAILURE);
    }
    send(socket_fd, init, 2, 0);
    int offset = 0;
    while ((bytes_read = recv(socket_fd, buffer + offset, BUFFER_SIZE, 0)) > 0)
    {
        if (bytes_read == -1)
        {
            puts("recv failed\n");
            exit(EXIT_FAILURE);
        }
        offset += bytes_read;
        char *tmp = realloc(buffer, offset + BUFFER_SIZE);
        if (tmp == NULL)
        {
            puts("realloc failed\n");
            exit(EXIT_FAILURE);
        }
        buffer = tmp;
    }
    return buffer;
}

答案1

得分: 1

NodeJS流,包括net.Socket,是缓冲的。默认情况下,可写流会在本地内存中累积写入它们的数据,直到它们准备好将数据发送到输出设备,或者直到通过end()方法表示不会再有更多数据。

决定不同流何时发送数据的详细信息可能部分取决于流的类型。一旦缓冲的数据量达到阈值,所有流都有可能发送数据。某些流在最后一次写入后经过一定时间或在其他各种情况下可能会额外发送数据。但总体而言,NodeJS可写流(如net.Socket)可能会长时间缓冲少量输出。

控制缓冲的一种方法是在写入之前调用套接字的cork()方法,然后在写入后调用其uncork()方法。cork()的文档中指出:

> writable.cork()方法强制将所有写入的数据缓冲在内存中。缓冲的数据将在调用stream.uncork()stream.end()方法时刷新。

在这种情况下要理解的重点是,NodeJS流无论如何都会缓冲数据,因此在这里使用cork()不是为了请求缓冲,也不一定是为了合并多个小写入,而主要是为了启用uncork()以刷新缓冲区。

还要注意uncork()的文档,特别是关于正确使用的部分:

> writable.uncork()方法刷新自从stream.cork()被调用以来缓冲的所有数据。
>
> 在使用writable.cork()writable.uncork()来管理对流的写入的缓冲时,使用process.nextTick()推迟对writable.uncork()的调用。这样做可以批处理在给定Node.js事件循环阶段内发生的所有writable.write()调用。

示例:

socket.cork();
socket.write(Buffer.from("something"));
// ...
process.nextTick(() => socket.uncork());

<sup>*</sup> 原则上是这样。但如果执行多个write(),那么在最后一个非空写入之前调用cork()可能就足够了,因为刷新最后一个写入将需要刷新任何先前的写入。

英文:

NodeJS streams, including net.Sockets, are buffered. By default, writable streams will accumulate the data you write to them in local memory until they are good and ready to send it to the output device, or until you signal (via the end() method) that no more data will be forthcoming.

Details of when different streams decide to send data may depend in part on the kind of stream. All are likely to do so once the amount of buffered data reaches a threshold value. Some may additionally do so if a certain amount of time has elapsed since the last write, or under various other circumstances. Generally speaking, however, NodeJS writable streams such as net.Socket may buffer small amounts of output for a long time.

One way to control that would be by invoking the socket's cork() method before you write,<sup>*</sup> and then invoking its uncork() method after. The docs for cork() say:

> The writable.cork() method forces all written data to be buffered in memory. The buffered data will be flushed when either the stream.uncork() or stream.end() methods are called.

The point to understand in this case is that NodeJS streams buffer data anyway, so using cork() here is not so much to request that, nor necessarily to combine multiple small writes, but principally to enable use of uncork() to flush the buffer.

Note also the documentation for uncork(), especially the part about proper usage:

> The writable.uncork() method flushes all data buffered since stream.cork() was called.
>
> When using writable.cork() and writable.uncork() to manage the buffering of writes to a stream, defer calls to writable.uncork() using process.nextTick(). Doing so allows batching of all writable.write() calls that occur within a given Node.js event loop phase.

Example:

socket.cork();
socket.write(Buffer.from(&quot;something&quot;));
// ...
process.nextTick(() =&gt; socket.uncork());

<sup>*</sup> In principle. But if you are performing multiple write()s then it's probably sufficient to cork() before the last non-empty one, because flushing the last one will require flushing any preceding writes, too.

答案2

得分: 1

根据当前的实现,您的 GetData 函数只有在对等方关闭连接,即调用 socket.end() 之后才会返回。这是因为您一遍又一遍地调用 recv,直到它返回 0,表示对等方关闭连接,或者直到它返回错误。

这可能是因为错误的假设,即如果对等方发送的“消息”完成,recv 就会返回 &lt;=0。但是,TCP 没有消息的概念,它只是一个字节流,recv 会阻塞,直到它读取更多字节或者直到对等方关闭连接。

有关更多信息,请参阅 How does socket recv function detects end of messageHow does the python socket.recv() method know that the end of the message has been reached?How to determine if I received entire message from recv() calls。请注意,这些问题中有些是关于 Python 的 recv 函数,但是在 C 中 recv 的行为是相同的。

英文:

With the current implementation your GetData function will only return once the peer has shutdown the connection, i.e. called socket.end(). This is because you are calling recv again and again - until it returns 0 which signals shutdown by the peer or until it returns an error.

This is likely due to a wrong assumption that recv will return with &lt;=0 if the "message" send by the peer is complete. Only, TCP has no concept of a message. It is only a byte stream and recv will block until it has read more bytes or until the peer has shutdown.

For more on this see How does socket recv function detects end of message, How does the python socket.recv() method know that the end of the message has been reached?, How to determine if I received entire message from recv() calls. Note that it does not matter that some of these question ask about Python recv function, since the behavior of recv in C is the same.

huangapple
  • 本文由 发表于 2023年5月14日 20:50:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76247579.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定