How to get the buffer from the socket?

Asked

Viewed 972 times

2

I am receiving a lot of data coming from a server, the client(the code below), after sending a message, it receives another, but I would like to know the size of the message that is coming from the server to allocate the variable before going to the recv (assuming the server sends random text sizes).

  • Is there any function to collect the size of the Buffer coming by Socket?
  • You can get the size of the data from Socket for fstat or fseek?
  • How can I make this implementation?

client.c

/*Socket and Network headers*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>

#define PORT 8000
#define TCP 6

int main(int argc, char *argv[]){
    int _sock;
    int res;
    char buffer[256];
    struct sockaddr_in serv; //socket address struct
    struct hostent *server;

    // int socket(sin_family, socket_type, protocol)
    // protocol TCP equal 6 (TRANSMISSION CONTROL PROTOCOL)
    _sock = socket(AF_INET, SOCK_STREAM, TCP);
    if(_sock < 0)
        return 1;

    server = gethostbyname("127.0.0.1");

    // set zero in buffer
    bzero( (char *) &serv, sizeof(serv));

    // configure connection
    serv.sin_family = AF_INET;
    serv.sin_port = htons(PORT);
    bcopy((char *) server->h_addr,
            (char *) &serv.sin_addr.s_addr,
            server->h_length);
    //serv.sin_addr.s_addr = INADDR_ANY;

    int len = sizeof(serv);
    res = connect(_sock, (struct sockaddr *) &serv, len);

    if(res < 0)
        return 2;

    bzero(buffer, 256);
    fgets(buffer,255,stdin);
    res = write(_sock,buffer, strlen(buffer));

    int size;
    ioctl(_sock, FIONREAD, &size);
    printf("%d\n", size);

    char c;
    while(1)
        if(read(_sock, &c, 1) != 1)
            break;
        else
            printf("%c",c);
    //  res = read(_sock, buffer, 255);
    //if(res)
    //  puts(buffer);
    //strcpy(buffer,"return message.");

    close(_sock);
}

3 answers

5

You’re trying to predict the size of a stream unknown to preallocate a buffer? This does not exist.

  • Assuming you can format the message on the server you could add a Header with the size of the rest of the message. Common file procedure. But if connection fails you will have problems with the read() function which is blocking.
  • If you cannot format the message you will have to deal with large buffers or buffer relocation and better think about working with threads and timeout to read sockets.

3

An answer I saw on Stackoverflow about because while(!eof(fd)) it’s wrong fits here. I will try to adapt here, and answer: "Because you should not count on the size of the 'message', even if the operating system offers this functionality?"

At first, consider this analysis at a high level of abstraction:

Competition and Simultaneity

I/O operations interact with the environment. The environment is not part of your program, and is not under your control. The environment exists concurrently to your show. Like all competing things, questions about an absolute current state make no sense: the concept of "simultaneity" does not exist at all, as Einstein already said with his general relativity, and Leslie Lamport, to try to establish the order relationship between events that happen concurrently in a distributed system.

To be more precise: suppose you ask, "how big is the message?". You could ask this to another competing thread receiving the message, or to the I/O. system. No matter what size is reported by the system, this information may always have changed at the time you read the message, so the usefulness of this information is limited.

In fact, it is not reasonable to ask the I/O system how an I/O operation will happen in the future. The only reasonable way to address the issue is to try to carry out the operation, to then deal with the possible consequences of the operation. The moment you interact with the environment, and only at that moment, you can figure out how the operation would go, effectively performing that operation.

Sockets

How much of the external concurrent environment does the socket interface actually expose to the programmer? It depends on the protocol. Socket is an abstract interface that gives access to several different communication protocols. Theoretically, it is possible for a protocol to treat all mishaps internally to the socket implementation, and make it possible to receive entire messages (also assuming that entire messages fit into the buffer). Perhaps UDP allows this treatment, considering that its datagrams have a maximum size of 65507 bytes, which fits in network buffers.

But its code does not use UDP. It uses TCP, and unlike UDP, it is not message-oriented, but oriented to stream. There are no logical boundaries between the communication units within the TCP, and a byte is potentially available to the socket user as soon as it arrives in the network interface. Therefore all uncertainties of communicating with the outside world are passed directly to the program.

The correct way to read a TCP socket is to allocate the logical structure your program expects, and read the socket in a loop until it is filled. For example:

#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>

bool read_full(int fd, void *buf, size_t size)
{
    uint8_t *ptr = buf;
    ssize_t ret;
    while(size > 0) {
        ret = read(fd, ptr, size);
        if(ret <= 0) {
            if(errno == EINTR)
                continue;

            return false;
        }
        size -= ret;
        ptr += ret;
    }

    return true;
}

void func(int fd)
{
    struct my_msg msg;

    if(read_full(fd, &msg, sizeof msg)) {
        puts("Mensagem recebida com sucesso!");

        /* Use msg... */
    } else {
        puts("Falha ao receber a mensagem.\n"
            "Poderia verificar errno para descobrir o que aconteceu.");
    }
}

If you do not know the size of a logical message unit, this should be handled by the protocol of its application (for example, each message could have a fixed-sized header containing the size of the rest of the message, so the first thing you would try to receive is this header). TCP doesn’t have that information! The information you get with ioctl(_sock, FIONREAD, &size); does not serve for nothing in a TCP communication, because each message sent by the server may have been divided into several datagrams by the TCP layer, and the information of the original size is not preserved by the network. What you get with this call is the size of the next pending datagram, which is not necessarily equal to the size of the message sent from the other end.

2


It is possible to use the function ioctl (on Windows, ioctlsocket) with argument FIONREAD, to get the information you want, but never I saw it being used. Among other things, because when you do the recv the value returned by ioctl may already be another (for example, if the data is continuously coming through a connection). This is a typical example of TOCTOU anti-pattern (see on Google, Wikipedia, etc).

Your way of trying to use sockets is reversed from the commonly used mode. This mode is as follows: you have an X-size buffer, and use this buffer to do repeated readings.

See my answer here a question about sockets, maybe it is useful.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.