chihuongbk
November 19th, 2009, 08:41 AM
I'm trying to write a very simple chatting program using completion port in Winsock. I'm imaging in my program, message will be sent from Client 1 -> through Server -> Client 2 and vice versa. Thus, I wrote a worker thread for Per-Handle Data as following:
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
SOCKET sourceClientSocket;
while(TRUE)
{
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
printf("GetQueuedCompletionStatus() failed with error %d\n", GetLastError());
return 0;
}
else
printf("GetQueuedCompletionStatus() is OK!\n");
// First check to see if an error has occured on the socket and if so
// then close the socket and cleanup the SOCKET_INFORMATION structure
// associated with the socket
if (BytesTransferred == 0){
printf("Closing socket %d\n", PerHandleData->Socket);
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR) {
printf("closesocket() failed with error %d\n", WSAGetLastError());
return 0;
}
else
printf("closesocket() is fine!\n");
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
// Check to see if the BytesRECV field equals zero. If this is so, then
// this means a WSARecv call just completed so update the BytesRECV field
// with the BytesTransferred value from the completed WSARecv() call
if (PerIoData->BytesRECV == 0){
PerIoData->BytesRECV = BytesTransferred;
PerIoData->BytesSEND = 0;
}else {
PerIoData->BytesSEND += BytesTransferred;
}
if (PerIoData->BytesRECV > PerIoData->BytesSEND){
// Post another WSASend() request.
// Since WSASend() is not gauranteed to send all of the bytes requested,
// continue posting WSASend() calls until all received bytes are sent.
// Keep the source client's socket
// and pass target client's socket
sourceClientSocket = PerHandleData->Socket;
PerHandleData->Socket = targetClientSocket;
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;
//send to target client
if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != ERROR_IO_PENDING){
printf("WSASend() failed with error %d\n", WSAGetLastError());
return 0;
}
}else
printf("WSASend() is OK!\n");
}else{
PerIoData->BytesRECV = 0;
PerHandleData->Socket = sourceClientSocket; // Pass the source client socket so Server will be back to listen to the source client.
// Now that there are no more bytes to send post another WSARecv() request
Flags = 0;
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != ERROR_IO_PENDING){
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return 0;
}
}else
printf("WSARecv() is OK!\n");
}
}
}
But it can only forward the first message. From the second one, it runs incorrectly.
Can someones please point me how to fix it or if there is any alternative way to do so using completion port? Thanks.
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
SOCKET sourceClientSocket;
while(TRUE)
{
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
printf("GetQueuedCompletionStatus() failed with error %d\n", GetLastError());
return 0;
}
else
printf("GetQueuedCompletionStatus() is OK!\n");
// First check to see if an error has occured on the socket and if so
// then close the socket and cleanup the SOCKET_INFORMATION structure
// associated with the socket
if (BytesTransferred == 0){
printf("Closing socket %d\n", PerHandleData->Socket);
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR) {
printf("closesocket() failed with error %d\n", WSAGetLastError());
return 0;
}
else
printf("closesocket() is fine!\n");
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
// Check to see if the BytesRECV field equals zero. If this is so, then
// this means a WSARecv call just completed so update the BytesRECV field
// with the BytesTransferred value from the completed WSARecv() call
if (PerIoData->BytesRECV == 0){
PerIoData->BytesRECV = BytesTransferred;
PerIoData->BytesSEND = 0;
}else {
PerIoData->BytesSEND += BytesTransferred;
}
if (PerIoData->BytesRECV > PerIoData->BytesSEND){
// Post another WSASend() request.
// Since WSASend() is not gauranteed to send all of the bytes requested,
// continue posting WSASend() calls until all received bytes are sent.
// Keep the source client's socket
// and pass target client's socket
sourceClientSocket = PerHandleData->Socket;
PerHandleData->Socket = targetClientSocket;
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;
//send to target client
if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != ERROR_IO_PENDING){
printf("WSASend() failed with error %d\n", WSAGetLastError());
return 0;
}
}else
printf("WSASend() is OK!\n");
}else{
PerIoData->BytesRECV = 0;
PerHandleData->Socket = sourceClientSocket; // Pass the source client socket so Server will be back to listen to the source client.
// Now that there are no more bytes to send post another WSARecv() request
Flags = 0;
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != ERROR_IO_PENDING){
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return 0;
}
}else
printf("WSARecv() is OK!\n");
}
}
}
But it can only forward the first message. From the second one, it runs incorrectly.
Can someones please point me how to fix it or if there is any alternative way to do so using completion port? Thanks.