Search code examples
c++socketstcpsendrecv

TCP Server/Client : client recv() returns blank buffer


I've created a TCP Server/Client in C++. The client takes an input char array from stdin. The client then sends it to the server. The server's job is to respond with a modified form of the message from the client. The server's modification: replace the first char of the input message with 'X'

It works as expected on the first input message, but the client receives malformed data starting with the second message.

./client.bin output:

helloworld          <-----stdin
TX: helloworld
RX: Xelloworld
test                <-----stdin
TX: test
RX: 0est
somethingelse       <-----stdin
TX: somethingelse
RX: Xest

./echoserver.bin output:

RX: helloworld
TX: Xelloworld
RX: test
TX: Xest
RX: somethingelse
TX: Xomethingelse

echoserver.cc:

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>   
#define  PORT 8080
#define  BUF_SIZE 1024

void error_exit(std::string text)
{
    std::cerr << text << std::endl;
    exit(1);
}

int main()
{
    int sd;
    int connection;
    int valread;
    int opt = 1;
    char buffer[BUF_SIZE] = {0};
    if ((sd = socket(AF_INET,SOCK_STREAM,0)) == 0)
        error_exit("socket failure");

    if (setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,
            &opt, sizeof(opt)))
        error_exit("failed to set socket opts");

    sockaddr_in address;
    address.sin_family      = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port        = htons(PORT);
    if (bind(sd, (struct sockaddr *)&address, sizeof(address)) != 0)
        error_exit("failed to bind socket");

    if (listen(sd,3) < 0)
        error_exit("failed to listen");

    int addrlen = sizeof(address);
    if ((connection = accept(sd, (struct sockaddr*)&address,
                    (socklen_t*)&addrlen)) < 0)
        error_exit("failed to accept socket link");

    while (1)
    {
        memset(&buffer[0],'0', sizeof(buffer));
        if (recv(connection,buffer,BUF_SIZE-1,0) <= 0)
            break;
        std::cout << "RX: " << buffer << std::endl;
        buffer[0] = 'X';
        std::cout << "TX: " << buffer << std::endl;
        send(connection,buffer,BUF_SIZE,0);
    }
    return 0;
}

client.cc

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUF_SIZE 1024

void error_exit(std::string text)
{
    std::cerr << text << std::endl;
    exit(1);
}

int main()
{
    int sd;
    int valread;
    struct sockaddr_in address;
    struct sockaddr_in server_addr;
    char buffer[BUF_SIZE] = {0};

    if ((sd = socket(AF_INET,SOCK_STREAM,0)) < 0)
        error_exit("failed to create socket");

    memset(&server_addr, '0', sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port   = htons(PORT);

    if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0)
        error_exit("Invalid Address");

    if (connect(sd,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
        error_exit("Connection Failure");

    std::cin >> buffer;
    while (buffer[0] != 'q')
    {
        std::cout << "TX: " << buffer << std::endl;

        send(sd,buffer,BUF_SIZE-1,0);
        if ( recv(sd,buffer,BUF_SIZE-1,0) <= 0)
            error_exit("Failed to receive");
        std::cout << "RX: " << buffer << std::endl;
        memset(&buffer[0],'0', sizeof(buffer));
        std::cin >> buffer;
    }
    return 0;
}

Solution

  • your send calls are always sending 1023 bytes but your recv calls don't necessarily download all of that data, you either need to call recv in a loop until you have all the sent data or pass the MSG_WAITALL flag.