3

Possible Duplicate:
What is the correct way of reading from a TCP socket in C/C++?

I'm trying to develop a TCP client/server. My problem is, when I try to send the data from cliente I do it in one sent.

But my problem appears when I try to receive the data with a specific structure, I mean, the first 8 bytes set a date, the next 10 a name, and undefined number of bytes set a text (this text ends with /r/n/r/n)

The client sends as follows:

char date[8];
char name[10];
char msg[4096];

strcpy(msg,"12/10/12"); //8 bytes
strcat(msg,"Kevin Fire"); //10 bytes
strcat(msg,"abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde\r\n\r\n");

nbytes_sent = send(sock,(char *)msg,sizeof(msg),0);
printf("Bytes_sent: %s -> %i\n",msg,nbytes_sent);

And the server try to parse the data from socket as follows:

char date[8];
char name[10];
char * text;
char buf[1024];

int i=0;
for(i=0; i < 8; i++)
    date[i] = '\0';
for(i=0; i < 10; i++)
    name[i] = '\0';

nbytes_read=recv(sclient,(char *)date,sizeof(date),0);
if(nbytes_read > 0){
    printf("Date: %s (%i)\n",date,nbytes_read);
    //cout.flush();
    nbytes_read=recv(sclient,(char *)name,sizeof(name),0);
    if(nbytes_read > 0){
        printf("Name: %s (%i)\n",name,nbytes_read);
        //cout.flush();
        nbytes_read=recv(sclient,(char *)buf,sizeof(buf),0);
        strcpy(text,buf);
        while(nbytes_read > 0){
            nbytes_read=recv(sclient(char*)buf,sizeof(buf),0);
            strcat(text,buf);
        }
    }
}

printf("Date: %s. Name: %s. Text: %s\n",date,name,text);
Community
  • 1
  • 1
Joe Lewis
  • 948
  • 5
  • 18
  • 34
  • Let me guess, one or all of the `recv` calls doesn't return the exact amount you ask for? Or that the strings you print out aren't properly terminated? – Some programmer dude Oct 02 '12 at 14:13
  • Don't just tag everything that flits into your head willy nilly. There is absolutely no reason for UDP to be tagged, and you should tag either C or C++ unless you have a very specific reason for tagging both. – Wug Oct 02 '12 at 14:13
  • Is this supposed to be C or C++ (tagged with *both*) – WhozCraig Oct 02 '12 at 14:13
  • And can we see what the definition of nbytes_received is? 4.2-billion bonus points if its an unsigned int. – WhozCraig Oct 02 '12 at 14:14
  • Possible duplication of: http://stackoverflow.com/questions/666601/what-is-the-correct-way-of-reading-from-a-tcp-socket-in-c-c – grieve Oct 02 '12 at 14:20
  • @JoachimPileborg That's exactly what happens. It seems that doesn't receive the correct data, or the exact amount I ask for... for example the server receive: Date: :10:12Ke (8) Name: vin Fire@ (10) and it doesn't retrieve anything in Text.... :/ – Joe Lewis Oct 02 '12 at 16:17
  • 1
    TCP is a streaming protocol, it means that you may not always get all you ask for in one single `recv` call, but have to call `recv` multiple times to get all. There are many examples of loops or functions for this on the Internet, or even here on SO. – Some programmer dude Oct 02 '12 at 17:03
  • Please @JoachimPileborg give me some references to that loop, because I'm gettin mad and I don't have so much time and try to test everything but it's tough for me – Joe Lewis Oct 02 '12 at 18:24
  • @JoeLewis: If you want some sample code, look at the question I listed as a duplicate. – grieve Oct 02 '12 at 19:29

3 Answers3

6

Here's a simple "receive all" function:

int recv_all(int sockfd, void *buf, size_t len, int flags)
{
    size_t toread = len;
    char  *bufptr = (char*) buf;

    while (toread > 0)
    {
        ssize_t rsz = recv(sockfd, bufptr, toread, flags);
        if (rsz <= 0)
            return rsz;  /* Error or other end closed cnnection */

        toread -= rsz;  /* Read less next time */
        bufptr += rsz;  /* Next buffer position to read into */
    }

    return len;
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

One (repeated) mistake is:

nbytes_read=recv(sclient,(char *)date,sizeof(date),0);

recv() does not null terminate. This means date will not have a null terminator if sizeof(date) bytes is read. This is a problem when a non-null terminated string is passed as an argument to printf() with "%s" format specifier. If the string is non-null terminated you may see garbage characters appearing after the actual string data. You need to read one less than the target buffer and null terminate or use the format specifier "%*.s" that does not require null termination:

printf("%.*s", n, s); /* Prints first 'n' bytes from 's'. */

Note you can initialise a char[] to all nulls instead of using a for:

char date[8] = "";

or you can use memset().

hmjd
  • 120,187
  • 20
  • 207
  • 252
1

Adding to @hmjd's find:

declared at the var decls is your text pointer...

char * text;

then later...

strcpy(text,buf);
while(nbytes_read > 0){
   nbytes_read=recv(sclient(char*)buf,sizeof(buf),0);
   strcat(text,buf);
}

Maybe try setting that 'text' pointer to something beside a random stack value will help as well.

Continuing the barrage, though the following will not necessarily blow up, your date variable as:

char date[8];

on both client and server side The client variable isn't used at all. The server variable, however is:

nbytes_read=recv(sclient,(char *)date,sizeof(date),0);
if(nbytes_read > 0){

Problem is, the date you sent is, in fact, 8 chars wide already: "12/10/12". Therefore, even if you firm-up a null terminator on the end of your string, which you should always do regardless (good practice):

date[ sizeof(date)/sizeof(date[0])-1 ] = 0;

you'll be truncating off the last char of your date.

There are other things wrong with this; we've only pointed out a few. Think about sending length-prefixes with each of these data values in the array, with checks or range to ensure you get what you expected.

Finally, spending some time on the business-end of a debugger would probably do you very well, especially on the server side.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141