- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
大家好我一直在一个服务器上工作,它从一个简单的客户端接收两个地址,然后使用异步套接字和 WINAPI 将它们打印出来,到目前为止我已经设置了窗口并且能够接受连接但是当我尝试向服务器发送两条消息时,它只收到一条消息而第二条失败。这是代码:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND hEdit = NULL;
int len = sizeof(Server);
switch (msg)
{
case WM_CREATE:
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
0, 0, WIDTH, HEIGHT, hwnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case 1111:
if (LOWORD(lParam) == FD_ACCEPT)
{
socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
socket = accept(sListen, (SOCKADDR*)&Server, &len);
print_line(hwnd, "IT WAS ACCEPTED!!!!!!!!\r\n");
}
if (LOWORD(lParam) == FD_CLOSE)
{
print_line(hwnd, "Client left the server!\r\n");
}
if (LOWORD(lParam) == FD_READ)
{
char NICK[4096] = { 0 };
char IP[4096] = { 0 };
ZeroMemory(NICK, strlen(NICK));
ZeroMemory(IP, strlen(IP));
if (recv(socket, IP, sizeof(IP), NULL) == INVALID_SOCKET)//get the IP address
{
print_line(hwnd, "Failed to recieve IP Address from socket!");
print_line(hwnd, "\r\n");
}
if (recv(socket, NICK, sizeof(NICK), NULL) == INVALID_SOCKET)//get the Nickname
{
print_line(hwnd, "Failed to recieve nickname from socket!");
print_line(hwnd, "\r\n");
}
//prints the Username and IP address to the window screen
print_line(hwnd, "Username: ");
print_line(hwnd, NICK);
print_line(hwnd, "\r\n");
print_line(hwnd, "IP Address: ");
print_line(hwnd, IP);
print_line(hwnd, "\r\n");
}
break;
default:
HWND hEdit;
RECT rcClient;
GetClientRect(hwnd, &rcClient);
hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
SetWindowPos(hEdit, NULL, 0, 0, rcClient.right, rcClient.bottom, SWP_NOZORDER);
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
}
最佳答案
假设您正在使用 WSAAsyncSelect()
,但您没有显示创建监听套接字或为其注册消息处理程序的代码。
您不应在代码中使用魔数(Magic Number)。 1111 是 WM_USER+87
,因此您应该将其分配给一个常量以便于阅读,例如:const UINT WM_SOCKETMSG = WM_USER + 87;
,然后使用该名称在您的 case
语句中,例如:case WM_SOCKETMSG:
。
您的套接字消息处理程序在调用 accept()
之前调用了 socket()
。 accept()
分配并返回一个新套接字。因此,每次收到 FD_ACCEPT
通知时,您都会泄漏套接字。如果多个客户端碰巧连接,您将失去对旧套接字的跟踪,因为您使用单个变量来跟踪所有这些套接字。
您没有考虑到 TCP 是字节流,recv()
返回的字节数可能少于您要求的字节数。它返回当前可用的任何数据,不超过您给它的缓冲区的大小。您使用的是异步套接字,但您编写的阅读代码就好像您使用的是同步套接字一样(即便如此,您展示的逻辑有时也会失败)。如果当前没有可用数据,recv()
将失败并出现您未处理的 WSAEWOULDBLOCK
错误。每当有新数据到达时,您需要将其读入滚动缓冲区,然后根据需要仅从该缓冲区中取出完整的数据,将不完整的数据留在缓冲区中,以便后续读取完成。
你需要设计一个协议(protocol)来控制你的数据流,你不能随便乱扔数据。我严重怀疑您想要 4KB 的用户名并在 IP 地址上浪费 ~3KB。您需要对传输的值进行定界,这不仅是为了减少带宽使用,也是为了减少保存它们所需的内存。对于像您所展示的那样简单的东西,您可以使用 LF 字符分隔值。然后,您可以在处理滚动缓冲区时查找该字符。
最后,您根本没有处理错误。你需要这样做。
尝试更像这样的东西:
#include <map>
#include <string>
std::map<SOCKET, std::string> ClientBuffers;
typedef std::map<SOCKET, std::string>::iterator BufferIterator;
const UINT WM_SOCKETMSG = WM_USER + 87;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
...
switch (msg)
{
...
case WM_SOCKETMSG:
{
SOCKET socket = (SOCKET) wParam;
int event = WSAGETSELECTEVENT(lParam);
int error = WSAGETSELECTERROR(lParam);
switch (event)
{
case FD_ACCEPT:
{
if (error == 0)
{
sockaddr_in clientaddr = {0};
int len = sizeof(clientaddr);
if (accept(socket, (SOCKADDR*)&clientaddr, &len) != INVALID_SOCKET)
{
print_line(hwnd, "A client connected to the server!\r\n");
break;
}
error = WSAGetLastError();
}
print_line(hwnd, "Error accepting a client!\r\n");
// handle the error on the reported socket as needed...
break;
}
case FD_CLOSE:
{
if (error == 0)
print_line(hwnd, "A client left the server!\r\n");
else
print_line(hwnd, "A client connection was lost unexpectedly!\r\n");
BufferIterator i = ClientBuffers.find(socket);
if (i != ClientBuffers.end())
ClientBuffers.erase(i);
break;
}
case FD_READ:
{
char buf[1024];
int numRead = recv(socket, buf, sizeof(buf), NULL);
if (numRead == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
print_line(hwnd, "Failed to read from a client!\r\n");
// handle the error on the reported socket as needed...
}
break;
}
if (numRead == 0)
break;
std::string &buffer = ClientBuffers[socket];
buffer += std::string(buf, numRead);
std::string::size_type idx1 = buffer.find('\n');
if (idx1 == std::string::npos)
break; // wait for more data
std::string::size_type idx2 = buffer.find('\n', idx1+1);
if (idx2 == std::string::npos)
break; // wait for more data
std::string IP = buffer.substr(0, idx1);
std::string NICK = buffer.substr(idx1+1, idx2-idx1-1);
buffer.erase(0, idx2+1);
//prints the Username and IP address to the window screen
print_line(hwnd, "Username: ");
print_line(hwnd, NICK.c_str());
print_line(hwnd, "\r\n");
print_line(hwnd, "IP Address: ");
print_line(hwnd, IP.c_str());
print_line(hwnd, "\r\n");
break;
}
}
break;
}
}
...
}
关于c++ - recv 函数在使用两次时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26069327/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!