简体   繁体   English

套接字编程read()正在读取我的所有写入()

[英]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. 我的客户端有两个read(),服务器代码有两个write()。 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). 服务器在第一次write()时向客户端发送数据,客户端读取并存储到缓冲区但它不会停止读取,它会一直读取服务器的第二次write(),因为在我的客户端我将它设置为在流中读取255(根据我的理解)。 I put 255 because i don't know how long the data datasize for first write() is. 我把255放了,因为我不知道第一个write()的数据数据有多长。 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. 您正在经历的是TCP的工作原理。 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. 如果服务器在客户端调用read()之前多次调用write() ,则read()可以接收先前写入的所有内容,最多可达到指定的最大缓冲区大小。 TCP has no concept of message boundaries, like UDP does. TCP没有消息边界的概念,就像UDP那样。 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): 使用对数据有意义的任何分隔符(STX / ETX,换行符,特殊保留字符等):

    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. TCP是字节流协议。 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. read()可以读取少至一个字节和您提供的长度,具体取决于已到达的数据,您无法在发送端或接收端控制这些数据。 You also can't control whether TCP coalesces outgoing writes into one segment. 您也无法控制TCP是否将传出写入合并到一个段中。

If you want messages, you have to implement them yourself, eg lines, length-word prefix, type-length-value, STX/ETX, XML, ... 如果你想要消息,你必须自己实现它们,例如行,长度字前缀,类型长度值,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'. 在这种情况下,调用'perror()',或者用'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. 你已经得到了有关发送长度正确的想法data发送实际的之前data ,但是你发送datasize格式错误。 Sending it as an ascii string means the length of datasize will vary depending on the length of data : 发送它作为一个ASCII字符串表示的长度datasize将根据的长度变化data

For instance: 例如:

  • If data is 5 bytes in length, datasize will be "5". 如果data长度为5个字节,则datasize将为“5”。
  • If data is 100 bytes in length, datasize will be "100". 如果data长度为100个字节,则datasize将为“100”。

Unfortunately when it comes to serializing data , this just won't work, datasize must always take up the same number of bytes. 不幸的是,当涉及到序列化data ,这不起作用, datasize必须总是占用相同的字节数。 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. 套接字==字节流,它不会使一些打包等等。因此,如果服务器应该向客户端发送2个数据包,您需要做一些事情,以便客户端可以区分它们。 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: 例如,如果您确定服务器应该每个发送2个数据包255个字节,那么接收一个数据包的客户端过程将如下所示:

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. 该程序具有循环,因为您可以在0..255之间接收读取结果中的任何数字,或者如果套接字已关闭则接收为负数。 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. 您可以在前2个字节中发送数据包的长度,并在上面的代码中使用数字而不是255常量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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