简体   繁体   English

使用C ++进行的Unix套接字编程,recv返回0,但仍接收数据,但有时接收的内容比发送的内容多

[英]Unix socket programming in C++, recv returning 0, but still receiving data, but sometimes receives more than what is sent

I am new to C++ and socket programming. 我是C ++和套接字编程的新手。 I studied with Beej's guide so my codes are almost same as the guide, but I am struggling really strange bugs. 我研究了Beej的指南,因此我的代码与该指南几乎相同,但是我正在努力解决一些非常奇怪的错误。

First, my server's recv() returns 0. According to document, the client should gracefully close the connection for recv() to return 0. Not really in my case. 首先,我的服务器的recv()返回0。根据文档,客户端应正常关闭recv()的连接以返回0。 It returns 0, at the same time, I still receive the data from the client. 它返回0,同时,我仍然从客户端接收数据。 So, the way Beej's do to receive, does not work for me. 因此,Beej的接收方式对我不起作用。 Can someone explain how this can be possible? 有人可以解释这怎么可能吗?

char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0); 
buf[numbytes] = '\0';

the last line here, because numbytes is 0, it sweeps out all message I received. 最后一行,因为numbytes为0,它将清除我收到的所有消息。 So I had to comment that out. 因此,我不得不对此进行评论。 Now, my code looks like this 现在,我的代码看起来像这样

char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0); 
//buf[numbytes] = '\0';
printf("received: %s\n", buf); 

It now works with receiving some messages sent by client. 现在,它可以接收客户端发送的某些消息。 However, I did some string manipulation (appending) in the client side, and then sent the message. 但是,我在客户端进行了一些字符串操作(附加),然后发送了消息。 Now, I send string length of 29 in the client side, but the server receives 41 bytes with strange characters. 现在,我在客户端发送长度为29的字符串,但是服务器接收到41个字节的奇怪字符。 What I sent: received: Login#1 Mary 123456 451912345 received: Login#1 Mary 123456 451912345ÿ>É„ÿy@ÿ>Ád 我发送的内容:收到:登录#1玛丽123456 451912345收到:登录#1玛丽123456451912345ÿ>É„ÿy@ÿ>Ád

Here is how I receive in the server: 这是我在服务器中收到的信息:

while(1) { // main accept() loop

    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
    if (new_fd == -1) {
        perror("accept");
        continue;
    }
    char buf[MAXDATASIZE];
    int numbytes;
    if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1) 
    perror("recv");
    //buf[numbytes] = '\0'; // this had to be commented out
    printf("received: %s\n", buf); // prints out with weird characters

    string msgRcved = buf;

        close(new_fd); 
}

This is how I send from client: 这是我从客户端发送的方式:

// string loginCredential is loaded with "1 Mary 123456 451912345" at this point
loginCredentials.insert(0, "Login#");
const char* msgToSend = loginCredentials.c_str();
int numbytesSent;
if (numbytesSent = send(sockfd, msgToSend, strlen(msgToSend), 0) == -1)
    perror("send");

I'd like to know how my recv receives data while it returns 0 at the first place. 我想知道我的recv如何在第一次返回0时接收数据。 And, I'd like to know what I am doing wrong to recv data from client/send data to server. 而且,我想知道从客户端接收数据/将数据发送到服务器时我做错了什么。

You have a precedence problem. 您有优先权问题。

This: 这个:

if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1) 

is equivalent to 相当于

if (numbytes = (recv(new_fd, buf, MAXDATASIZE-1, 0) == -1))

and

recv(new_fd, buf, MAXDATASIZE-1, 0) == -1

is 0 whenever recv succeeds. recv成功时为0。

The same problem is present on the sending end. 发送端存在相同的问题。

There's no reason to write such awkward and error-prone condition. 没有理由编写这种尴尬且容易出错的条件。
This is safer: 这更安全:

int numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0);
if (numbytes == -1) 
    perror("recv");

You have to test 'numbytes' for zero, separately, and if you get it close the socket and exit the read loop, because the peer has closed the connection. 您必须分别测试'numbytes'是否为零,如果得到它,则关闭套接字并退出读取循环,因为对等方已关闭连接。 Otherwise, and assuming you have also tested for -1, you have to only process 'numbytes' bytes of the buffer. 否则,假设您还测试了-1,则只需处理缓冲区的“ numbytes”个字节。 Not all of them. 不是所有的人。 Otherwise you're liable to reprocess bytes you already processed. 否则,您可能会重新处理已经处理过的字节。 In this case that might mean restoring the line that null-terminated the buffer, or it might mean this: 在这种情况下,这可能意味着恢复以null结束缓冲区的行,或者这可能意味着:

printf("%.*s", numbytes, buf);

You are printing whatever garbage was in that stack-allocated buffer, not what the client sent. 您正在打印该堆栈分配缓冲区中的所有垃圾,而不是客户端发送的垃圾。 When recv(2) returns zero, nothing has been placed into the supplied buffer, so this is probably from some previous iteration of the loop. recv(2)返回零时,所提供的缓冲区中未放置任何内容,因此这可能来自循环的某些先前迭代。

Notes: 笔记:

  1. Connected TCP socket is is a bi-directional stream of bytes . 连接的TCP套接字是双向字节流 This means you might send several of your "messages" and receive them in one chunk on the other side, or the other way around. 这意味着您可能会发送多个“消息”,并在另一侧以另一块或相反的方式接收它们。 Read from the socket in a loop until you have enough data to process, ie use explicit message separators, or pre-pend a length of your message that follows. 循环读取套接字中的数据,直到有足够的数据要处理为止,即使用显式的消息分隔符,或在后面加上一段消息。 This is your application-level protocol . 这是您的应用程序级协议

  2. Don't mix C and C++ string handing like this. 不要像这样混合使用C和C ++字符串处理。 std::string has a size() method, use it instead of doing strlen( msgToSend.c_str() ) . std::string具有size()方法,而不要使用strlen( msgToSend.c_str() )使用它。

  3. Allocating any sizable buffers on the stack, especially ones receiving input from the network is a bad idea. 在堆栈上分配任何可观的缓冲区,尤其是从网络接收输入的缓冲区是一个坏主意。

  4. Printing, or otherwise passing further, unverified network input is a gross security violation leading to all sorts of problems. 打印或以其他方式传递未经验证的网络输入是严重的安全违规,从而导致各种问题。

Edit 0: 编辑0:

@molbdnilo's answer is the right one. @molbdnilo的答案是正确的。 I did not spot the precedence problem in the conditionals. 我没有在条件句中发现优先级问题。 My notes still apply though. 我的笔记仍然适用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM