C语言 套接字编程 read() 正在读取我所有的 writes()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19127398/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:36:04  来源:igfitidea点击:

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

csockets

提问by user2644819

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?

我有一个客户端和一个服务器。我的客户端中有两个 read() ,我的服务器代码中有两个 write() 。服务器在第一次 write() 时向客户端发送数据,客户端读取并存储到缓冲区但它不会停止读取,它继续通过服务器的第二次 write() 读取,因为在我的客户端中我已将其设置为在流中读取 255(根据我的理解)。我放了 255,因为我不知道第一次 write() 的数据数据大小是多长。我该如何解决?

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");

回答by Remy Lebeau

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.

您正在体验的是 TCP 的工作方式。如果服务器write()在客户端调用之前多次调用read(),则read()可以接收先前写入的所有内容,直至您指定的最大缓冲区大小。TCP 没有消息边界的概念,就像 UDP 一样。没有什么不妥。你只需要考虑它,仅此而已。

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);
    
  1. 在发送实际数据之前发送数据长度,这样客户端就知道要读取多少数据,例如:

    服务器:

    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");
    

    客户:

    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. 用实际数据中没有出现的分隔符包装数据,然后客户端可以继续阅读并查找那些分隔符。使用对您的数据有意义的任何分隔符(STX/ETX、换行符、特殊保留字符等):

    服务器:

    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");
    

    客户:

    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);
    

回答by user207421

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.

您不能假设一次读取将完全读取一次写入所写入的内容。TCP是字节流协议。没有消息边界。read() 可以读取最少一个字节和您提供的长度,具体取决于到达的数据,您无法在发送端或接收端控制这些数据。您也无法控制 TCP 是否将传出写入合并到一个段中。

If you want messages, you have to implement them yourself, e.g. 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' 组成一个格式化的字符串。

回答by goji

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

data在发送实际 之前data,您对发送长度有正确的想法,但是您datasize以错误的格式发送。将其作为 ascii 字符串发送意味着 的长度datasize将取决于 的长度data

For instance:

例如:

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

Unfortunately when it comes to serializing data, this just won't work, datasizemust 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:

不幸的是,当涉及到序列化时data,这将不起作用,datasize必须始终占用相同数量的字节。您需要将其作为整数写入套接字,并在另一端作为整数再次读取。然后将这个确切字节数的数据写入套接字并在另一端读取这个确切字节数的数据:

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] = '
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
'; printf("%u %s\n", len, msg); } int main(int argc, char** argv) { int sockets[2]; pipe(sockets); send(sockets[1]); receive(sockets[0]); }

回答by barmatat

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:

Socket == 字节流,它不会进行一些打包等。所以如果服务器应该向客户端发送 2 个数据包,您需要做一些事情以便客户端可以区分它们中的每一个。例如,如果您决定服务器应该发送 2 个数据包,每个数据包 255 个字节,那么接收一个数据包的客户端过程将如下所示:

##代码##

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.

该程序具有循环,因为您可以在读取结果中接收 0..255 之间的任何数字,如果套接字已关闭,则为负数。你可以对第二个数据包做同样的事情。如果您的数据包大小不同,那么您必须从服务器告诉客户端数据包的大小是多少。您可以发送数据包长度的前 2 个字节,并在上面的代码中使用数字而不是 255 常量。