GetQueuedCompletionStatus blocking indefinitely on UDP socket - c++

I'm attempting to create a UDP client/server class that relies on IO completion ports using Winsock, but I haven't been able to get the GetQueuedCompletionStatus() function to return when new data is available. This is likely due to some misunderstanding on my part, but examples/documentation on IOCP with UDP instead of TCP are few and far between.
Here's the relevant bits of my server class, with error checking removed for brevity:
Server::Server()
{
m_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
}
void Server::StartReceiving()
{
StopReceiving();
m_iocp = CreateIoCompletionPort((HANDLE)m_receiveSocket, m_iocp, (DWORD)this, 0);
//WSAEVENT event = WSACreateEvent();
//WSAEventSelect(m_receiveSocket, event, FD_ACCEPT | FD_CLOSE);
// Start up worker thread for receiving data
m_receiveThread.reset(new std::thread(&Server::ReceiveWorkerThread, this));
}
void Server::Host(const std::string& port)
{
if (!m_initialized)
Initialize();
addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
const char* portStr = port.empty() ? kDefaultPort.c_str() : port.c_str();
int result;
AddressInfo addressInfo = AddressInfo::Create(nullptr, portStr, &hints, result); // Calls getaddrinfo()
m_receiveSocket = WSASocket(addressInfo.GetFamily(), addressInfo.GetSocketType(), addressInfo.GetProtocol(), nullptr, 0, WSA_FLAG_OVERLAPPED);
// Bind receiving socket
result = bind(m_receiveSocket, addressInfo.GetSocketAddress(), addressInfo.GetAddressLength());
StartReceiving();
}
void Server::ReceiveWorkerThread()
{
SOCKADDR_IN senderAddr;
int senderAddrSize = sizeof(senderAddr);
DWORD bytesTransferred = 0;
OVERLAPPED* pOverlapped = nullptr;
WSABUF wsaBuf = { (ULONG)m_buffer.GetWriteBufferSize(), m_buffer.GetWriteBufferPointer() };
DWORD flags = 0;
DWORD bytesReceived;
int result = WSARecvFrom(m_receiveSocket, &wsaBuf, 1, &bytesReceived, &flags, (sockaddr*)&senderAddr, &senderAddrSize, pOverlapped, nullptr);
// Process packets until signaled to exit
while (true)
{
DWORD context = 0;
BOOL success = GetQueuedCompletionStatus(
m_iocp,
&bytesTransferred,
&context,
&pOverlapped,
INFINITE);
wsaBuf.len = (ULONG)m_buffer.GetWriteBufferSize();
wsaBuf.buf = m_buffer.GetWriteBufferPointer();
flags = 0;
result = WSARecvFrom(m_receiveSocket, &wsaBuf, 1, &bytesReceived, &flags, (sockaddr*)&senderAddr, &senderAddrSize, pOverlapped, nullptr);
// Code to process packet would go here
if (m_exiting.load() == true)
break; // Kill worker thread
}
}
When my client sends data to the server, the first WSARecvFrom picks up the data correctly but the server blocks on the call to GetQueuedCompletionStatus and never returns, even if more datagrams are sent. I've also tried putting the socket into non-blocking mode with WSAEventSelect (code for that is commented above), but it made no difference.
From reading this similar post it sounds like there needs to be at least one read on the socket to trigger IOCP, which is why I added the first call to WSARecvFrom outside the main loop. Hopefully I'm correct in assuming that the client code is irrelevant if the server receives the data without IOCP, so I haven't posted it.
I'm sure I'm doing something wrong, but I'm just not seeing it.

You need to check the result code from WSARecvFrom and call GetQueuedCompletionStatus only if the return code is ERROR_IO_PENDING- if it is not either the operation completed without blocking and you have the data, or there was an error, but in any of these cases it was not posted to the I/O completion port and thus it will never be picked up by GetQueuedCompletionStatusand the call will block.
And you should not do this in one thread. The common approach is to have a thread that only polls the I/O completion port and calls some callbacks on context objects to notify about incoming/outgoing data, and the sending receiving calls are called wherever needed.

Related

Socket recv() made my string into a char "C:\User" 'C'

