简体   繁体   中英

C++ win32 async socket client send() issue

I have to write a WIN32, asynchronous socket client application, which connects to a server. 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. But the server looks like it doesn't receive anything. My send() is called within the FD_READ statement, first if statement. I tried to move it to other statements (WM_CREATE, FD_WRITE, FD_CONNECT), but still the same issue. I tried to find what's wrong, look at many many forums since one mounth, but I do not find the problem. 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:

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:

int messageLength = messageString.length();

BTW, using a std:string just to calculate the message length is a waste of memory. You can use strlen() instead:

int messageLength = strlen(message);

A stream socket connection doesn't know anything about message boundaries. It just transports bytes, much like a RS232 connection. 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 s return value will tell you how much has been copied. If more data follows, it will be returned by succeeding recv calls. 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 . Or you might get it in 3 chunks. Or 4. Or even one byte at a time.

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. Until you see that you have received the whole message (in your example this would be after 4 \\n characters have been received).

The preferred way would be something like:

  1. Call recv with a bigger buffer
  2. Append however many bytes recv returned to your "message assembly buffer"
  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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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