How to get the buffer from the socket?

2

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

  • Is there a function to collect the Buffer size coming from Socket ?
  • Is it possible to get the size of the data coming from the Socket by fstat or fseek ?
  • How can I do 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);
}
    
asked by anonymous 23.03.2017 / 01:56

3 answers

2

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

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

See my answer here for a question about sockets might be helpful.

    
25.03.2017 / 23:31
5

Are you wanting to predict the size of an unknown stream to pre-allocate a buffer? That does not exist.

  • Assuming you can format the message on the server you could add at the beginning a Header with the size of the rest of the message. Common file procedure. But if the connection fails you will have problems with the read () function that is blocking.
  • If you can not format the message you will have to deal with large buffers or and it is best to think about working with threads and timeout when read sockets.
25.03.2017 / 21:59
2

An answer I saw in StackOverflow in English on because while(!eof(fd)) is wrong fits here. I'll try to adapt here, and respond: "Why should not you 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 Concurrency

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 program. Like all things competing, questions about an absolute current state do not make sense: the concept of "simultaneity" does not exist at all, as Einstein already said with its general relativity, and Leslie Lamport, at try to establish the order relationship between events that happen concurrently in a distributed system .

More precise: Suppose you ask, "What is the size of the message?". You could ask this to another concurrent thread getting the message, or to the I / O system. But the answer is usually useless, and therefore meaningless. For if the thread says "200 bytes", at the time you read it, it may be that the amount has doubled. No matter what size is reported by the system, this information may have changed at the time you read the message, so the usefulness of this information is limited.

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

Sockets

How much of the external competing 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 handle all mishaps internally to the socket implementation, and to allow full messages to be received (assuming that the entire messages fit into the buffer). Perhaps UDP allows this processing, considering that its datagrams have a maximum size of 65507 bytes, which fits in the network buffers.

But your 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 communication units within TCP, and a byte is potentially available to the socket user as soon as it arrives at the network interface. Therefore all the uncertainties of communicating with the outside world are passed on directly to the program.

The correct way to read a TCP socket is to allocate the logical structure that your program expects, and read the socket in a loop until it is populated. 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 your application's protocol (for example, each message could have a fixed-length header containing the size of the rest of the message, thus first thing you would try to receive is this header). TCP does not have this information! The information you get with ioctl(_sock, FIONREAD, &size); is useless in TCP communication, because each message sent by the server may have been divided into several datagrams by the TCP layer, and the original size information is not preserved by the network. What you get with this call is the size of the next pending datagram, which is not necessarily the size of the message sent by the other endpoint.

    
31.03.2017 / 22:13