Why does string a return 'C' instead of "C:\Users\Desktop\Project phoneedge\ForMark\Top"?
When I tested it in a empty c++ project, and before I moved some of my code from ThreadFunction to StartButton it worked(The UI is suppose to update constantly but the socket recv() is cockblocking it causing it only update once so I moved the UI code to startbutton)
This is the server code, after start button is pressed, initiate the socket and there is a thread created to run the listen() accept() and recv(). The close button closes the socket and the thread.
Server Code(MFC project)
void CUIServerDlg::StartButton()
{
WSADATA Winsockdata;
int iTCPClientAdd = sizeof(TCPClientAdd);
WSAStartup(MAKEWORD(2, 2), &Winsockdata);
TCPServerAdd.sin_family = AF_INET;
TCPServerAdd.sin_addr.s_addr = inet_addr("127.0.0.1");
TCPServerAdd.sin_port = htons(8000);
TCPServersocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(TCPServersocket, (SOCKADDR*)&TCPServerAdd, sizeof(TCPServerAdd));
bRunning = true;
hthread = CreateThread(NULL, 0, ThreadFunction, this, 0, &ThreadID);
WaitForSingleObject(hthread, INFINITE);
funRunning = true;
while (funRunning == true) {
vector<string> caseOne;
/*string a;
char RecvBuffer[512];//this is the declaration in member class
int iRecvBuffer = strlen(RecvBuffer) + 1;*/
**a = RecvBuffer;**//a is a String, RecvBuffer is a path name like c:\user..
//Find files,This part of code is left out because it should not effect the question
//put the files found in a vector, then display it on a listbox
for (string fileVec : caseOne) {
CString fileunderPath;
string filevector1 = fileVec;
fileunderPath = filevector1.c_str();//conversion for AddString
list1.AddString(fileunderPath);
}
Sleep(1000);//The code updates every 1 second , when file names are modified is displays immediately.
}
}
I am suppose to change Sleep(1000) into WaitForSingleObject() to replace WM_Timer for the assignment but I don't know how since you need a handle, do I create another thread?
void CUIServerDlg::CloseButton()
{
bRunning = false;
funRunning = false;
WaitForSingleObject(hthread, INFINITE);
CloseHandle(hthread);
closesocket(TCPServersocket);
}
So I have never learned anything about socket and thread prior to this project, the idea of the code below is to use a thread to run a while loop to constantly check for new cilents to send things in, do make sure to correct me if the thought process is wrong.
DWORD WINAPI CUIServerDlg::ThreadFunction(LPVOID lpParam) {
CUIServerDlg* This = (CUIServerDlg*)lpParam;
while (This->bRunning == true) {
int iListen = listen(This->TCPServersocket, 10);
if (iListen == INVALID_SOCKET)
OutputDebugString(_T("FAIL LISTEN\n"));
This->sAccecpSocket = accept(This->TCPServersocket, (SOCKADDR*)&This->iTCPClientAdd, &This->iTCPClientAdd);
recv(This->sAccecpSocket, This->RecvBuffer, This->iRecvBuffer, 0);
}
return 0;
}
Client Code (Empty c++ project)
int main(){
string a = "C:\\Users\\Desktop\\Project phoneedge\\ForMark\\Top";
const char* SenderBuffer = a.c_str();
int iSenderBuffer = strlen(SenderBuffer) + 1;
WSAStartup(MAKEWORD(2, 2), &WinSockData);
TCPClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TCPServerAdd.sin_family = AF_INET;
TCPServerAdd.sin_addr.s_addr = inet_addr("127.0.0.1");
TCPServerAdd.sin_port = htons(8000);
connect(TCPClientSocket,(SOCKADDR*)&TCPServerAdd,sizeof(TCPServerAdd));
send(TCPClientSocket, SenderBuffer, iSenderBuffer, 0);
closesocket(TCPClientSocket);
WSACleanup();
system("PAUSE");
return 0;
}
The TCP protocol passes the data by byte stream.
It means the client passes the data byte by byte rather than pass all the data at one time.
When you receive the data from the client. The passing procedure maybe not be finished.
So you need to check whether the data is finished passing after receiving some data by one recv call and then save the sub-data until receiving all the data.

recv does not return after udp socket closed in linux

