简体   繁体   English

C ++ win32异步套接字客户端send()问题

[英]C++ win32 async socket client send() issue

I have to write a WIN32, asynchronous socket client application, which connects to a server. 我必须编写一个WIN32,异步套接字客户端应用程序,它连接到服务器。 I can launch the program, connect to the server, receive and proceed the received data, but when I try to send a message to the server, nothing happens on the server. 我可以启动程序,连接到服务器,接收并继续接收数据,但是当我尝试向服务器发送消息时,服务器上没有任何反应。 I do not have any socket error message, and the send() function returns the correct sent bytes. 我没有任何套接字错误消息,send()函数返回正确的发送字节。 But the server looks like it doesn't receive anything. 但服务器看起来好像没有收到任何东西。 My send() is called within the FD_READ statement, first if statement. 我的send()在FD_READ语句中调用,首先是if语句。 I tried to move it to other statements (WM_CREATE, FD_WRITE, FD_CONNECT), but still the same issue. 我试图将其移动到其他语句(WM_CREATE,FD_WRITE,FD_CONNECT),但仍然是同一个问题。 I tried to find what's wrong, look at many many forums since one mounth, but I do not find the problem. 我试图找出什么是错的,从一个mounth看很多论坛,但我没有发现问题。 Any help would be very appreciated. 任何帮助将非常感激。 Here is a sample of my code (I skipped the non interesting parts) : 这是我的代码示例(我跳过了非常有趣的部分):

#include <windows.h>
#include <fstream>
#include <sstream>
#include<process.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define IDC_BUTTON_CONNECT 101 // Button identifiers

#define IDC_EDIT_IP 102 // Edit / Text box identifiers
#define IDC_EDIT_PORT 103
#define IDC_EDIT_DEBUG 104
#define WM_SOCKET 105 // Socket messages structure identifier

HWND hWnd;
HWND hEditIp;
HWND hEditPort;
HWND hDebug;

HWND hButtonConnect;

HANDLE hReadMutex;

SOCKET Socket = NULL;
SOCKADDR_IN SockAddr;
char *ip = "";
char *port = "";
bool connectStatus = FALSE;
char readBuffer[5000];
char id[32];
char version[256];

LPSTR statusText = TEXT("Connecting...");

LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);

// WinMain...

// appendTextToEdit function...

// incoming data processing thread (not used yet)...

LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{   
    switch(msg)
    {
        case WM_CREATE:
        {
        // Get IP and port from file "config.ini"...        

        //Create windows for IP, Port and Debug...

        // Create "connect" button...


        WSADATA WsaDat; // Winsock initialization...
        int nResult=WSAStartup(MAKEWORD(2,2),&WsaDat);
        if(nResult!=0)
        {
            statusText=TEXT("Winsock initialization failed");                   
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }

        Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // Creating socket...
        if(Socket==INVALID_SOCKET)
        {
            statusText=TEXT("Socket creation failed");                  
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }

        nResult=WSAAsyncSelect(Socket,hWnd,WM_SOCKET,(FD_CLOSE|FD_READ|FD_WRITE|FD_CONNECT)); // Select AsyncSocket events...
        if(nResult)
        {
            statusText=TEXT("WSAAsyncSelect failed");                   
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }           

        // Set up our socket address structure...
        SockAddr.sin_addr.s_addr=inet_addr(ip);
        SockAddr.sin_port=htons(atoi(port)); 
        SockAddr.sin_family=AF_INET;        

        connect(Socket,(LPSOCKADDR)(&SockAddr),sizeof(SockAddr)); // Connexion command              
    }
    break;

    case WM_PAINT:
    {
        // painting / updating the window...
    }
    break;

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case IDC_BUTTON_CONNECT:
            {                   
                if(connectStatus==FALSE) // Avoid to reconnect when already online...
                {
                    // Window repaint with updated text (statusText = global variable)
                    statusText=TEXT("Connecting...");                   
                    InvalidateRect(hWnd, NULL, TRUE);                   

                    // Retrieve edit box datas and store to variables and file...
                    // Saving data to "config.ini" file...

                    // Reinitializing adress structure with new host adress and port from edit boxes, and reconnect attempt...
                    SockAddr.sin_addr.s_addr=inet_addr(ip);
                    SockAddr.sin_port=htons(atoi(port));
                    SockAddr.sin_family=AF_INET;    

                    connect(Socket,(LPSOCKADDR)(&SockAddr),sizeof(SockAddr));
                }
                break;
            }
            break;          
        }
        break;          

    case WM_SOCKET:
    {           
        switch(WSAGETSELECTEVENT(lParam))
        {
            case FD_CONNECT:
            {
                connectStatus = TRUE;
                statusText=TEXT("Connected");               
            }

            case FD_READ:
            {
                appendTextToEdit(hDebug, "FD_READ event...\n");

                char *rawVariable = nullptr;                    
                char *next_token = nullptr;                         

                recv(Socket, readBuffer, sizeof(readBuffer), 0);            

                rawVariable = readBuffer;               

                if(strstr(rawVariable, "id=") != NULL) // Identifying message sent by the server (starts with id, version, ...) and store the values to variables (working fine)
                {   
                    char *label = strtok_s(rawVariable, "=", &next_token);
                    char *pId = strtok_s(NULL, "\n", &next_token);  
                    rawVariable = strtok_s(NULL, "", &next_token);
                    strcpy_s(id, pId);

                    char message[256] = "Qh57=1"; // Setting up message structure... (the server should do an action when it receives "Qh57=1\r\n" but it doesn't...) here is my problem...
                    appendTextToEdit(hDebug, "Message sent : ");
                    appendTextToEdit(hDebug, message);
                    char *endline = "\r\n";
                    strcat_s(message, endline); 

                    string messageString(message);          
                    int messageLength = ((messageString.length()) - 2); // Get outgoing string length (-2)                          
                    int sent = send(Socket, message , messageLength , 0);               

                    char buffer [33]; // Display number of bytes sent...
                    _itoa_s(sent,buffer,10);                    
                    appendTextToEdit(hDebug, " (");
                    appendTextToEdit(hDebug, buffer);
                    appendTextToEdit(hDebug, " bytes sent)\n");
                }

                if( strstr(rawVariable, "version") != NULL)
                {
                    char *label = strtok_s(rawVariable, "=", &next_token);      
                    char *pVersion = strtok_s(NULL, "\n", &next_token); 
                    rawVariable = strtok_s(NULL, "", &next_token);      
                    strcpy_s(version, pVersion);    
                }                                       

                appendTextToEdit(hDebug, "End of FD_READ\n");           
            }
            break;

            case FD_WRITE:
            {
                appendTextToEdit(hDebug, "FD_WRITE event...\n");

                appendTextToEdit(hDebug, "End of FD_WRITE\n");
            }
            break; 

            case FD_CLOSE:
            {   
                statusText = "Disconnected from server";
                connectStatus = FALSE;
                InvalidateRect(hWnd, NULL, TRUE);
//              SendMessage(hWnd, WM_DESTROY, NULL, NULL); // Final version...                  
            }
            break;
        }
        break; 
    }
    break;

    case WM_DESTROY:
    {
        closesocket(Socket);
        WSACleanup();
        PostQuitMessage(0);
        return 0;
    }
    break;
}   
return DefWindowProc(hWnd,msg,wParam,lParam);
}

