umbreLLaJYL
umbreLLaJYL

Reputation: 185

TCP/IP client send

the following is my C/S code and output just for test.
I have trouble on it.
it is clearly that I just send twice via client. but, there are 3 messages the server just get. and the second one seems like a NULL.

#define MP_MAXLINE 4096    
/* client */
int main(int ac, char *av[])
{
    char buf[MP_MAXLINE];
    int clifd;
    struct sockaddr_in cliaddr;

    if((clifd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        perror("socket error");

    memset(&cliaddr, 0, sizeof(struct sockaddr_in));
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(atoi(av[2]));
    inet_pton(AF_INET, av[1], &cliaddr.sin_addr);

    if(connect(clifd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)))
        perror("can not connect to server");

    printf("enter data to send: ");
    while(fgets(buf, MP_MAXLINE, stdin) != NULL){
        write(clifd, buf, sizeof(buf));
        printf("enter data to send: ");
    }
}

/* server */
static void cli_service(int clifd){
    int lclifd, rcv_cnt = 0;
    char data[MP_MAXLINE];

    for(;;){
        if(read(clifd, data, sizeof(data)) == 0)
            exit(1);
        rcv_cnt++;
        printf("cnt: %d, data: %s", rcv_cnt, data);
    }
}

int main(int ac, char *av[])
{
    ...

    for(;;){
        clifd = accept(svrfd, (struct sockaddr *)&cliaddr, &clisocklen);

        if((clipid = fork()) < 0){
            printf("fork error\n");
            continue;
        }
        else if(clipid == 0){
            close(svrfd);
            cli_service(clifd);
        }
        close(clifd);
        continue;
    }
}

the output of client is here:
enter data to send: 1111
enter data to send: 2222
enter data to send:

the output of server is here:
cnt: 1, data: 1111
cnt: 2, data: cnt: 3, data: 2222

Upvotes: 0

Views: 616

Answers (1)

Jeremy Friesner
Jeremy Friesner

Reputation: 73294

What's happening is that you are about to discover that TCP is a stream-oriented protocol, which means that it guarantees that the bytes you send will be delivered correctly and in-order, but the mapping of bytes to individual write()/read() calls is arbitrary -- that is, number of bytes that are written into the receiver's array by each call to read() is definitely not guaranteed to be the same as the number of bytes that were previously passed to a write() call by the sending program.

So it appears that in your client, each call to write() is sending 4096 bytes, the first few of which will contain the 0-terminated text string the user typed in, and the rest of which will be uninitialized data (i.e. it could be anything because you never wrote to that part of the array).

Then in your server, you are calling read(), which reads in some number of bytes, but you don't know how many bytes it read in because all you do is check whether read() returned 0 or not. So you don't know if read() placed 4096 bytes of data into your data array or some smaller number of bytes; and if it was some smaller number, then your receiving code is now out-of-sync with your sending code, in that subsequent calls to read() will likely write the uninitialized data to the start of your data array, and the user's next string somewhere in the middle of the array.

That is, of course, not useful behavior. What you'll need to do in order to fix it is pay close attention to the value returned by read(); and if it's greater than zero but less than sizeof(data) you'll need to handle that somehow. One way to handle it would be to keep a count of how many valid bytes are currently in your data array, and always read(clifd, data+numValidBytes, sizeof(data)-numValidBytes) until (numValidBytes == sizeof(data))... and only then parse the data and set numValidBytes back to zero. Another, easier option would be to call recv() instead of read(), and set MSG_WAITALL in the last parameter -- in that case, recv() will promise not to return until it has read all sizeof(data) bytes, and then you don't have to deal with partial-reads yourself.

Upvotes: 3

Related Questions