C语言 分别通过 TCP 套接字发送和接收字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13656702/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sending and receiving strings over TCP socket separately
提问by user1202888
I have a TCP server and client, with an established socket. Let's say I have the following case:
我有一个 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);
CLIENT:
客户:
char *fn;
char *ln;
char buffer[512];
bytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = 'char *fn = "Johnbytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = 'bytes = recv(*socket, buffer, sizeof(buffer) - 1, 0);
';
";
char *ln = "Doebuffer[bytes] = '##代码##';
";
';
strcpy(fn, buffer);
bytes = recv(*socket, buffer, sizeof(buffer), 0);
buffer[bytes] = '##代码##';
strcpy(ln, buffer);
printf("%s", fn);
printf("%s", ln);
Expected result: I want to receive each string separately (as I am saving them in different variables), instead the first recv() gets both the fn "John" and ln "Doe" concatenated "JohnDoe", and the program gets stuck on the second recv()... since I've already done the sending.
预期结果:我想分别接收每个字符串(因为我将它们保存在不同的变量中),而不是第一个 recv() 获得 fn "John" 和 ln "Doe" 连接的 "JohnDoe",并且程序卡住了第二个 recv()... 因为我已经完成了发送。
I tried the following: - Adding \0 to the fn and ln strings like so:
我尝试了以下操作: - 将 \0 添加到 fn 和 ln 字符串中,如下所示:
##代码##But the problem persisted.
但问题仍然存在。
Is there some sort of condition I can impose so each string is received separately?
我是否可以强加某种条件,以便分别接收每个字符串?
EDIT TO ADD MORE QUESTIONS:
编辑添加更多问题:
1) Why does it in certain instances actually send them separately, and others sends them jointly?
1)为什么它在某些情况下实际上是分开发送的,而其他情况下是联合发送的?
2) Should I send() and recv() alternatively maybe? In some other parts of my code, it seems it is self regulating and only receiving the right strings at the right time without me having to check. Does this do the trick?
2)我应该使用 send() 和 recv() 替代吗?在我代码的其他一些部分,它似乎是自我调节的,并且只在正确的时间接收正确的字符串而无需我检查。这行得通吗?
3) About sending a header with the length of the string I am about to send, how do I parse the incoming string to know where to split? Any examples would be great.
3)关于发送带有我将要发送的字符串长度的标头,我如何解析传入的字符串以知道在哪里拆分?任何例子都会很棒。
4) I will definitely check if I'm getting something bigger than my buffer, thanks for pointing to it.
4)我一定会检查是否我得到的东西比我的缓冲区大,谢谢你指出它。
Edit 2:
编辑2:
5) Since I'm always putting things in a buffer char array of size 512. Shouldn't every send operation take the full size of the buffer even if I am sending a 1 letter string? That's what confusing. I have "John" but I'm putting it in an array of size 512 and sending it, shouldn't that fill up the send buffer, and so the recv() function empties the network buffer by also pulling the whole array of size 512?
5) 因为我总是把东西放在一个大小为 512 的缓冲区字符数组中。即使我发送一个 1 个字母的字符串,难道每个发送操作都不应该占用缓冲区的完整大小吗?这就是令人困惑的地方。我有“John”,但我将它放在一个大小为 512 的数组中并发送它,这不应该填满发送缓冲区,因此 recv() 函数还通过拉动整个大小数组来清空网络缓冲区512?
回答by cegfault
First, when the client calls recv(*socket, buffer, sizeof(buffer), 0);, it will receive up tosizeof(buffer) characters (here, 512) from the network. This is from the network buffer, and has nothing to do with how many times sendhas been called on the server. recvdoes not look for \0or any other character - it's a binary pull from the network buffer. It will read either the entire network buffer, or sizeof(buffer)characters.
首先,当客户端调用 时recv(*socket, buffer, sizeof(buffer), 0);,它将从网络接收最多sizeof(buffer) 个字符(此处为 512)。这是来自网络缓冲区,与send服务器上被调用的次数无关。 recv不寻找\0或任何其他字符 - 它是从网络缓冲区中提取的二进制文件。它将读取整个网络缓冲区或sizeof(buffer)字符。
You need to check in the first recvto see if it has both the first and last name and handle it appropriately.
您需要先签入recv以查看它是否同时具有名字和姓氏并进行适当处理。
You can do so either by having the server send the length of the strings as the first few bytes of a stream (a "header"), or by scanning the received data to see if there are two strings.
您可以通过让服务器将字符串的长度作为流的前几个字节(“标题”)发送,或者通过扫描接收到的数据以查看是否有两个字符串来实现。
You really need to perform checks whenever you call recv- what if the string is longer than 512 bytes? Then you need to call recvmultiple time sto get the string. And what if you get the first string, and only half of the second string? Usually these two cases only happen when dealing with larger amounts of data, but I've seen it happen in my own code before, so it's best to incorporate good checks on recveven when dealing with small strings like this. If you practice good coding tactics early on you won't have as many headaches down the road.
您确实需要在每次调用时执行检查recv- 如果字符串长于 512 字节怎么办?然后你需要recv多次调用s 来获取字符串。如果你得到第一个字符串,而只有第二个字符串的一半怎么办?通常这两种情况只发生在处理大量数据时,但我之前在我自己的代码中看到过这种情况,所以recv即使在处理这样的小字符串时,最好也加入良好的检查。如果您尽早练习良好的编码策略,那么以后就不会遇到那么多麻烦。
回答by nycynik
The right thing to do is to enclose the sent data with a header. So you would pass the size of the message, including the size of the message, then on the other side you can figure out how long the message should be.
正确的做法是用标头将发送的数据括起来。所以你会传递消息的大小,包括消息的大小,然后在另一边你可以计算出消息应该有多长。
John is 4 chars, so you have the length, something like "0004John" then you would know when to stop reading. You may also want to add a command to the header, that is very common. So for instance, first name might be 1, and second name 2;
John 是 4 个字符,所以你有长度,比如“0004John”,那么你就会知道什么时候停止阅读。您可能还想在标题中添加一个命令,这很常见。例如,名字可能是 1,第二个名字可能是 2;
"01004JOHN" for instance would be set in the buffer and sent.
例如,“01004JOHN”将在缓冲区中设置并发送。
To do this, you add items to the buffer, then increment the insertion point in the buffer by the sizeof the values you added. You can use this to add many parts to the buffer. *Just be sure you dont overrun the size of the buffer.
为此,您将项目添加到缓冲区,然后将缓冲区中的插入点增加您添加的值的大小。您可以使用它向缓冲区添加许多部分。*请确保您没有超出缓冲区的大小。
Remember these examples im giving are not correct, because you dont pass the string representation of the number, just the number, so it would be a long int 1, long int 4 and then chars, or unicode, either way, 4 characters.
记住我给出的这些例子是不正确的,因为你没有传递数字的字符串表示,只是数字,所以它会是一个 long int 1,long int 4 然后是字符,或者 unicode,无论哪种方式,4 个字符。
回答by glglgl
TCP is stream-oriented, not datagram-oriented. If it was the latter, your stuff would work great. You have the following options:
TCP 是面向流的,而不是面向数据报的。如果是后者,你的东西会很好用。您有以下选择:
- use SCTP
- use UDP if you can afford losing reliability
- separate the strings in a way you can work with, either by prepending a length tag or so or by adding a
NULbyte for terminating each string.
- 使用SCTP
- 如果您能承受失去可靠性,请使用 UDP
- 以您可以使用的方式分隔字符串,或者通过预先添加长度标记或通过添加一个
NUL字节来终止每个字符串。
For the latter, you would just do send(*socket, buffer, strlen(buffer)+1, 0);for including the NULwhich is here nevertheless, and the sender would repeatedly recv()and find a string terminator until it has found 2 strings.
对于后者,您只需要send(*socket, buffer, strlen(buffer)+1, 0);包含NUL仍然在这里的 ,并且发送者将反复recv()查找字符串终止符,直到找到 2 个字符串。
Note that you should take care with strcpy(): only use it if you are absolutely sure that the string you are writing to is ok.
请注意,您应该注意strcpy(): 只有在您绝对确定要写入的字符串没有问题时才使用它。
Your receiver does
您的接收器可以
##代码##That is bad because, if the buffer is full, so bytes == sizeof(buffer)(which could happen in more complex situations), buffer[bytes] = '\0'writes beyond the buffer. SO use
这很糟糕,因为如果缓冲区已满,那么bytes == sizeof(buffer)(这可能发生在更复杂的情况下),buffer[bytes] = '\0'写入超出缓冲区。所以使用
and you can happily do
你可以愉快地做
##代码##.
.
回答by Willian Mainieri
instead of sizeof(buffer) use strlen(fn); so it will transmit only the exact amount of bytes.. if you need null terminator together use strlen(fn)+1 but don't forget to concatenate null terminator with strcat()
而不是 sizeof(buffer) 使用 strlen(fn); 所以它只会传输确切数量的字节..如果你需要空终止符一起使用 strlen(fn)+1 但不要忘记用 strcat() 连接空终止符
also if you send all the buffer size without cleaning with memset you'll also have rubbish along, so beware that..
此外,如果您发送所有缓冲区大小而不使用 memset 进行清理,您也会有垃圾,所以请注意..

