- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在为从桌面客户端到浏览器的视频流编写 IOCP 服务器。双方都使用 WebSocket 协议(protocol)来统一服务器的架构(并且因为浏览器没有其他方式可以执行全双工交换)。
工作线程是这样开始的:
unsigned int __stdcall WorkerThread(void * param){
int ThreadId = (int)param;
OVERLAPPED *overlapped = nullptr;
IO_Context *ctx = nullptr;
Client *client = nullptr;
DWORD transfered = 0;
BOOL QCS = 0;
while(WAIT_OBJECT_0 != WaitForSingleObject(EventShutdown, 0)){
QCS = GetQueuedCompletionStatus(hIOCP, &transfered, (PULONG_PTR)&client, &overlapped, INFINITE);
if(!client){
if( Debug ) printf("No client\n");
break;
}
ctx = (IO_Context *)overlapped;
if(!QCS || (QCS && !transfered)){
printf("Error %d\n", WSAGetLastError());
DeleteClient(client);
continue;
}
switch(auto opcode = client->ProcessCurrentEvent(ctx, transfered)){
// Client owed to receive some data
case OPCODE_RECV_DEBT:{
if((SOCKET_ERROR == client->Recv()) && (WSA_IO_PENDING != WSAGetLastError())) DeleteClient(client);
break;
}
// Client received all data or the beginning of new message
case OPCODE_RECV_DONE:{
std::string message;
client->GetInput(message);
// Analizing the first byte of WebSocket frame
switch( opcode = message[0] & 0xFF ){
// HTTP_HANDSHAKE is 'G' - from GET HTTP...
case HTTP_HANDSHAKE:{
message = websocket::handshake(message);
while(!client->SetSend(message)) Sleep(1); // Set outgoing data
if((SOCKET_ERROR == client->Send()) && (WSA_IO_PENDING != WSAGetLastError())) DeleteClient(client);
break;
}
// Browser sent a closing frame (0x88) - performing clean WebSocket closure
case FIN_CLOSE:{
websocket::frame frame;
frame.parse(message);
frame.masked = false;
if( frame.pl_len == 0 ){
unsigned short reason = 1000;
frame.payload.resize(sizeof(reason));
frame.payload[0] = (reason >> 8) & 0xFF;
frame.payload[1] = reason & 0xFF;
}
frame.pack(message);
while(!client->SetSend(message)) Sleep(1);
if((SOCKET_ERROR == client->Send()) && (WSA_IO_PENDING != WSAGetLastError())) DeleteClient(client);
shutdown(client->Socket(), SD_SEND);
break;
}
IO 上下文结构:
struct IO_Context{
OVERLAPPED overlapped;
WSABUF data;
char buffer[IO_BUFFER_LENGTH];
unsigned char opcode;
unsigned long long debt;
std::string message;
IO_Context(){
debt = 0;
opcode = 0;
data.buf = buffer;
data.len = IO_BUFFER_LENGTH;
overlapped.Offset = overlapped.OffsetHigh = 0;
overlapped.Internal = overlapped.InternalHigh = 0;
overlapped.Pointer = nullptr;
overlapped.hEvent = nullptr;
}
~IO_Context(){ while(!HasOverlappedIoCompleted(&overlapped)) Sleep(1); }
};
客户端发送函数:
int Client::Send(){
int var_buf = O.message.size();
// "O" is IO_Context for Output
O.data.len = (var_buf>IO_BUFFER_LENGTH)?IO_BUFFER_LENGTH:var_buf;
var_buf = O.data.len;
while(var_buf > 0) O.data.buf[var_buf] = O.message[--var_buf];
O.message.erase(0, O.data.len);
return WSASend(connection, &O.data, 1, nullptr, 0, &O.overlapped, nullptr);
}
当桌面客户端断开连接时(它只使用 closesocket(),没有 shutdown()),GetQueuedCompletionStatus 返回 TRUE 并将设置转移到 0 - 在这种情况下,WSAGetLastError() 返回 64(指定的网络名称不再是可用),并且有意义 - 客户端已断开连接(符合 if(!QCS || (QCS && !transfered))
)。但是当浏览器断开连接时,错误代码让我感到困惑......它可以是0、997(等待操作)、87(无效参数)......并且没有与连接结束相关的代码。
为什么 IOCP 选择这个事件?它如何选择挂起的操作?为什么传输 0 个字节时错误为 0?它还会导致无休止地尝试删除与重叠结构关联的对象,因为析构函数调用 ~IO_Context(){ while(!HasOverlappedIoCompleted(&overlapped)) Sleep(1); }
用于安全删除。在 DeleteClient
调用中,套接字使用 closesocket()
关闭,但是,如您所见,我发布了一个 shutdown(client->Socket(), SD_SEND);
在它之前调用(在 FIN_CLOSE
部分)。
我知道连接有两个方面,在服务器端关闭它并不意味着另一端也会关闭它。但我需要创建一个稳定的服务器,不受坏连接和半开连接的影响。例如,Web 应用程序的用户可以快速按 F5 几次重新加载页面(是的,有些人这样做 :))- 连接将重新打开几次,并且服务器不能由于此操作而延迟或崩溃。
如何在 IOCP 中处理这种“不良”事件?
最佳答案
你这里有很多错误的代码。
while(WAIT_OBJECT_0 != WaitForSingleObject(EventShutdown, 0)){
QCS = GetQueuedCompletionStatus(hIOCP, &transfered, (PULONG_PTR)&client, &overlapped, INFINITE);
这不是有效且错误的停止代码 WorkerThread
.一开始你做多余的电话WaitForSingleObject
, 使用过量 EventShutdown
并且主要是无论如何都无法关闭。如果您的代码等待内部的数据包 GetQueuedCompletionStatus
你说的EventShutdown
- 不中断 GetQueuedCompletionStatus
调用 - 你在这里继续无限等待。正确的关机方式 - PostQueuedCompletionStatus(hIOCP, 0, 0, 0)
而是调用 SetEvent(EventShutdown)
如果工作线程 View client == 0
- 他打破循环。通常你需要多个 WorkerThread
(不是单例)。和多个电话 PostQueuedCompletionStatus(hIOCP, 0, 0, 0)
- 工作线程的准确计数。您还需要将此调用与 io 同步 - 只有在所有 io 已经完成并且没有新的 io 数据包将排队到 iocp 之后才执行此操作。所以“空数据包”必须是最后排队到端口的
if(!QCS || (QCS && !transfered)){
printf("Error %d\n", WSAGetLastError());
DeleteClient(client);
continue;
}
如果!QCS
- client
中的值未初始化,您根本无法使用它并调用 DeleteClient(client);
在这种情况下是错误的
当多个线程使用对象 (client
) - 谁必须删除它?如果一个线程删除对象,而另一个线程仍在使用它会怎样?正确的解决方案是在此类对象(客户端)上使用引用计数。并根据您的代码 - 每个 hIOCP 都有一个客户端?因为您检索客户端的指针作为 hIOCP 的完成键,它对于绑定(bind)到 hIOCP 的套接字上的所有 I/O 操作都是单一的。这一切都是错误的设计。
您需要在 IO_Context
中存储指向客户端的指针.并在 IO_Context
中添加对客户端的引用并在 IO_Context
中发布客户端析构函数。
class IO_Context : public OVERLAPPED {
Client *client;
ULONG opcode;
// ...
public:
IO_Context(Client *client, ULONG opcode) : client(client), opcode(opcode) {
client->AddRef();
}
~IO_Context() {
client->Release();
}
void OnIoComplete(ULONG transfered) {
OnIoComplete(RtlNtStatusToDosError(Internal), transfered);
}
void OnIoComplete(ULONG error, ULONG transfered) {
client->OnIoComplete(opcode, error, transfered);
delete this;
}
void CheckIoError(ULONG error) {
switch(error) {
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
OnIoComplete(error, 0);
}
}
};
那你有没有单例IO_Context
?如果是,这是 fatal error 。 IO_Context
每个 I/O 操作都必须是唯一的。
if (IO_Context* ctx = new IO_Context(client, op))
{
ctx->CheckIoError(WSAxxx(ctx) == 0 ? NOERROR : WSAGetLastError());
}
来自工作线程s
ULONG WINAPI WorkerThread(void * param)
{
ULONG_PTR key;
OVERLAPPED *overlapped;
ULONG transfered;
while(GetQueuedCompletionStatus(hIOCP, &transfered, &key, &overlapped, INFINITE)) {
switch (key){
case '_io_':
static_cast<IO_Context*>(overlapped)->OnIoComplete(transfered);
continue;
case 'stop':
// ...
return 0;
default: __debugbreak();
}
}
__debugbreak();
return GetLastError();
}
类似while(!HasOverlappedIoCompleted(&overlapped)) Sleep(1);
的代码总是错的。绝对的,永远的。永远不要写这样的代码。
ctx = (IO_Context *)overlapped;
尽管在你的具体情况下这给出了正确的结果,但不是很好并且如果你更改 IO_Context
的定义可能会中断.你可以使用 CONTAINING_RECORD(overlapped, IO_Context, overlapped)
如果你使用 struct IO_Context{
但最好使用
OVERLAPPED overlapped; }class IO_Context : public OVERLAPPED
和 static_cast<IO_Context*>(overlapped)
现在关于为什么 IOCP 选择这个事件?如何处理 IOCP 中的这种“不良”事件?
IOCP 没有选择。他只是在 I/O 完成时发出信号。全部。您在不同的网络操作中遇到的特定 wsa 错误完全独立于使用 IOCP 或任何其他完成机制。
on graceful disconnect 当错误代码为 0 并且在 recv 操作中传输了 0 个字节时是正常的。您需要在连接完成后永久激活 recv 请求,如果 recv 完成并传输了 0 个字节,这意味着断开连接发生了
关于c++ - 与浏览器通信时奇怪的 IOCP 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53869052/
我正在设计客户端-服务器应用程序。我的服务器将使用 C++ 并使用 IOCP 进行 TCP/IP 通信。 尚未决定用于客户端开发的技术。所以我对 IOCP 与将来要开发的客户端的兼容性有几个问题(我需
我有经典的 IOCP 回调,它以这种方式使 i/o 挂起的请求出列、处理它们并释放它们: struct MyIoRequest { OVERLAPPED o; /* ... other params
我有 IOCP 应用程序,每个套接字上下文存储 64kb 缓冲区。它使用大量 RAM,同时处理数千个套接字。相反,我想切换到每个 iocp 线程上下文有 64kb 缓冲区的模型(就像我可以在 epol
关闭。这个问题需要debugging details .它目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and th
正在学习IOCP,根据这个article : To begin using completion ports we need to create a Completion Port which in
我一直在脑海中传递一些关于如何在保持 KISS 的同时使用 IO 类型的架构实际包含大量连接的想法。通过网络上的示例,似乎大多数人都使用带有 CONTAINING_RECORD 的双/单链表。而且,作
我想了解为什么使用 IOCP。我可以想到两个原因: 因为 WSARecv() 不会阻塞,所以我可以处理 1000 个客户端,而不必为每个客户端创建一个新线程(另外,您可以创建的线程数量是有限制的,因此
我最近在 windows 平台上遇到了一个叫做 IOCP 的东西,更准确地说:输入/输出控制端口。这似乎是最有效的编写服务器软件代码的方式,因为它需要同时容纳数千名用户。(如果我错了请纠正我,但是每个
到目前为止,我发现的所有示例要么只能读取或写入,要么是 10000 行的巨兽,我什至不知道从哪里开始了解它们的工作原理。 为了测试我的代码,我将浏览器指向我的服务器并发送了一个简单的 http 请求。
我应该读取前 9 个字节,其中应包括数据包的协议(protocol)和传入大小。 当完成端口返回 9 个字节时哪个更好? (性能/良好实践或美学方面) 在套接字上发送另一个重叠读取,这次使用数据包的大
据我了解Windows Server 2003/2008和C++编程下的IOCP,它们或多或少是服务多个套接字(而不是选择)或将多个线程 bundle 在一起以服务那些请求的最高性能方式。 但是,如果
我正在构建 IOCP/RIO Winsock 服务器,并且当客户端触发我的 AcceptEx() 调用时,一直在努力从完成端口获取正确的通知要求。 当我在发送客户端请求后调用 GetQueuedCom
因此,我构建了一个高度多线程的并发应用程序,它管理自己的工作线程(因为它们在整个应用程序生命周期内运行)。现在我使用 SocketAsyncEventArgs 进行网络连接,它在后台使用 I/O 完成
当我调用 BeginExecuteReader (SQL) 时,它是否使用 IO 完成端口?是什么让它成为异步的?我应该从 Begin[Operation]() 还是从 IAsyncResult 构造
这是Microsoft Windows 网络编程 的代码片段: ... // Determine how many processors are on the system. GetSystemInf
如果 FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 未启用,那么即使操作立即成功完成,我仍然会在完成端口上收到完成通知。我想知道如果它也立即完成并出现错误,是否属于这种情况
我正在创建一个客户端程序,它与通过 LAN 连接到我的 PC 的设备进行通信。 我的程序和设备之间典型的通信如下: Program -> Device 1616000D 08 02 00 00
我读到传递给 CreateIoCompletionPort() 函数的 NumberOfConcurrentThreads 参数的理想数字是 0,它将转换为核心数。 但是,我实际应该创建多少个等待完成
我正在为从桌面客户端到浏览器的视频流编写 IOCP 服务器。双方都使用 WebSocket 协议(protocol)来统一服务器的架构(并且因为浏览器没有其他方式可以执行全双工交换)。 工作线程是这样
我可以找到大量关于启动 IOCP 服务器的文章,但没有一篇是关于正确关闭它的 =/ 完成后关闭服务器的正确方法是什么?更具体地说,我已经使用 PostQueuedCompletionStatus()
我是一名优秀的程序员,十分优秀!