I am trying to write client server application using udp protocol, but I have problem with connection ending.
I open two sockets (one is a "server" and the other is a "client"), and while the server receiving from the client with async, the client send him one simple message that printed to the console.
After some sleep (to be sure the server will call again to recv) the client and server socket getting closed.
At this point I expected the recv will return -1 and the async will end.
But what actualy happen is that the recv stuck forever*.
If just before closing the socket I sending an empty package (sendToMakeExit variable set to true in the code), the recv return with that empty package, and only after next call it return -1, although the socket was closed in the first calling.
const bool sendToMakeExit = false;
const int port = 2000;
const auto addr = "127.0.0.1";
int serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(port);
inet_pton(AF_INET, addr, &target.sin_addr);
bind(serverSocket, (sockaddr *) &target, sizeof(target));
auto readAsync = std::async(std::launch::async, [&serverSocket] {
const int MAX_READ = 4096;
char readBuf[MAX_READ];
ssize_t actualRead;
do {
actualRead = recv(serverSocket, readBuf, MAX_READ, 0);
if (actualRead > 0) {
cout << readBuf << endl;
}
} while (actualRead != -1);
});
int clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
connect(clientSocket, (sockaddr *) &target, sizeof(target));
this_thread::sleep_for(chrono::seconds(1));
send(clientSocket, "test", 5, 0);
this_thread::sleep_for(chrono::seconds(1));
close(clientSocket);
if (sendToMakeExit) {
sendto(serverSocket, nullptr, 0, 0, (sockaddr *) &target, sizeof(target));
}
close(serverSocket);
*If I run this code in debug and create new breakpoint when the recv is stuck surprisingly the recv return with -1.
How can I getting the recv will return -1 when I close the socket?
Closing a socket does not guarantee that any function call in another thread that is still using that socket immediately returns. If you have calls that wait for data to come in, like recv(), select(), poll(), you must send some data to the socket for these calls to return. You do that in your code, but you don't actually exit when a zero-length UDP packet is received: change the end of the while-loop to:
} while (actualRead > 0);
However, I would recommend having a flag variable that indicates whether the thread should continue running or not, like so:
volatile bool running = true;
auto readAsync = std::async(std::launch::async, [&serverSocket, &running] {
...
while (running) {
...recv()...
}
});
...
running = false;
sendto(serverSocket, ...);
readAsync.wait();
close(serverSocket);
Note that I added a line to wait for readAsync to finish before closing the socket, in order to prevent any accidents from happening: there is a small window where the socket is invalidated, but readAsync might still call recv() on it. If you have even more threads, it might also happen that you close the socket in this thread, another thread opens a new socket and gets the same filedescriptor number as the one you just closed, and then the readAsync thread would use the wrong socket.

recvfrom() returns error 10022 when passing socket handle to thread

I'm working on UDP chat for programming classes. For now, I'm dealing with parallel in/out.
So, I'm creating thread to receive messages from server:
// in-thread
DWORD WINAPI in_thread(void* param)
{
int n; // variable receivefrom returned
char buff2[1000];
sockaddr_in client_addr;
int client_addr_size = sizeof(client_addr);
SOCKET my_sock;
my_sock = (SOCKET)param; // casting from void* to SOCKET
// reading server message
while (1)
{
n = recvfrom(my_sock, buff2, sizeof(buff2) - 1, 0, (sockaddr*)&client_addr, &client_addr_size);
// ......................
}
ExitThread(0);
}
And socket handle goes from:
hThread = CreateThread(NULL, NULL, &in_thread, (void*)sock, NULL, &ThreadId);
But I am recieving:
Error 10022: Invalid argument. (Returned by rercvfrom)
Where could it have gone wrong?
edit:
If it goes without passing to CreateThread, it works fine.
For example:
SOCKET sock;
// Opening socket
sock=socket(AF_INET, SOCK_DGRAM, 0);
int n; // variable receivefrom returned
char buff2[1000];
sockaddr_in client_addr;
int client_addr_size = sizeof(client_addr);
n= recvfrom(sock,buff2,sizeof(buff2)-1,0, (sockaddr *) &client_addr, &client_addr_size);
It works fine, socket works, no errors given, but when I pass it to createthread like in code in the question, error occures.
Using VS10, winsock2 lib.
'my_sock', and therefore 'param' and 'sock', is not a valid socket handle. Something wrong with your socket creation code.

WSASend for UDP socket triggers FD_READ when no destination available

