2012-12-01 360 views
3

我有一个TCP服务器和客户端,并带有已建立的套接字。比方说,我有以下情况:分别通过TCP套接字发送和接收字符串

SERVER:

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个字母串?这是令人困惑的。我有“约翰”,但我把它放在一个大小为512的数组中,发送它,不应该填满发送缓冲区,所以recv()函数会清空网络缓冲区,同时拉动整个数组的大小512?

+0

你跳过写一个客户机/服务器应用程序的非常拳头一步 - 设计客户端和服务器用于交换信息的协议。如果你从来没有设计过一个协议,那么你就不能实现一个协议,然后你的代码只能运行,如果它工作的话,那么运气好。显然你想发送消息,但TCP不是消息协议。那么你使用的是什么消息协议? –

回答

8

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

您需要检查第一个recv以查看它是否同时具有姓氏和名字,并对其进行适当处理。

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

无论何时调用recv,您都需要执行检查 - 如果字符串长度超过512字节,该怎么办?那么你需要多次拨打recv才能得到字符串。而如果你得到第一个字符串,只有第二个字符串的一半呢?通常这两种情况只会在处理大量数据时才会发生,但我以前在我自己的代码中看到过这种情况,所以最好在recv中包含好的检查,即使在处理这样的小字符串时也是如此。如果你尽早练习良好的编码策略,你就不会有太多令人头疼的事情。

+1

另外,如果字符串足够长,甚至有可能单个读取不会返回整个字符串。接收逻辑必须考虑到这一点, – mac

+0

好点,@mac,我已经更新了我的答案。 – cegfault

2

正确的做法是将发送的数据与标题放在一起。所以你可以传递消息的大小,包括消息的大小,然后在另一边你可以计算消息的长度。

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

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

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

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

+0

如果你有一个ASCII协议,字符串表示非常好。 – glglgl

+0

如何在发送字符串之前发送一个long int,send()只接受一个(void *)作为第三个参数。另外,我不明白的是sizeof()应该已经在照顾这个尺寸明智的否?我发送一个8字节的字符串,不应recv()读取8个字节并停止? – user1202888

+0

你不发送它之前,你发送缓冲区,你只需填充缓冲区,所以你可以使用指针算术移动缓冲区插入点。这里是一个示例问题的想法http://stackoverflow.com/questions/11680309/c-buffer-combining-adding-extra-empty-values你只需添加值,然后提前您刚刚添加到项目的大小缓冲区 – nycynik

2

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

0

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

如果您发送所有缓冲区大小没有与memset的清洁你也有垃圾一起,所以要小心的..

0

对于socket流是使用某种消息帧(也称为长度前缀取景)一个很好的做法。

或者使用一个库,像nanomsgzeromq

libuv是基于流的,但我们可以用libuv_message_framing它。

或协议一样SCTPWebsocket

您也可以发送前串行数据。这可以用Binnnetstrings来完成:

4:John,3:Doe, 
相关问题