簡體   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