I'm writing C++ code for UDP socket class to handle basic operations (such as connect, send and receive data). I try using network events mechanism with WSAEventSelect for these basic operations associated with the socket.
When I use WSASend to send data to a (UDP) destination that receives the data everything goes well.
However, when I use WSASend to send data to a destination that does not exist (UDP) or is not reachable through the network I get the FD_READ event triggered. This of course causes serious problems since there is no actual data to receive !!
I can't explain why is this happening - any ideas ?
Maybe I'm doing something wrong, Here are relevant parts of my code:
WSADATA m_wsaData ;
SOCKET m_Socket ;
WSAEVENT m_SocketEvent ;
if(WSAStartup(MAKEWORD(2,2), &m_wsaData) != 0)
{
// some error
}
// Create a new socket to receive datagrams on
struct addrinfo hints, *res = NULL ;
int rc ;
memset(&hints, 0, sizeof(hints)) ;
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_DGRAM ;
hints.ai_protocol = IPPROTO_UDP ;
rc = getaddrinfo("SomePC", "3030", &hints, &res) ;
if(rc == WSANO_DATA)
{
// some error
}
if ((m_Socket = WSASocket(res->ai_family, res->ai_socktype, res->ai_protocol, NULL, 0, 0)) == INVALID_SOCKET)
{
// some error
}
// create event and associate it with the socket
m_SocketEvent = WSACreateEvent() ;
if(m_SocketEvent == WSA_INVALID_EVENT)
{
// some error
}
// associate only the following events: close, read, write
if(SOCKET_ERROR == WSAEventSelect(m_Socket, m_SocketEvent, FD_CLOSE+FD_READ+FD_WRITE))
{
// some error
}
// connect to a server
int ConnectRet = WSAConnect(m_Socket, (SOCKADDR*)res->ai_addr, res->ai_addrlen, NULL, NULL, NULL, NULL) ;
if(ConnectRet == SOCKET_ERROR)
{
// some error
}
And then, whenever I try to send some data over the socket to a (UDP socket) destination that is not listening or not reachable I always get the FD_READ triggered:
char buf[32] ; // some data to send...
WSABUF DataBuf;
DataBuf.len = 32;
DataBuf.buf = (char*)&buf;
DWORD NumBytesActualSent ;
if( SOCKET_ERROR == WSASend(m_Socket, &DataBuf, 1, &NumBytesActualSent,0,0,0))
{
if(WSAGetLastError() == WSAEWOULDBLOCK) // non-blocking socket - wait for send ok ?
{
// handle WSAEWOULDBLOCK...
}
else
{
// some error
return ;
}
}
int ret = WSAWaitForMultipleEvents(1, &m_SocketEvent, FALSE, INFINITE, FALSE) ;
if(ret == WAIT_OBJECT_0)
{
WSANETWORKEVENTS NetworkEvents ;
ZeroMemory(&NetworkEvents, sizeof(NetworkEvents)) ;
if(SOCKET_ERROR == WSAEnumNetworkEvents(m_Socket, m_SocketEvent, &NetworkEvents))
{
return ; // some error
}
if(NetworkEvents.lNetworkEvents & FD_READ) // Read ?
{
if(NetworkEvents.iErrorCode[FD_READ_BIT] != 0) // read not ok ?
{
// some error
}
else
{
TRACE("Read Event Triggered ! - Why ? ? ? ? ? ? ?\n") ;
}
}
}
Any help or insights would be most appriciated !
Thanks,
Amit C.
The easiest way to inspect what really happens is to use Wireshark to capture packets. I do not have Windows PC nearby to provide you a complete example, but my guess is that this is a normal behaviour - you try to send UDP datagram, it gets dropped by a router (host not found) or rejected by a server (socket closed); an ICMP message is sent back to inform you about the failure, which is what you receive and get an event for. Since there is no actual user data, underlying stack translates ICMP message and provides you an appropriate error message via WSARecv() return code. The "false" FD_READ event is necessary since UDP is connectionless protocol and there are no other means to inform about a network state. Simply add error handling for WSARecv() and your code should work fine.

IOCP C++ TCP client

