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

时间:2015-08-04 20:39:17

标签: c++ sockets winapi asynchronous send

我必须编写一个连接到服务器的WIN32异步套接字客户端应用程序。我可以启动程序,连接到服务器,接收并继续接收数据,但是当我尝试向服务器发送消息时,服务器上没有任何反应。我没有任何套接字错误消息,send()函数返回正确的发送字节。但服务器看起来似乎没有收到任何东西。我的send()在FD_READ语句中调用,首先是if语句。我试图将其移动到其他语句(WM_CREATE,FD_WRITE,FD_CONNECT),但仍然是同一个问题。我试图找出错误,从一个mounth看很多论坛,但我没有发现问题。任何帮助将非常感激。这是我的代码示例(我跳过了非常有趣的部分):

#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);
}

2 个答案:

答案 0 :(得分:3)

问题是您向服务器发送了不完整的消息,因此它不会执行任何操作。根据您自己在代码中的评论:

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

它永远不会收到\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!

不要从消息长度中减去CRLF:

int messageLength = messageString.length();

BTW,使用std:string来计算消息长度是浪费内存。您可以改为使用strlen()

int messageLength = strlen(message);

答案 1 :(得分:0)

流套接字连接对消息边界一无所知。它只传输字节,就像RS232连接一样。如果您仔细阅读recv的文档,您会发现成功的recv调用可以在一个字节和指定的缓冲区大小之间的任何地方“返回”(=复制到缓冲区) - 具体取决于多少数据已经收到。 recv的返回值将告诉您已复制了多少。如果跟随更多数据,则将通过后续recv次呼叫返回该数据。这意味着,如果服务器发送id=3\nversion=10.0.5\nQh57=0\nQs323=-999999\n,您的第一个recv调用可能会返回id=3\nversion=10.0.5\nQh,第二个可能会返回57=0\nQs323=-999999\n。或者你可以用3个块来获得它。或者4.或者甚至一次一个字节。

处理此问题的最简单方法(不幸的是性能非常差)是使用一个字节的缓冲区大小重复调用recv。直到您看到已收到整条消息(在您的示例中,这将是在收到4个\n个字符后。)

首选方式如下:

  1. 使用更大的缓冲区调用recv
  2. 追加许多字节recv返回到“消息程序集缓冲区”
  3. 如果“消息程序集缓冲区”包含至少一条完整消息:处理并删除它。重复,直到处理完所有消息并将其删除。
  4. 转到(1)