Cristian
Cristian

Reputation: 23

Socket server receiving data at buffer size

I send packets of different sizes one after the other, how can I receive the packets separately at the size I send, not cumulated in the buffer. It seems that now the server adds to the buffer until it fills it and then I can process them.

Example:

Buffer size: 84.

Send from client: 84 bytes, 76 bytes, 76 bytes, 80 bytes

Receive in server: 84 bytes, 84 bytes, 84 bytes, 64 bytes.

I would like to receive them as I sent them. Is it possible?

    int port = stoi(getConfig("server_port"));
    std::string ipAddress = getConfig("ip_address");

    // Create a socket
    int listening = socket(AF_INET, SOCK_STREAM, 0);
    if (listening < 0){
        std::cerr << "Can't create a socket!" << endl;
        Logger("Can't create a socket!");
        exit(-1);
    }
    std::cout << "The socket server was created successfully." << endl;

    // Bind the socket to a IP / port
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(port);
    inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);

    if (bind(listening, (sockaddr*)&hint, sizeof(hint)) < 0){
        cerr << "Can't bind to IP/port!" << endl;
        Logger("Can't bind to IP/port!");
        exit(-1);
    }

    // Mark the socket for listening in
    if (listen(listening, SOMAXCONN) < 0){
        cerr << "Can't listen!" << endl;
        Logger("Can't listen!");
        exit(-1);
    }

    // Accept a call
    sockaddr_in client;
    socklen_t clientSize = sizeof(client);
    char host[NI_MAXHOST];
    char svc[NI_MAXSERV];

    while(true){
        int clientSoket = accept(listening, (sockaddr*)&client, &clientSize);
        if(clientSoket < 0){
            cerr << "Problem with client connecting!" << endl;
            Logger("Problem with client connecting!");
            break;
        }
        cout << "The client whas conected successfully." << endl;

        memset(host, 0, NI_MAXHOST);
        memset(svc, 0, NI_MAXSERV);

        int result = getnameinfo((sockaddr*)&client, clientSize, host, NI_MAXHOST, svc, NI_MAXSERV, 0);
        if(result == 0) {
            cout << host << " connected on " << svc << endl;
        } else {
            inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
            cout << host << " connected on " << ntohs(client.sin_port) << endl;
        }

        // While receiving
        char buff[84];
        while(true){
            // Clear the buffer
            memset(buff, 0, sizeof(buff));

            // Wait for a message
            int bytesRecv = recv(clientSoket, buff, sizeof(buff), 0);
            if(bytesRecv < 0){
                cerr << "There was a connection issue!" << endl;
                Logger("There was a connection issue!");
                break;
            }

            if(bytesRecv == 0){
                cout << "The client disconnected." << endl;
                Logger("The client disconnected");
                break;
            }

            cout << "bytesRecv: " << bytesRecv << endl;
       }

       // Close the socket
       close(clientSoket);
    }

Upvotes: 2

Views: 721

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118340

No, stream sockets don't work that way.

A stream socket is an unstructured byte stream, without any structure to it, whatsoever. In this respect it is no different from a plain file. If you wrote your records, of varying sizes, to a plain file and you are now prepared to read them back, how do you expect to read the your variably-sized records?

Whichever answer you give here, the same answer applies to sockets, with the additional twist that a read() on a socket offers you no guarantees whatsoever as to how much you'll read, except that it'll be less than or equal to the size parameter to read(). That's the only warranty you'll get from read().

If the sender called write() twice (and, by the way, sockets also don't guarantee that however much you want to write, that much gets written, write can also return a byte count less than or equal to its size parameter, and it's up to your code to figure out how to deal with it), once writing 76 bytes and the second time with 84, read()ing that (assuming a sufficiently large buffer size) can read any number of bytes between 1 and 160 bytes, on the initial read.

If you wish to implement some formal structure, records of some kind, it is up to you to figure out how to implement it within these constraints. Maybe by sending the size of each record, in bytes, followed by the record itself. Or do whatever you want. Just keep in mind that you have no guarantees, whatsover, how much an individual read() returns. If, for example, you're sending the record count first, as a four byte value. Your initial read() may return one, two, or three bytes. Your code must be prepared to handle any eventuality.

Upvotes: 1

Related Questions