The problem is that you are sending an incomplete message to the server, so it is not going to do anything. 问题是您正在向服务器发送不完整的消息,因此它不会执行任何操作。 Per your own comments in the code: 根据您自己在代码中的注释:

the server should do an action when it receives "Qh57=1\r\n" but it doesn't...

It never receives the \\r\\n , because you are never sending it: 它永远不会收到\\r\\n ,因为你永远不会发送它:

char message[256] = "Qh57=1";
...
char *endline = "\r\n";
strcat_s(message, endline); // <-- you do append a CRLF...

string messageString(message);          
int messageLength = ((messageString.length()) - 2); // <-- but you subtract the CRLF from the message length...
int sent = send(Socket, message , messageLength , 0); // <-- so the CRLF is not sent!

Do not subtract the CRLF from the message length: 不要从消息长度中减去CRLF:

int messageLength = messageString.length();

BTW, using a std:string just to calculate the message length is a waste of memory. 顺便说一下,使用std:string来计算消息长度是浪费内存。 You can use strlen() instead: 您可以使用strlen()代替:

int messageLength = strlen(message);

A stream socket connection doesn't know anything about message boundaries. 流套接字连接对消息边界一无所知。 It just transports bytes, much like a RS232 connection. 它只传输字节,就像RS232连接一样。 If you read the documentation of recv carefully, you will find that a successful recv call can "return" (=copy to the buffer) anywhere between one byte and the specified buffer size - depending on how much data has already been received. 如果你仔细阅读recv的文档,你会发现一个成功的recv调用可以在一个字节和指定的缓冲区大小之间的任何地方“返回”(=复制到缓冲区) - 这取决于已经接收了多少数据。 recv s return value will tell you how much has been copied. recv s返回值将告诉您已复制了多少。 If more data follows, it will be returned by succeeding recv calls. 如果跟随更多数据,则将通过后续的recv调用返回。 That means if the server sends id=3\\nversion=10.0.5\\nQh57=0\\nQs323=-999999\\n your first recv call might return id=3\\nversion=10.0.5\\nQh and the second might then return 57=0\\nQs323=-999999\\n . 这意味着如果服务器发送id=3\\nversion=10.0.5\\nQh57=0\\nQs323=-999999\\n您的第一次recv调用可能会返回id=3\\nversion=10.0.5\\nQh ,第二次可能会返回57=0\\nQs323=-999999\\n Or you might get it in 3 chunks. 或者你可以用3个块来获得它。 Or 4. Or even one byte at a time. 或者4.或者甚至一次一个字节。

The easiest way to deal with this, which unfortunately has very bad performance, is to repeatedly call recv with a buffer size of one byte. 解决这个问题的最简单的方法,不幸的是性能非常差,是重复调用recv ,缓冲区大小为一个字节。 Until you see that you have received the whole message (in your example this would be after 4 \\n characters have been received). 直到你看到你收到了整条信息(在你的例子中,这将是在收到4 \\n字符之后)。

The preferred way would be something like: 首选的方式是:

  1. Call recv with a bigger buffer 使用更大的缓冲区调用recv
  2. Append however many bytes recv returned to your "message assembly buffer" 追加许多字节recv返回到“消息组装缓冲区”
  3. If the "message assembly buffer" contains at least one complete message: process and remove it. 如果“消息程序集缓冲区”包含至少一个完整消息:处理并删除它。 Repeat until all complete messages have been processed and removed. 重复此操作,直到处理完所有消息并将其删除。
  4. Goto (1) 转到(1)

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

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