Khaled
Khaled

Reputation: 8573

C Socket Programming, 1 Client read reads multiple writes

In my code, the client will initiate a request to server, upon so, server will keep sending data to client until a special output is presented (say "END") then client will stop reading. Here's some code (just an example):

/**Client**/
char req[] = "some-request";
write(socket,req,strlen(req));  
while(1) 
{
    read(socket,readline,100);
    strtok(readline, "\n");
    if(strcmp(readline,"END") == 0){ //if end of sending
        break;
}   

/** HANDLE OUTPUT **/
}

/** Server **/

int data[] = {5532,127,332,65,723,8,912,2,421,126,8,3,2}
int i;

for(i = 0 ; i < sizeof(data); i++) 
{
     write(socket, data[i], sizeof(data[i]);
}

write(socket, "END", 3);

This code works fine, but due some context switching in processes, the server writes twice before the client reads, so the client reads two lines at once, say:

5532127

332

65723

8912

2421

1268

32END

As seen, sometimes 2 or more writes are grouped into 1 read, and as a result of course the END isn't handled as it is combined..

I tried using usleep() function, which works most of the time, but still isn't a solution. So any idea how I make sure the server doesn't write to buffer if it is not empty?

Upvotes: 0

Views: 1229

Answers (2)

Serge Ballesta
Serge Ballesta

Reputation: 148870

I can see some problems in your code.

You use a strtok(readline, "\n"); client side while sending raw int server side. It means that in one of the sent integers is 0x..10 or 0x10.. it will be changed to (resp.) 0x..00 or 0x00... Never use strxx functions on raw bytes !

Next, you are using stream oriented sockets. You can have no assurance that packets send will not be grouped or splitted. So you should collate everything in the receiver until you have 3 consecutive chars containing END. But you cannot use strxx function for that ...

Finally, you transfer raw integer, and wait for 3 characters END. But if you try to send {12, 17743, 17408, 13} (on a big-endian system) you will have a major problem : 17743 = 0x454F, and 17408 = 0x4400 => you have exactly "END" ! On a little endian, the magic values would be 20293 = 0x4F45 and 68 = 0x0044.

The rules are :

  • in you want to transer binary data, you should use a lenght + packet pattern because any pattern can happen in raw data. And a good practice is to convert all data longer than one byte to network order (htonx functions) to avoid endianness problems
  • if you want to use delimiters, you should only use character data, ensuring (via escaping or ...) that the end pattern cannot occur in data.

In you use case, you should simply tranfer textual representation, converting integer using sprintf, separating them with space, end terminating with \n.

Upvotes: 1

domen
domen

Reputation: 1908

You are using a stream oriented socket. The server can't know if client has already received the data or not, and there's not really any difference between your multiple writes in a loop, or just one write which sends the whole array.

You will need to use something packet oriented, or have a way to figure out what are your "packets". As you've figured out, you can't rely on timings, so you'll need to define some sort of protocol. Something simple would be to just read the ints*, and have a special value as end.

Btw. your code also has a couple of issue with number representation. You need to used a fixed length type (int can be 16 or 32-bit, so use something like uint32_t), and you need to be aware of endianess issues (should probably use htonl/ntohl or similar).

Upvotes: 1

Related Questions