4

我有一个 TCP 服务器和客户端,有一个已建立的套接字。假设我有以下情况:

服务器:

char *fn = "John";
char *ln = "Doe";

char buffer[512];

strcpy(buffer, fn);
send(*socket, buffer, strlen(buffer), 0);
strcpy(buffer, ln);
send(*socket, buffer, strlen(buffer), 0);

客户:

char *fn;
char *ln;

char buffer[512];

bytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = '\0';
strcpy(fn, buffer);

bytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = '\0';
strcpy(ln, buffer);

printf("%s", fn); 
printf("%s", ln);

预期结果:我想分别接收每个字符串(因为我将它们保存在不同的变量中),而不是第一个 recv() 获得 fn "John" 和 ln "Doe" 连接的 "JohnDoe",程序卡住了第二个 recv()... 因为我已经完成了发送。

我尝试了以下方法: - 将 \0 添加到 fn 和 ln 字符串,如下所示:

char *fn = "John\0";
char *ln = "Doe\0";

但问题依然存在。

我可以施加某种条件,以便单独接收每个字符串吗?

编辑以添加更多问题:

1)为什么在某些情况下实际上是分开发送的,而其他情况下是联合发送的?

2)我应该 send() 和 recv() 交替吗?在我的代码的其他一些部分,它似乎是自我调节的,并且只在正确的时间接收正确的字符串,而无需我检查。这行得通吗?

3)关于发送一个带有我即将发送的字符串长度的标题,我如何解析传入的字符串以知道在哪里拆分?任何例子都会很棒。

4)我一定会检查我是否得到比我的缓冲区更大的东西,谢谢你指出它。

编辑2:

5) 因为我总是把东西放在大小为 512 的缓冲区字符数组中。即使我发送 1 个字母的字符串,每个发送操作是否不应该占用缓冲区的全部大小?这就是令人困惑的地方。我有“John”,但我将它放在一个大小为 512 的数组中并发送它,这不应该填满发送缓冲区,因此 recv() 函数还通过拉取整个数组大小来清空网络缓冲区512?

4

4 回答 4

10

首先,当客户端调用 时recv(*socket, buffer, sizeof(buffer), 0);,它将从网络接收最多sizeof(buffer) 个字符(此处为 512)。send这是来自网络缓冲区,与服务器上调用 了多少次无关。recv不寻找\0或任何其他字符 - 它是从网络缓冲区中提取的二进制文件。它将读取整个网络缓冲区或sizeof(buffer)字符。

您需要先签入recv以查看它是否同时具有名字和姓氏并适当地处理它。

您可以通过让服务器将字符串的长度作为流的前几个字节(“标头”)发送,或者通过扫描接收到的数据来查看是否有两个字符串来做到这一点。

您确实需要在每次调用时执行检查recv- 如果字符串长于 512 字节怎么办?然后你需要recv多次调用 s 来获取字符串。如果你得到第一个字符串,而第二个字符串只有一半怎么办?通常这两种情况只在处理大量数据时发生,但我以前在我自己的代码中看到过这种情况,所以recv即使在处理像这样的小字符串时,最好也加入良好的检查。如果您尽早练习良好的编码策略,那么您以后就不会那么头疼了。

于 2012-12-01T05:39:52.877 回答
2

正确的做法是用标头将发送的数据括起来。因此,您将传递消息的大小,包括消息的大小,然后在另一端您可以计算出消息的长度。

John 是 4 个字符,所以你有长度,比如“0004John”,那么你就会知道什么时候停止阅读。您可能还想在标题中添加一个命令,这很常见。例如,名字可能是 1,而名字可能是 2;

例如,“01004JOHN”将在缓冲区中设置并发送。

为此,您将项目添加到缓冲区,然后将缓冲区中的插入点增加您添加的值的大小。您可以使用它向缓冲区添加许多部分。*请确保您没有超出缓冲区的大小。

记住我给出的这些例子是不正确的,因为你没有传递数字的字符串表示,只是数字,所以它将是一个 long int 1、long int 4 然后是 chars 或 unicode,无论哪种方式,4 个字符。

于 2012-12-01T05:39:41.300 回答
2

TCP是面向流的,而不是面向数据报的。如果是后者,你的东西会很好用。您有以下选择:

  • 使用SCTP
  • 如果您能承受失去可靠性,请使用 UDP
  • 以您可以使用的方式分隔字符串,方法是预先添加一个长度标记或添加一个NUL字节来终止每个字符串。

对于后者,您只需要send(*socket, buffer, strlen(buffer)+1, 0);包含NULwhich is here,发送者将反复recv()查找字符串终止符,直到找到 2 个字符串。

请注意,您应该注意strcpy(): 只有在您绝对确定要写入的字符串正常时才使用它。

您的接收器可以

bytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = '\0';

这很糟糕,因为如果缓冲区已满,那么bytes == sizeof(buffer)(在更复杂的情况下可能会发生)buffer[bytes] = '\0'写入缓冲区之外。所以使用

bytes = recv(*socket, buffer, sizeof(buffer) - 1, 0);

你可以愉快地做

buffer[bytes] = '\0';

.

于 2012-12-01T05:43:00.617 回答
0

而不是 sizeof(buffer) 使用 strlen(fn); 所以它只会传输确切数量的字节..如果您需要空终止符一起使用 strlen(fn)+1 但不要忘记将空终止符与 strcat() 连接

此外,如果您发送所有缓冲区大小而不使用 memset 清理,您也会有垃圾,所以要小心..

于 2015-07-17T02:32:36.663 回答