У меня есть следующий упрощенный IO Completion Port сервер C++ код:IO Completion Port Начальное чтение и Bi-Directional Data
int main(..)
{
startCompletionPortThreadProc();
// Await client connection
sockaddr_in clientAddress;
int clientAddressSize = sizeof(clientAddress);
SOCKET acceptSocket = WSAAccept(serverSocket, (SOCKADDR*)&clientAddress, &clientAddressSize, NULL, NULL);
// Connected
CreateIoCompletionPort((HANDLE)acceptSocket, completionPort, 0, 0);
// Issue initial read
read(acceptSocket);
}
DWORD WINAPI completionPortThreadProc(LPVOID param)
{
DWORD bytesTransferred = 0;
ULONG_PTR completionKey = NULL;
LPPER_IO_DATA perIoData = NULL;
while(GetQueuedCompletionStatus(completionPort, &bytesTransferred, &completionKey, (LPOVERLAPPED*)&perIoData, INFINITE))
{
if(WaitForSingleObject(exitEvent, 0) == WAIT_OBJECT_0)
{
break;
}
if(!perIoData)
continue;
if(bytesTransferred == 0)
{
//TODO
}
switch(perIoData->operation)
{
case OPERATION_READ:
{
// Bytes have been received
if(bytesTransferred < perIoData->WSABuf.len)
{
// Terminate string
perIoData->WSABuf.buf[bytesTransferred] = '\0';
perIoData->WSABuf.buf[bytesTransferred+1] = '\0';
}
// Add data to message build
message += std::tstring((TCHAR*)perIoData->WSABuf.buf);
// Perform next read
perIoData->WSABuf.len = sizeof(perIoData->inOutBuffer);
perIoData->flags = 0;
if(WSARecv(perIoData->socket, &(perIoData->WSABuf), 1, &bytesTransferred, &(perIoData->flags), &(perIoData->overlapped), NULL) == 0)
{
// Part message
continue;
}
if(WSAGetLastError() == WSA_IO_PENDING)
{
// End of message
//TODO: Process message here
continue;
}
}
}
break;
case OPERATION_WRITE:
{
perIoData->bytesSent += bytesTransferred;
if(perIoData->bytesSent < perIoData->bytesToSend)
{
perIoData->WSABuf.buf = (char*)&(perIoData->inOutBuffer[perIoData->bytesSent]);
perIoData->WSABuf.len = (perIoData->bytesToSend - perIoData->bytesSent);
}
else
{
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = _tcslen(perIoData->inOutBuffer) * sizeof(TCHAR);
perIoData->bytesSent = 0;
perIoData->bytesToSend = perIoData->WSABuf.len;
}
if(perIoData->bytesToSend)
{
if(WSASend(perIoData->socket, &(perIoData->WSABuf), 1, &bytesTransferred, 0, &(perIoData->overlapped), NULL) == 0)
continue;
if(WSAGetLastError() == WSA_IO_PENDING)
continue;
}
}
break;
}
}
return 0;
}
bool SocketServer::read(SOCKET socket, HANDLE completionPort)
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory(perIoData, sizeof(PER_IO_DATA));
perIoData->socket = socket;
perIoData->operation = OPERATION_READ;
perIoData->WSABuf.buf = (char*)perIoData->inOutBuffer;
perIoData->WSABuf.len = sizeof(perIoData->inOutBuffer);
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesReceived = 0;
if(WSARecv(perIoData->socket, &(perIoData->WSABuf), 1, &bytesReceived, &(perIoData->flags), &(perIoData->overlapped), NULL) == SOCKET_ERROR)
{
int gle = WSAGetLastError();
if(WSAGetLastError() != WSA_IO_PENDING)
{
delete perIoData;
return false;
}
}
return true;
}
bool SocketServer::write(SOCKET socket, std::tstring& data)
{
PER_IO_DATA* perIoData = new PER_IO_DATA;
ZeroMemory(perIoData, sizeof(PER_IO_DATA));
perIoData->socket = socket;
perIoData->operation = OPERATION_WRITE;
perIoData->WSABuf.buf = (char*)data.c_str();
perIoData->WSABuf.len = _tcslen(data.c_str()) * sizeof(TCHAR);
perIoData->bytesToSend = perIoData->WSABuf.len;
perIoData->overlapped.hEvent = WSACreateEvent();
DWORD bytesSent = 0;
if(WSASend(perIoData->socket, &(perIoData->WSABuf), 1, &bytesSent, 0, &(perIoData->overlapped), NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
delete perIoData;
return false;
}
}
return true;
}
1) Первый вопрос, который я имею с начальным чтения.
При подключении к клиенту (примите), я выдаю сообщение. Поскольку клиент еще не отправил никаких данных, WSAGetLastError() - WSA_IO_PENDING, и метод чтения возвращается.
Когда клиент отправляет данные, поток остается застрявшим в вызове GetQueuedCompletionStatus (как я предполагаю, мне нужен другой вызов WSARecv?).
Должен ли я продолжать цикл использования метода чтения до тех пор, пока данные не поступят? Это не кажется логичным, подумал я, выпустив начальное чтение GetQueuedCompletionStatus, которое завершится при поступлении данных.
2) Мне нужно читать и писать данные двунаправленно без подтверждения. Поэтому я также создал клиента с потоком IOCP. Возможно ли это сделать с помощью портов завершения или чтение должно сопровождаться записью?
Извините за то, что походит на основные вопросы, но после траления в Интернете и создания примеров IOCP я все еще не могу ответить на вопросы.
Большое спасибо заранее.
Привет Remy, Большое спасибо за долгое объяснение, это очень высоко, я схожу с ума здесь. Я буду работать с вашими комментариями и отчитываться! Я взял много кода из других примеров, которые, как я полагал, работал. – CAM79
Hi Remy, ок, новый цикл был ключевым и специально проверял GetLastError в «if (perIoData)». Ошибка ввода-вывода с WSA_OPERATION_ABORTED, поэтому он так и не завершился. У меня был поток принятия, который выдал чтение, а затем закончился. Я бы подумал, что это все равно будет работать, но, очевидно, мой дизайн ошибочен. Большое спасибо за вашу помощь, я буду использовать ваши мысли в своем новом дизайне. Надеюсь, ваши комментарии также помогут другим, поскольку многие примеры, похоже, используют код, подобный моему. – CAM79
Когда поток завершается, все запущенные операции ввода-вывода, которые все еще ожидаются, автоматически прерываются. Вы должны вызывать 'WSAAccept()' в цикле в потоке, который живет, по крайней мере, на протяжении жизни прослушивающего сокета (или переместить прием клиента в порт завершения ввода-вывода и выдавать начальный прием из такого потока), поэтому я/Os не прерывается .. –