WSASend로 packet을 전송하는 과정은 2가지 과정으로 나뉜다.
우리가 작성하는 ChattingServer에서 Session은 WSASend를 한번만 걸 수 있다.
즉, 하나의 워커 쓰레드가 WSASend를 걸면 해당 WSASend가 완료될때까지 다른 워커 쓰레드에서는 WSASend를 걸 수 없다.
SendPacket은 여러 쓰레드에서 호출할 수 있기 때문에 SendPacket에서는 packet을 큐잉을 중점으로 구현한다. ( 물론 SendPost를 호출해 WSASend 걸기를 시도 한다 )
1️⃣ SendPacket
WSASend를 걸 session을 찾고, 전송할 packet을 큐잉하고, WSASend를 건다.
void CoreNetwork::SendPacket(__int64 sessionId, Packet* packet)
{
Session* sendSession = FindSession(sessionId);
if (sendSession == nullptr)
{
return;
}
packet->Encode();
sendSession->sendQueue.push(packet);
SendPost(sendSession);
ReturnSession(sendSession);
}
2️⃣ SendPost
WSASend를 걸기전 isSend를 1로 바꾸면서 진입하고, 다른 워커쓰레드에서 isSend가 false가 될때까지 WSASend를 걸지 못하도록 막는다. 보내려고 진입했으나 sendUseSize가 0일 경우 바로나가지 않고 sendQueue의 사이즈를 다시 한번 더 확인해서 SendPost를 다시 진행한다.
이후 전송할 packet을 WSABUF에 담고 WSASend를 건다.
void CoreNetwork::SendPost(Session* sendSession)
{
int sendUseSize;
int sendCount = 0;
WSABUF sendBuf[500];
do
{
if (InterlockedExchange(&sendSession->isSend, 1) == 0)
{
sendUseSize = sendSession->sendQueue.size();
if (sendUseSize == 0)
{
InterlockedExchange(&sendSession->isSend, 0);
if (!sendSession->sendQueue.empty())
{
continue;
}
else
{
return;
}
}
}
else
{
return;
}
} while (0);
sendSession->sendPacketCount = sendCount = sendUseSize;
for (int i = 0; i < sendCount; i++)
{
if (sendSession->sendQueue.size() == 0)
{
break;
}
InterlockedIncrement(&_sendPacketTPS);
Packet* packet = sendSession->sendQueue.front();
sendSession->sendQueue.pop();
sendBuf[i].buf = packet->GetHeaderBufferPtr();
sendBuf[i].len = packet->GetUseBufferSize();
sendSession->sendPacket[i] = packet;
}
memset(&sendSession->sendOverlapped, 0, sizeof(OVERLAPPED));
InterlockedIncrement64(&sendSession->IOBlock->IOCount);
int WSASendRetval = WSASend(sendSession->clientSocket, sendBuf, sendCount, NULL, 0, (LPWSAOVERLAPPED)&sendSession->sendOverlapped, NULL);
if (WSASendRetval == SOCKET_ERROR)
{
DWORD error = WSAGetLastError();
if (error != ERROR_IO_PENDING)
{
if (InterlockedDecrement64(&sendSession->IOBlock->IOCount) == 0)
{
ReleaseSession(sendSession);
}
}
}
}