繁体   English   中英

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

[英]Socket programming read() is reading all of my writes()

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

客户:

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

服务器:

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

您正在经历的是TCP的工作原理。 如果服务器在客户端调用read()之前多次调用write() ,则read()可以接收先前写入的所有内容,最多可达到指定的最大缓冲区大小。 TCP没有消息边界的概念,就像UDP那样。 没有什么不妥。 你只需要考虑它,就是这样。

如果您需要知道一条消息的结束位置和下一条消息的开始位置,那么您只需要构建消息框架。 有几种不同的方法可以做到这一点。

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

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

如果你想要消息,你必须自己实现它们,例如行,长度字前缀,类型长度值,STX / ETX,XML,......

注意当您收到错误时,请不要只打印您自己设计的消息。 打印错误。 在这种情况下,调用'perror()',或者用'strerror'组成一个带格式的字符串。

你已经得到了有关发送长度正确的想法data发送实际的之前data ,但是你发送datasize格式错误。 发送它作为一个ASCII字符串表示的长度datasize将根据的长度变化data

例如:

  • 如果data长度为5个字节,则datasize将为“5”。
  • 如果data长度为100个字节,则datasize将为“100”。

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

例如:

#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]);
}

套接字==字节流,它不会使一些打包等等。因此,如果服务器应该向客户端发送2个数据包,您需要做一些事情,以便客户端可以区分它们。 例如,如果您确定服务器应该每个发送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

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

暂无
暂无

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

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