TCP C套接字服务器端到c#客户端,数据包使用send一起进来

本文关键字:send 一起 数据包 套接字 服务器端 客户端 TCP | 更新日期: 2023-09-27 18:02:23

我已经实现了一个C套接字服务器,它是对这里的示例服务器的略微调整:

http://www.tutorialspoint.com/unix_sockets/socket_server_example.htm

它使用第一个示例而没有分叉,因为只有一个连接到它。我所做的更改是将bzero和read命令移动到一个单独的函数中,然后在主函数中,我在do/while循环中包装对该单独函数的调用。我还将读取命令更改为recv。因此,它看起来像这样,伪代码:

int main( int argc, char *argv[] )
{
  ... (code from the sample code I linked to above) ...
  if (newsockfd < 0) {
    perror("ERROR on accept");
    exit(1);
  }
  puts("Connected.");
  do{       
    res = doStuff(newsockfd);   
  } while(res > 0);
  puts("Disconnected.");
  close(newsockfd); 
  ...
}
int doStuff(int socket){
  ...
  int n;
  uint8_t *buf = (uint8_t *) malloc(4);
  uint8_t *packet;
  bzero(buf, 4);
  n = recv(socket, buf, 4, 0);
  if(n<=0) { return(n); }
  (... do stuff, including creating a packet of a certain size ...)
   if (send(socket, packet, packetLength, MSG_DONTWAIT) == -1) {
      puts("Error with send");
      return(-1);
   }
   return(1);
}

好消息是,这在大多数情况下都有效。我能够从我的c#客户端连接,发送多条消息,并获得消息返回到客户端。我正在从c#客户端向c套接字服务器发送大量请求消息,一个接一个。

坏消息是,有时它不工作,看起来像是C服务器发送一些数据包给C客户端,C客户端将它们一起添加到一个数据包中。

例如,我从客户端连续向C服务器发送了三条消息,请求不同字节大小的响应。C服务器接收请求并根据屏幕上的打印输出以正确的顺序将它们发送回来。例如,它是这样说的:"收到12字节的请求。发送回12字节。收到5字节的请求。发送回5个字节。收到的请求为18字节。回送18字节。"

所以C服务器应该收到3个独立的数据包,一个有12个字节,一个有5个字节,一个有18个字节。相反,它得到一个35字节的数据包,即12+5+18,然后它输出一个错误,因为它期望接下来是一个12字节的数据包。在c#端,使用receive().

谁能给我一个线索,我可以看看调试这个?谢谢你!

编辑添加:来回发送的数据包大小不同,但始终是十六进制字节数组(如0x00, 0x42, 0x01等)

TCP C套接字服务器端到c#客户端,数据包使用send一起进来

您必须通过TCP向协议添加帧。没有人能保证发送的大小与接收的大小匹配。发送的10个字节可以接收为10乘以1字节,发送的10乘以1字节可以接收为7字节和3字节。

向协议添加帧是微不足道的。您可以使用传统方法,使用长度头(在每个消息之前使用int,宣布消息长度)。或者您可以使用protobufs,它有很多优点。

TCP不是数据包特定处理的正确选择,它适用于流处理。要执行应用程序级帧(这是对"数据包"概念的模糊描述),您需要在客户端上仅读取预期大小,处理它,然后读取下一个预期大小,继续。

您看到的问题是TCP的预期行为。TCP试图在传输时尽可能高效,因此它会尽可能将多个写操作收集到一个数据包中进行传输。

您显然还没有看到(但将来会看到)的问题是碎片问题。当您试图在一次写入中发送超过最大传输单元(MTU)大小时,就会发生这种情况。例如,如果您写入2000字节,而MTU大小为1500,则接收端将收到一个1500字节的数据包,然后是一个500字节的数据包。

这个故事的寓意是,接收者必须准备好通过多次读取和从一次读取中提取多个消息来重建消息。为此,您需要实现一个协议,该协议允许您从字节流中提取单个消息。