0

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;
}

v.doro2
  • 3
  • 2
  • I guess that in your implementation `recv` on the client side is blocking. The socket ops are blocking by default unless you change this manually. Therefore, it will wait until `BUFFER_SIZE` amount of data is received or until the socket is closed by the other party (what you observe). If you want to able to receive and immediately process any amount of data received, you will need to make the socket non-blocking like discussed here https://stackoverflow.com/questions/1543466/how-do-i-change-a-tcp-socket-to-be-non-blocking – RAllen May 15 '23 at 03:54
  • Another option is to instruct `recv` to read a single byte instead of `BUFFER_SIZE`. In this case, `recv` will return after every single byte and you will be able to process whatever is received at any moment in time. In this scenario, it will be a bit overkill to `realloc` after every byte, and you may want to modify this logic a bit by allocating, let's say 4096 bytes initially and then `realloc` only if you receive more than this. Memory strategy here depends on your task and desired behavior, so it is hard to suggest the best approach without more context. – RAllen May 15 '23 at 04:00

2 Answers2

1

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,* 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("something"));
// ...
process.nextTick(() => socket.uncork());

* 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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thank You, I didn't knew about `cork()` `uncork()` but wrapping a `write()` as You showed didn't worked.. only `socket.end()` worked for now – v.doro2 May 14 '23 at 16:38
1

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 <=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.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thank You. I made a `recv()` to work until 0 bytes read because I wanted to read indefinite amount of data. Now I know what I need to fix for it to work correctly, but what if I "really" need to read indefinite amount of data isn't it that "stop word" of `recv()` is 0 bytes read from socket? – v.doro2 May 14 '23 at 17:59
  • @v.doro2: Again, TCP is a byte stream. There are no "stop words" inside this stream which would make `recv` return 0 - the only stop is the end of the stream (shutdown). If you want to have message semantics on top of this byte stream you need to implement it yourself in your application protocol, like defining your own stop words and looking for these in the byte stream or by having a fixed size prefix before each message which contains the size of the message. Only then you know when to stop calling `recv`. – Steffen Ullrich May 14 '23 at 18:13