简体   繁体   中英

Socket programming read() is reading all of my writes()

I have a client and a server. I have two read() in my client and two write() in my server code. The server sends data to the client on the first write(), the client reads and stores to a buffer but it doesn't stop reading, it keeps reading through the server's second write() because in my client i have it set up to read 255 in the stream(from my understanding). I put 255 because i don't know how long the data datasize for first write() is. How do i fix this?

Client:

n = read(sockfd,buffer,255);
if (n < 0) 
     error("ERROR reading from socket");
      printf("%s\n",buffer);

 n = read(sockfd,buffer,255);
if (n < 0) 
     error("ERROR reading from socket");
      printf("%s\n",buffer);

Server:

  n = write(newsockfd,datasize,strlen(datasize));
if (n < 0) error("ERROR writing to socket");

n = write(newsockfd,data,255);
if (n < 0) error("ERROR writing to socket");

What you are experiencing is how TCP works. If the server calls write() multiple times before the client calls read() , then read() can receive everything that was previously written, up to the maximum buffer size that you specify. TCP has no concept of message boundaries, like UDP does. There is nothing wrong with that. You just need to account for it, that's all.

If you need to know where one message ends and the next begins, then you simply need to frame your messages. There are a couple of different ways you can do that.

  1. Send the data length before sending the actual data, so the client knows how much data to read, eg:

    Server:

     int datalen = ...; // # of bytes in data int tmp = htonl(datalen); n = write(newsockfd, (char*)&tmp, sizeof(tmp)); if (n < 0) error("ERROR writing to socket"); n = write(newsockfd, data, datalen); if (n < 0) error("ERROR writing to socket"); 

    Client:

     int buflen; n = read(sockfd, (char*)&buflen, sizeof(buflen)); if (n < 0) error("ERROR reading from socket"); buflen = ntohl(buflen); n = read(sockfd, buffer, buflen); if (n < 0) error("ERROR reading from socket"); else printf("%*.*s\\n", n, n, buffer); 
  2. wrap the data with delimiters that do not appear in the actual data, then the client can keep reading and look for those delimiters. Use whatever delimiters make sense for your data (STX/ETX, line breaks, special reserved characters, etc):

    Server:

     char delim = '\\x2'; n = write(newsockfd, &delim, 1); if (n < 0) error("ERROR writing to socket"); n = write(newsockfd, data, datalen); if (n < 0) error("ERROR writing to socket"); delim = '\\x3'; n = write(newsockfd, &delim, 1); if (n < 0) error("ERROR writing to socket"); 

    Client:

     char tmp; do { n = read(sockfd, &tmp, 1); if (n < 0) error("ERROR reading from socket"); if (tmp != '\\x2') continue; buflen = 0; do { n = read(sockfd, &tmp, 1); if (n < 0) error("ERROR reading from socket"); if (tmp == '\\x3') break; // TODO: if the buffer's capacity has been reached, either reallocate the buffer with a larger size, or fail the operation... buffer[buflen] = tmp; ++buflen; } while (1); printf("%*.*s\\n", buflen, buflen, buffer); break; } while (1); 

You can't assume that one read will read exactly what was written by one write. TCP is a byte stream protocol. No message boundaries. read() can read as little as one byte and as much as the length you provide, depending on what data has arrived, which you can't control at either the sending or the receiving end. You also can't control whether TCP coalesces outgoing writes into one segment.

If you want messages, you have to implement them yourself, eg lines, length-word prefix, type-length-value, STX/ETX, XML, ...

NB When you get an error, don't just print a message of your own devising. Print the error. In this case, call 'perror()', or make up a formatted string with 'strerror'.

You've got the right idea about sending the length data before sending the actual data , but you're sending datasize in the wrong format. Sending it as an ascii string means the length of datasize will vary depending on the length of data :

For instance:

  • If data is 5 bytes in length, datasize will be "5".
  • If data is 100 bytes in length, datasize will be "100".

Unfortunately when it comes to serializing data , this just won't work, datasize must always take up the same number of bytes. You need to write this into the socket as an integer, and read it again at the other end as an integer. Then write this exact number of bytes of data into the socket and read this exact number of bytes of data at the other end:

For example:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

void send(int sock)
{
    const char* msg = "this is a message!";
    uint16_t len = strlen(msg); 
    uint16_t networkLen = htons(len); // convert to network byte order

    write(sock, &networkLen, sizeof(networkLen));
    write(sock, msg, len);    
}

void receive(int sock)
{
    char msg[1024];

    uint16_t networkLen;
    read(sock, &networkLen, sizeof(networkLen));

    uint16_t len = ntohs(networkLen); // convert back to host byte order
    read(sock, msg, sizeof(msg) - 1);

    msg[len] = '\0';

    printf("%u %s\n", len, msg);
}

int main(int argc, char** argv)
{
    int sockets[2];
    pipe(sockets);

    send(sockets[1]);
    receive(sockets[0]);
}

Socket == stream of bytes, it doesn't make some packetizing etc. So if Server should send 2 packets to client, you need to do something so client can distinguish each of them. For instance, if you decide that server should send 2 packets by 255 bytes each, your client procedure which receives one packet would look like this:

int count = 0;
while (count < 255) {
    n = read(sockfd, buffer + count, 255 - count);
    if (n < 0) {
         error("ERROR reading from socket");
          printf("%s\n",buffer);
         return;
    } 
    count += n;
}
// here buffer has 255 bytes for the packet

The program has cycle, because you can receive any number in read result in between 0..255, or negative if the socket was closed. you can do the same for second packet. If your packets are different in size, then you have to tell client from server what the packet size is. You can send in first 2 bytes the length of your packet, and use the number instead of 255 constant in code above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM