分别通过TCP套接字发送和接收字符串
我有一个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)我应该发送()和recv()吗? 在我的代码的其他一些部分,它似乎是自我调节,只在正确的时间接收正确的字符串,而我不必检查。 这样做有用吗?
3)关于发送一个标题我将要发送的字符串的长度,如何解析传入的字符串以知道在哪里拆分? 任何例子都会很棒。
4)我肯定会检查我是否收到比缓冲区更大的东西,谢谢你的指点。
编辑2:
5)因为我总是把东西放在大小为512的缓冲区字符数组中。即使我发送1个字母的字符串,每个发送操作都不应该占用缓冲区的全部大小吗? 这令人困惑。 我有“John”,但我把它放在一个大小为512的数组并发送它,不应该填满发送缓冲区,所以recv()函数通过拉动整个数组来清空网络缓冲区512?
首先,当客户端调用recv(*socket, buffer, sizeof(buffer), 0);
,它将从网络接收最多 sizeof(缓冲)字符(此处为512)。 这是来自网络缓冲区,与服务器上调用了多少次无关。 recv
不会查找\0
或任何其他字符 – 它是来自网络缓冲区的二进制拉取。 它将读取整个网络缓冲区或sizeof(buffer)
字符。
您需要检查第一个recv
以查看它是否同时具有名字和姓氏并进行适当处理。
您可以通过让服务器将字符串的长度作为流的前几个字节(“标题”)发送,或者通过扫描接收的数据来查看是否有两个字符串。
每次调用recv
时都需要执行检查 – 如果字符串超过512字节会怎样? 然后你需要多次调用recv
获取字符串。 如果你得到第一个字符串,只有第二个字符串的一半怎么办? 通常这两种情况只会在处理大量数据时发生,但我之前已经看到它发生在我自己的代码中,因此即使在处理像这样的小字符串时也最好对recv
进行良好的检查。 如果你在早期练习良好的编码策略,你将不会有太多的麻烦。
正确的做法是用标题包含发送的数据。 因此,您将传递消息的大小,包括消息的大小,然后在另一方面,您可以确定消息的长度。
约翰是4个字符,所以你有长度,像“0004John”,然后你会知道什么时候停止阅读。 您可能还想在标题中添加命令,这很常见。 例如,名字可能是1,第二名是2;
例如,“01004JOHN”将被设置在缓冲区中并被发送。
要执行此操作,请将项添加到缓冲区,然后按添加的值的大小增加缓冲区中的插入点。 您可以使用它将许多部件添加到缓冲区。 *请确保不要超出缓冲区的大小。
记住这些给出的例子是不正确的,因为你没有传递数字的字符串表示,只是数字,所以它将是一个long int 1,long int 4然后是chars,或unicode,无论如何,4个字符。
TCP是面向流的,而不是面向数据报的。 如果是后者,你的东西会很好用。 您有以下选择:
- 使用SCTP
- 如果你能承受失去的可靠性,请使用UDP
- 以您可以使用的方式分隔字符串,可以通过预先设置长度标记,也可以通过添加
NUL
字节来终止每个字符串。
对于后者,你只需要send(*socket, buffer, strlen(buffer)+1, 0);
为了包括这里的NUL
,发送者将重复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';
。
而不是sizeof(缓冲区)使用strlen(fn); 因此它只会传输确切的字节数量。如果你需要使用null终结符一起使用strlen(fn)+1,但不要忘记将null终结符与strcat()连接起来
如果你发送所有的缓冲区大小而不用memset清理你也会有垃圾,所以要小心..