C++ win32 async socket client send() issue
C++ win32 async socket client send() issue
我必须编写一个WIN32,异步套接字客户端应用程序,它连接到服务器。我可以启动程序,连接到服务器,接收并处理接收到的数据,但是当我试图向服务器发送消息时,服务器上什么也没有发生。我没有任何套接字错误消息,并且send()函数返回正确的发送字节。但是服务器看起来好像没有接收到任何东西。我的send()是在FD_READ语句中调用的,首先是if语句。我试图将其移动到其他语句(WM_CREATE, FD_WRITE, FD_CONNECT),但仍然是同样的问题。我试图找到什么是错的,看了很多很多论坛,因为一个月,但我没有发现问题。任何帮助都将非常感激。下面是我的代码示例(我跳过了不有趣的部分):
#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=1rn" but it doesn't...) here is my problem...
appendTextToEdit(hDebug, "Message sent : ");
appendTextToEdit(hDebug, message);
char *endline = "rn";
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_READn");
}
break;
case FD_WRITE:
{
appendTextToEdit(hDebug, "FD_WRITE event...n");
appendTextToEdit(hDebug, "End of FD_WRITEn");
}
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 server should do an action when it receives "Qh57=1rn" but it doesn't...
它从来没有收到rn
,因为你从来没有发送它:
char message[256] = "Qh57=1";
...
char *endline = "rn";
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();
顺便说一句,使用std:string
只是为了计算消息长度是对内存的浪费。您可以使用strlen()
代替:
int messageLength = strlen(message);
流套接字连接不知道任何消息边界。它只是传输字节,就像RS232连接一样。如果您仔细阅读recv
的文档,您会发现一个成功的recv
调用可以在一个字节和指定的缓冲区大小之间的任何地方"返回"(=复制到缓冲区)——这取决于已经接收到多少数据。recv
的返回值将告诉您复制了多少。如果后面有更多的数据,它将通过后续的recv
调用返回。这意味着如果服务器发送id=3nversion=10.0.5nQh57=0nQs323=-999999n
,您的第一个recv
调用可能返回id=3nversion=10.0.5nQh
,然后第二个可能返回57=0nQs323=-999999n
。或者你可以把它分成三块。或4。或者一次一个字节。
最简单的方法来处理这个问题,不幸的是它有非常糟糕的性能,是重复调用recv
的缓冲区大小为一个字节。直到您看到您已经收到了整个消息(在您的示例中,这将是在收到了4个n
字符之后)。
首选的方式是:
- 使用更大的缓冲区调用
recv
- 附加多少字节
recv
返回到您的"消息程序集缓冲区" - 如果"消息集缓冲区"至少包含一条完整的消息:处理并删除它。重复,直到所有完整的消息被处理并删除。
- 转到(1)
- Seg Fault Issue C++ (file IO / getline)
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- Issue with WriteProcessMemory
- std::partition segfault issue
- VSCode C/C++ Intellisense issue: Undefined identifiers (Linu
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- OpenGL glBufferSubData Offset issue
- boost::property_tree XML issue
- ZMQ::send() 抛出异常并终止 QNX 进程.为什么以及如何从中恢复?
- qt QWidget::closeEvent link issue
- QT 和 JIRA Rest API: /rest/api/2/issue/createmeta 意外返回韩语的 iss
- Crypto++ GetModulus() issue
- 在UNIX中通过recv/send交换数据时,如何正确使用缓冲区
- 如何为winsock的send()函数构造constchar*缓冲区
- Vector.push_back Issue
- 实时线程中的 ZeroMQ inproc PubSub send() 调用会导致严重的阻塞吗?
- Epoll zero recv() and negative(EAGAIN) send()
- C++嵌套类从嵌套类继承,请键入 issue
- C++ PBKDF2 Issue
- C++ win32 async socket client send() issue