I am having some trouble implementing TCP IOCP client. I have implemented kqueue on Mac OSX so was looking to do something similar on windows and my understanding is that IOCP is the closest thing. The main problem is that GetCompetetionStatus is never returning and always timeouts out. I assume I am missing something when creating the handle to monitor, but not sure what. This is where I have gotten so far:
My connect routine: (remove some error handling for clarity )
struct sockaddr_in server;
struct hostent *hp;
SOCKET sckfd;
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ((hp = gethostbyname(host)) == NULL)
return NULL;
WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED)
if ((sckfd = WSASocket(AF_INET,SOCK_STREAM,0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("Error at socket(): Socket\n");
WSACleanup();
return NULL;
}
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr = *((struct in_addr *)hp->h_addr);
memset(&(server.sin_zero), 0, 8);
//non zero means non blocking. 0 is blocking.
u_long iMode = -1;
iResult = ioctlsocket(sckfd, FIONBIO, &iMode);
if (iResult != NO_ERROR)
printf("ioctlsocket failed with error: %ld\n", iResult);
HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0);
CreateIoCompletionPort((HANDLE)sckfd, hNewIOCP , ulKey, 0);
connect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr));
//WSAConnect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr),NULL,NULL,NULL,NULL);
return sckfd;
Here is the send routine: ( also remove some error handling for clarity )
IOPortConnect(int ServerSocket,int timeout,string& data){
char buf[BUFSIZE];
strcpy(buf,data.c_str());
WSABUF buffer = { BUFSIZE,buf };
DWORD bytes_recvd;
int r;
ULONG_PTR ulKey = 0;
OVERLAPPED overlapped;
OVERLAPPED* pov = NULL;
HANDLE port;
HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0);
CreateIoCompletionPort((HANDLE)ServerSocket, hNewIOCP , ulKey, 0);
BOOL get = GetQueuedCompletionStatus(hNewIOCP,&bytes_recvd,&ulKey,&pov,timeout*1000);
if(!get)
printf("waiton server failed. Error: %d\n",WSAGetLastError());
if(!pov)
printf("waiton server failed. Error: %d\n",WSAGetLastError());
port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long)0, 0);
SecureZeroMemory((PVOID) & overlapped, sizeof (WSAOVERLAPPED));
r = WSASend(ServerSocket, &buffer, 1, &bytes_recvd, NULL, &overlapped, NULL);
printf("WSA returned: %d WSALastError: %d\n",r,WSAGetLastError());
if(r != 0)
{
printf("WSASend failed %d\n",GetLastError());
printf("Bytes transfered: %d\n",bytes_recvd);
}
if (WSAGetLastError() == WSA_IO_PENDING)
printf("we are async.\n");
CreateIoCompletionPort(port, &overlapped.hEvent,ulKey, 0);
BOOL test = GetQueuedCompletionStatus(port,&bytes_recvd,&ulKey,&pov,timeout*1000);
CloseHandle(port);
return true;
}
Any insight would be appreciated.
You are associating the same socket with multiple IOCompletionPorts. I'm sure thats not valid. In your IOPortConnect function (Where you do the write) you call CreateIOCompletionPort 4 times passing in one shot handles.
My advice:
Create a single IOCompletion Port (that, ultimately, you associate numerous sockets with).
Create a pool of worker threads (by calling CreateThread) that each then block on the IOCompletionPort handle by calling GetQueuedCompletionStatus in a loop.
Create one or more WSA_OVERLAPPED sockets, and associate each one with the IOCompletionPort.
Use the WSA socket functions that take an OVERLAPPED* to trigger overlapped operations.
Process the completion of the issued requests as the worker threads return from GetQueuedCompletionStatus with the OVERLAPPED* you passed in to start the operation.
Note: WSASend returns both 0, and SOCKET_ERROR with WSAGetLastError() as WSA_IO_PENDING as codes to indicate that you will get an IO Completion Packet arriving at GetQueuedCompletionStatus. Any other error code means you should process the error immediately as an IO operation was not queued so there will be no further callbacks.
Note2: The OVERLAPPED* passed to the WSASend (or whatever) function is the OVERLAPPED* returned from GetQueuedCompletionStatus. You can use this fact to pass more context information with the call:
struct MYOVERLAPPED {
OVERLAPPED ovl;
};
MYOVERLAPPED ctx;
WSASend(...,&ctx.ovl);
...
OVERLAPPED* pov;
if(GetQueuedCompletionStatus(...,&pov,...)){
MYOVERLAPPED* pCtx = (MYOVERLAPPED*)pov;
Chris has dealt with most of the issues and you've probably already looked at plenty of example code, but...
I've got some free IOCP code that's available here: http://www.serverframework.com/products---the-free-framework.html
There are also several of my CodeProject articles on the subject linked from that page.