IWProgrammer
IWProgrammer

Reputation: 157

How to read data from socket correctly?

I've got a simple client/server application. The user writes strings in the console. When he pushes enter string is sent. I can transfer one line correctly, but after it substitutes the first letter of first sent word to each next. For example, if a user sends "Hello", the server will get "Hello", but after, if I send "Hello" again, the server will get "HHello". If I try to clear buffer at the client-side after sending it, it never sends something again.

Server code:

// Server side C/C++ program to demonstrate Socket programming 
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>

#define PORT 57174
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
    perror("socket failed");
    exit(EXIT_FAILURE);
}


if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
               &opt, sizeof(opt)))
{
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );


if (bind(server_fd, (struct sockaddr *)&address,
         sizeof(address))<0)
{
    perror("bind failed");
    exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
    perror("listen");
    exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
                         (socklen_t*)&addrlen))<0)
{
    perror("accept");
    exit(EXIT_FAILURE);
}

char buffer[1024];
bzero(buffer, sizeof(buffer));
int step = 0;
   while(1){
   valread = read( new_socket , buffer, 1024);


   if(valread == 0)
       break;

   printf("%s", buffer );
printf("\n");

   bzero(buffer, sizeof(buffer));   


};

       return 0;
   } 

Client code:

// Client side C/C++ program to demonstrate Socket programming

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>
#define PORT 57174

int main(int argc, char const *argv[]) 
{ 
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    { 
        printf("\n Socket creation error \n"); 
        return -1; 
    } 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));

    // Convert IPv4 and IPv6 addresses from text to binary form 
    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
    { 
        printf("\nConnection Failed \n"); 
        return -1; 
    }
//    char buffer[1024] = {0};

    unsigned int N = 10, delta=10, i = 0;
    char* buf = (char*) malloc (sizeof(char)*N);
    while (1)  {
        buf[i] = getchar();
        if(buf[i] == 27)
            break;

        if(buf[i] == 10){
            send(sock , buf , strlen(buf) , 0 );
//            bzero(buf, sizeof(buf));
            N = 10;
            buf = (char*) realloc (buf, sizeof(char)*N);
            i = 0;
        }

        if (++i >= N) {
            N += delta;
            buf = (char*) realloc (buf, sizeof(char)*N);
        }
    }



    return 0; 
}

Upvotes: 0

Views: 1931

Answers (1)

bruno
bruno

Reputation: 32586

if a user sends "Hello", the server will get "Hello", but after, if I send "Hello" again, the server will get "HHello"

This is because you missed an else in your client, in

   if(buf[i] == 10){
       send(sock , buf , strlen(buf) , 0 );
   //            bzero(buf, sizeof(buf));
       N = 10;
       buf = (char*) realloc (buf, sizeof(char)*N);
       i = 0;
   }
   if (++i >= N) {
       N += delta;
       buf = (char*) realloc (buf, sizeof(char)*N);
   }

you need to replace

if (++i >= N) {

by

else if (++i >= N) {

else after you sent you buffer and set i to 0 you increment it, and you will memorize the next char at the index 1, the character at the index 0 is still present and you will send it again and again


You also have a problem in your client at

send(sock , buf , strlen(buf) , 0 );

because you do not put a null character in buff needed by strlen to return the expected value, so the behavior is undefined. In fact you do not need strlen, just do

 send(sock , buf , i , 0 );

supposing you do not want to send the \n


On your server side

char buffer[1024];
...
valread = read( new_socket , buffer, 1024);
if(valread == 0)
  break;
printf("%s", buffer );

you fill each time buffer with null characters but in case you read 1024 characters there is no null character in your buffer and printf will go out of the buffer with an undefined behavior

warning read returns -1 on error, valread == 0 is wrong

remove all your bzero an just do

 char buffer[1024];
 ...
 while ((valread = read(new_socket, buffer, sizeof(buffer)-1)) > 0) {
   buffer[valread ] = 0;
   printf("%s", buffer);
 }

notice I used sizeof(buffer) rather than 1024, that allows to be sure to have the right size even you resize buffer


Other remarks for the client :

  • the variable hello is useless

  • by definition sizeof(char) values 1, so sizeof(char)*N can be replaced by N everywhere

  • do not compare the read char with the literal 10 and 27, compare with '\n' and '\e'

  • you do not manage the EOF in input, for that you need to save the read char in an int rather than a char (like buf[i] is) to compare it with EOF

In the server the variable step is useless


Out of that you use SOCK_STREAM so your socket is a stream (tcp not udp), that means you cannot suppose the size of the data you read each time you call read, I mean if the client sent N bytes that does not mean the server will read N bytes on the corresponding read (if I can say 'corresponding' because there is no correspondence ;-) ).

Supposing the other problems are fixed if you input azeqsd\n you send azeqsd but may be on the server side you will read azeq so print azeq\n and on the next loop you will read sd and print sd\n.

It is also possible the server read a partial or full concatenation of several buffers sent separably by the client.

Do you want that behavior ? if no you need to send the size before each buffer to know how much to read even on several times to constitute the full sent buffer (an other advantage is you no not read byte per

Upvotes: 2

Related Questions