简体   繁体   English

C ++套接字,发送和接收不同步

[英]C++ Sockets, send and recv not in sync

I'm currently working on a multiplayer game using sockets and I encountered some problems at the log-in. 我目前正在使用套接字进行多人游戏,并且在登录时遇到了一些问题。

Here's the server function - thread that deals with incoming messages from a user: 这是服务器功能-处理来自用户的传入消息的线程:

void Server::ClientThread(SOCKET Connection)
{
char *buffer = new char[256];

while (true)
{
    ZeroMemory(buffer,256);
    recv(Connection, buffer, 256, 0);
    cout << buffer << endl;
    if (strcmp(buffer, "StartLogIn"))
    {
        char* UserName = new char[256];
        ZeroMemory(UserName, 256);
        recv(Connection, UserName, 256, 0);

        char* Password = new char[256];
        ZeroMemory(Password, 256);
        recv(Connection, Password, 256, 0);

        cout << UserName << "-" << Password << " + "<<  endl;
        if (memcmp(UserName, "taigi100", sizeof(UserName)))
        {
            cout << "SMB Logged in";
        }
        else
            cout << "Wrong UserName";
    }

    int error = send(Connection, "0", 1, 0);
//  error = WSAGetLastError();
    if (error == SOCKET_ERROR)
    {
        cout << "SMB D/Ced";
        ExitThread(0);
    }
}
}

And here is the function that sends the data from the client to the server: 这是将数据从客户端发送到服务器的函数:

if (LogInButton->isPressed())
{
    send(Srv->getsConnect(), "StartLogIn", 256, 0);
    const wchar_t* Usern = UserName->getText();
    const wchar_t* Passn = Password->getText();
    stringc aux = "";
    aux += Usern;
    char* User = (char*)aux.c_str();

    stringc aux2 = "";
    aux2 += Passn;
    char* Pass = (char*)aux2.c_str();

    if (strlen(User) > 0 && strlen(Pass) > 0)
    {
        send(Srv->getsConnect(), User, 256, 0);
        send(Srv->getsConnect(), Pass, 256, 0);
    }
}

I'm going to try to explain this as easy as possible. 我将尽力解释这一点。 The first recv function from the while(true) in the Server-side function receives at first "StartLogIn" but does not enter the if only until the next loop of the while. 服务器端函数中while(true)中的第一个recv函数首先接收“ StartLogIn”,但直到下一个while循环才输入if。 Because it loops again it changes to "taigi100" ( a username I use ) and then it enters the if even tho it shouldn't. 因为它再次循环,所以更改为“ taigi100”(我使用的用户名),然后输入是否应该输入。

A way to fix this would be to make a send-recv system in order to not send anything else until it got some feedback. 解决此问题的一种方法是制作一个send-recv系统,以便在收到反馈之前不发送其他任何东西。

I want to know if there are any other fast ways of solving this problem and why such weird behaviour happens. 我想知道是否还有其他快速方法可以解决此问题,以及为什么会发生这种奇怪的行为。

Well it's full of bugs. 好吧,这里充满了错误。

  • Your overuse of new[]. 您对new []的过度使用。 Ok not a bug but you are not deleting any of these, and you could use either local stack buffer space or vector< char > 好的,不是bug,但是您不会删除其中任何一个,可以使用本地堆栈缓冲区空间或vector< char >

  • You need to always check the result of any call to recv as you are not guaranteed to receive the number of bytes you are expecting. 您需要始终检查对recv的任何调用的结果,因为不能保证会收到期望的字节数。 The number you specify is the size of the buffer, not the number of bytes you are expecting to get. 您指定的数字是缓冲区的大小,而不是期望的字节数。

  • strcmp returns 0 if the strings match, non-zero if they do not (actually 1 or -1 depending whether they compare less or greater). 如果字符串匹配,strcmp返回0,否则返回非零(实际上是1还是-1,取决于它们比较的是大还是小)。 But it appears you are using non-zero to mean equal. 但是看来您使用非零表示相等。

  • Not sure what stringc is. 不知道什么是stringc。 Some kind of conversion from wide string to string? 从宽字符串到字符串的某种转换? In any case, I think send is const-correct so there is no need to cast the constness away. 无论如何,我认为send是const正确的,因此不需要抛弃constness。

  • 3rd parameter of send is the number of bytes you are sending, not the capacity of your buffer. send的第三个参数是要发送的字节数,而不是缓冲区的容量。 The user name and password are probably not 256 bytes. 用户名和密码可能不是256个字节。 You need to send them as a "packet" though so the receiver knows what they are getting and will know when they have received a full packet. 但是,您需要将它们作为“数据包”发送,以便接收方知道它们要获取的内容,并知道它们何时收到完整的数据包。 eg send a string like "User=vandamon\\0". 例如,发送一个字符串,例如“ User = vandamon \\ 0”。 (And you need to check its return value too) (并且您还需要检查其返回值)

Because send() and recv() calls may not match up, two very good habits to get into are (1) preceed all variable length data by a fixed size length, and (2) only send the bare minimum needed. 由于send()recv()调用可能不匹配,因此有两个很好的习惯:(1)将所有可变长度数据的大小固定为固定长度,并且(2)仅发送所需的最低要求。

So your initial send() call would be written as follows: 因此,您的初始send()调用将编写如下:

char const * const StartLogin = "StartLogIn";
short const StartLoginLength = static_cast<short>(strlen(StartLogin));
send(Srv->getsConnect(), reinterpret_cast<char *>(&StartLoginLength), sizeof(short), 0);
send(Srv->getsConnect(), StartLogin, StartLoginLength, 0);

The corresponding receive code would then have to read two bytes and guarantee that it got them by checking the return value from recv() and retrying if not enough was received. 然后,相应的接收代码将必须读取两个字节,并通过检查来自recv()的返回值recv()试接收到的字节数来确保获得了它们。 Then it would loop a second time reading exactly that many bytes into a buffer. 然后,它将循环第二次将正好那么多字节读取到缓冲区中。

int guaranteedRecv(SOCKET s, char *buffer, int expected)
{
    int totalReceived = 0;
    int received;
    while (totalReceived < expected)
    {
        received = recv(s, &buffer[totalReceived], expected - totalReceived, 0);
        if (received <= 0)
        {
            // Handle errors
            return -1;
        }
        totalReceived += received;
    }
    return totalReceived;
}

Note that this assumes a blocking socket. 请注意,这是假设插座是阻塞的。 Non-blocking will return zero if no data is available and errno / WSAGetLastError() will say *WOULDBLOCK. 如果没有可用数据,则非阻塞将返回零,并且errno / WSAGetLastError()将显示* WOULDBLOCK。 If you want to go this route you'll have to handle this case specifically and find some way to block till data is available. 如果您想走这条路,则必须专门处理这种情况,并找到某种方法来阻止直到数据可用。 Either that or busy-wait waiting for data, by repeatedly calling recv() . 通过重复调用recv() ,或者忙于等待数据。 UGH. 啊。

Anyway, you call this first with the address of a short reinterpret_cast<char *> and expected == sizeof(short) . 无论如何,您首先用一个简短的reinterpret_cast<char *>的地址调用它,并期望它== sizeof(short) Then you new[] enough space, and call a second time to get the payload. 然后,您获得了new[]足够的空间,并再次调用以获取有效负载。 Beware of the lack of trailing NUL characters, unless you explicitly send them, which my code doesn't. 谨防缺少尾随的NUL字符,除非您显式地发送它们,而我的代码却没有。

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

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