Home > Enterprise >  Data gets cut off when send through a tcp socket in c/c
Data gets cut off when send through a tcp socket in c/c

Time:01-02

Long messages get cut off when I send them through a tcp socket. It differs depending on the destination. When sending and receiving locally on my machine, all goes through. When sending and receiving locally on my server, it gets cut off after the 21846th byte consistently. When sending from my machine to the server, it gets cut off at the 1441th byte consistently. The server is in Stockholm and I'm in the UK. The same problem is also present when the client is on Windows and uses Windows' networking code.

Here the client is supposed to send 29 999 zeros and a null terminator, receive them and print them. When I counted them with wc, I got the figures of actual bytes that I received. So the figures represent a two-way transfer but from testing I can say that the problem has the same properties one-way.

The read/write functions are blocking as far as I can tell, so the problem is not that the data has not arrived fully before the functions exit - a problem described in the man page.

How can I fix this?

Go to the bottom to see solution

Here's the code that I used to test this:

server/main.cpp

#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>

#include "aes/aes.hpp"
#include "network.hpp"
#include <thread>

using namespace std;

int main()
{
    int server_socket = InitServerSocket(26969);
    std::cout << "Listening..." << std::endl;

    while (true) {
        // accept incomming connections - blocking
        int client_socket = accept(server_socket, NULL, NULL);

        if (client_socket < 0) {
            std::cerr << "Unable to accept";
            close(server_socket);
            return 1;
        }

        char long_text[30000];
        read(client_socket, long_text, 30000);

        std::cout << long_text << std::endl;

        write(client_socket, long_text, 30000);

        close(client_socket);
    }

    return 0;
}

InitServerSocket():

int InitServerSocket(int port)
{
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(port);
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);

    int server_socket;
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        perror("Unable to create socket");
        _exit(1);
    }

    int result = bind(
        server_socket,
        (struct sockaddr*)&server_address,
        sizeof(server_address));
    if (result < 0) {
        perror("Unable to bind");
        _exit(1);
    }

    if (listen(server_socket, 1000) < 0) {
        perror("Unable to listen");
        _exit(1);
    }

    return server_socket;
}

client/main.cpp

#include <arpa/inet.h>
#include <iostream>
#include <netdb.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "network.hpp"

using namespace std;

int main()
{
    int socket = ConnectToHost("70.34.195.74", 26969);

    char long_text[30000];
    for (int i = 0; i < 30000; i  )
        long_text[i] = '0';
    long_text[29999] = '\0';

    write(socket, long_text, 30000);

    read(socket, long_text, 30000);

    std::cout << long_text << std::endl;

    CloseConnection(socket);

    return 0;
}

ConnectToHost():

int ConnectToHost(char* IPAddress, int PortNo)
{
    // create a socket
    int network_socket; // socket descriptor ~= pointer ~= fd
    network_socket = socket(AF_INET, SOCK_STREAM, 0);
    // specify a destination address
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET; // specify protocol
    server_address.sin_port = htons(PortNo); // specify port
    // server_address.sin_addr.s_addr = a.s_addr; // specify resolved ip
    server_address.sin_addr.s_addr = inet_addr(IPAddress);

    // connect
    int connection_status = connect(
        network_socket,
        (struct sockaddr*)&server_address,
        sizeof(server_address));
    if (connection_status == -1)
        std::cout << "Failed to conect to remote host\n";
    return network_socket;
}

Solution:

Here are the wrapper functions I wrote to fix the problem:

int Send(int soc, char* buffer, int size)
{
    int ret = -1;
    int index = 0;

    do {
        ret = write(soc, buffer   index, size - index);
        index  = ret;
    } while (ret > 0);

    if (ret == -1)
        return -1;
    else
        return 1;
};

int Recv(int soc, char* buffer, int size)
{
    int ret = -1;
    int index = 0;

    do {
        ret = read(soc, buffer   index, size - index);
        index  = ret;
    } while (ret > 0);

    if (ret == -1)
        return -1;
    else
        return 1;
};

CodePudding user response:

write(client_socket, long_text, 30000);

You have no guarantees, whatsoever, that all 30000 bytes get written, even with blocking sockets. You must check the write()'s return value to determine how many bytes were actually written, then implement the required logic to try again, with whatever's left to be written, and proceed in this manner until all 30000 bytes get written to the socket. This is how sockets always work.

read(socket, long_text, 30000);

Same thing here, you must check the returned value. If the socket has only a hundred bytes of unread data waiting you'll get these 100 bytes and read() will return 100. If there's nothing unread from a blocking socket, read() will block. If the socket ends up receiving a packet with 1 byte, read() returns 1, which tells you that only 1 byte was read.

How can I fix this?

You must always check what every read() and write() returns, and proceed accordingly. If you need to read or write more data from the socket, then try again, and do that.

so the problem is not that the data has not arrived fully before the functions exit - a problem described in the man page.

The man page also describes what the returned value from read() and write() means: a negative value indicates an error, a positive value indicates how many bytes were actually read or written. Only reading and writing to regular files guarantees that the requested number of bytes will be read or written (unless reading reaches the end of the file).

  • Related