Related
Edit: Working on a solution - turns out googling 204.204.204.204 gets me further than more descriptive requests.
Honestly. Wits end. I have no idea how I can spend an entire day doing something that took 10 minutes in Flask (Server) and Javascript (client). I need this to run in C++ and allow a client to connect via BlueStacks' port on the same machine. The client is unimportant because I can't even get that far.
I've tried WinSocks, I've tried WxWidget's networking implementation, I've even tried some random C++ wrapper thing. All of them failed (typically within the EXAMPLE! As in, copy paste and errors everywhere). So I ultimately went back to WinSockets and followed a tutorial on YouTube.
int ServerStuff() {
WSADATA WsData;
WORD ver = MAKEWORD(2, 2);
int wsOK = WSAStartup(ver, &WsData);
if (wsOK != 0) {
wxLogMessage("Can't initialize Winsock! Quitting");
return false;
}
//Create a socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET) {
wxLogMessage("Can't create a socket! Quitting");
return false;
}
//Bind the ip and port to a socket
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; //Could also use inet_pton
bind(listening, (sockaddr*)&hint, sizeof(hint));
//Tell winsock the socket is for listening
listen(listening, SOMAXCONN);
//Wait for a connection
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
//if (clientSocket == INVALID_SOCKET) {
// wxLogMessage("Client Invalid Socket");
// return false;
//}
char host[NI_MAXHOST]; //Client's remote name
char service[NI_MAXHOST]; //Service (port) the client is connected on
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXHOST);
if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) {
wxLogMessage("Can't initialize Winsock! Quitting");
}
else {
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
wxLogMessage(host);
int wut = client.sin_port;
wxString mystring = wxString::Format(wxT("%i"), wut);
wxLogMessage("Connected on port");
wxLogMessage(mystring);
//wxLogMessage(to_string(ntohs(client.sin_port)));
}
wxLogMessage("Got this far somehow");
//Close listening socket - we don't need it anymore - later on we'll learn how to accept multiple client
closesocket(listening);
//while loop: accept and echo message back to client
char buf[4096];
while (true)
{
ZeroMemory(buf, 4096);
//Wait for client to send data
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == SOCKET_ERROR) {
//wxLogMessage("ERROR in recv");
break;
}
if (bytesReceived == 0) {
wxLogMessage("Client Disconnected");
break;
}
//Echo back to client
send(clientSocket, buf, bytesReceived + 1, 0);
//Close the socket
closesocket(clientSocket);
//Cleanup winsock
WSACleanup();
wxLogMessage("Welp");
}
}
// event handlers
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
// true is to force the frame to close
ServerStuff();
//Close(true);
}
On the YouTube video ("Creating a TCP Server in C++" - not allowed to post links) this works! The command window opens, is blank infinitely until he connects a client and then the client sends a message and the server replies with the same exact message in return.
Not mine. Mine just rushes through everything and then closes. My log used to immediately quit on the commented code where it states the client socket is invalid so I commented it out. Now my output is:
204.204.204.204
Connected on port
52428
Got this far somehow
I don't know what to do. I'm just trying to send data over a same-machine TCP connection. I'm baffled as to how this is so difficult. It seems like some random process is immediately trying to connect as a client to my server ? But why is it allowed to connect on port 52428 when I'm explicitly hosting on 54000?
My goal:
Start Server
Connect to Server using a Java App within BlueStacks
Send data from Server to Client
It makes more sense for the computer to be the server because there will be multiple BlueStacks instances and I'd prefer to not have to "spawn" multiple programs / servers for what I'm doing.
I see a few mistakes in your socket code.
not calling WSACleanup() if WSAStartup() is successful and then something goes wrong afterwards.
not calling closesocket() if socket() is successful and then something goes wrong afterwards.
not zeroing out the sockaddr_in that you pass to bind(). Random bytes in the struct can cause bind() to fail.
ignoring the return values of bind(), listen(), accept(), and send().
not treating the return value of getnameinfo() correctly. It returns 0 on success, not failure.
sending +1 extra byte back to the client than you received from the client. If the client sends fewer bytes than your buffer can hold, that extra byte would be 0x00 due to your ZeroMemory() call. But if the client actually sends enough bytes to completely fill your buffer, then you would send an extra byte from memory that you do not own. If you really want to send a null terminator after everything you echo, do so explicitly. Otherwise, a true echo server should only send back exactly what it receives, no more, no less.
Try something more like this:
void ServerStuff() {
WSADATA WsData;
int ret = WSAStartup(MAKEWORD(2, 2), &WsData);
if (ret != 0) {
wxLogMessage("Can't initialize Winsock! Error: %d", ret);
return;
}
//Create a socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listening == INVALID_SOCKET) {
wxLogMessage("Can't create a socket! Error: %d", WSAGetLastError());
WSACleanup();
return;
}
//Bind the ip and port to a socket
sockaddr_in hint = {};
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.s_addr = INADDR_ANY; //Could also use inet_pton
ret = bind(listening, (sockaddr*)&hint, sizeof(hint));
if (ret == SOCKET_ERROR) {
wxLogMessage("Can't bind socket! Error: %d", WSAGetLastError());
closesocket(listening);
WSACleanup();
return;
}
//Tell winsock the socket is for listening
ret = listen(listening, SOMAXCONN);
if (ret == SOCKET_ERROR) {
wxLogMessage("Can't listen on socket! Error: %d", WSAGetLastError());
closesocket(listening);
WSACleanup();
return;
}
//Wait for a connection
sockaddr_in client = {};
int clientSize = sizeof(client);
SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
if (clientSocket == INVALID_SOCKET) {
wxLogMessage("Can't accept a client! Error: %d", WSAGetLastError());
closesocket(listening);
WSACleanup();
return;
}
char host[NI_MAXHOST] = {}; //Client's remote name
ret = getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, NULL, 0, 0);
if (ret != 0) {
wxLogMessage("Can't get client name info! Error: %d", ret);
inet_ntop(AF_INET, &(client.sin_addr), host, NI_MAXHOST);
}
wxLogMessage("Client: %s, Connected on port: %hu", host, ntohs(client.sin_port));
//Close listening socket - we don't need it anymore - later on we'll learn how to accept multiple client
closesocket(listening);
//while loop: accept and echo message back to client
char buf[4096];
int bytesReceived;
while (true)
{
//Wait for client to send data
bytesReceived = recv(clientSocket, buf, sizeof(buf), 0);
if (bytesReceived == SOCKET_ERROR) {
wxLogMessage("Can't read from client! Error: ", WSAGetLastError());
break;
}
if (bytesReceived == 0) {
wxLogMessage("Client Disconnected");
break;
}
//Echo back to client
ret = send(clientSocket, buf, bytesReceived, 0);
if (ret == SOCKET_ERROR) {
wxLogMessage("Can't send to client! Error: ", WSAGetLastError());
break;
}
}
//Close the socket
closesocket(clientSocket);
//Cleanup winsock
WSACleanup();
wxLogMessage("Welp");
}
Trying to write a client which will try to receive data till 3 seconds. I have implemented the connect method using select by below code.
//socket creation
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_stAddress.sin_family = AF_INET;
m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchIP);
m_stAddress.sin_port = htons(iPort);
m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;
//connecting to server
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));
long iMode = 0;
iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stWrite;
FD_ZERO(&stWrite);
FD_SET(m_hSocket, &stWrite);
iResult = select(0, NULL, &stWrite, NULL, &m_stTimeout);
if((iResult > 0) && (FD_ISSET(m_hSocket, &stWrite)))
return true;
But I cannot figure out what I am missing at receiving timeout with below code? It doesn't wait if the server connection got disconnected. It just returns instantly from select method.
Also how can I write a non blocking socket call with timeout for socket send.
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
if (iRcvLen == SOCKET_ERROR)
{
return false;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
The first parameter to select should not be 0.
Correct usage of select can be found here :
http://developerweb.net/viewtopic.php?id=2933
the first parameter should be the max value of your socket +1 and take interrupted system calls into account if it is non blocking:
/* Call select() */
do {
FD_ZERO(&readset);
FD_SET(socket_fd, &readset);
result = select(socket_fd + 1, &readset, NULL, NULL, NULL);
} while (result == -1 && errno == EINTR);
This is just example code you probably need the timeout parameter as well.
If you can get EINTR this will complicate your required logic, because if you get EINTR you have to do the same call again, but with the remaining time to wait for.
I think for non blocking mode one needs to check the recv() failure along with a timeout value. That mean first select() will return whether the socket is ready to receive data or not. If yes it will go forward else it will sleep until timeout elapses on the select() method call line. But if the receive fails due to some uncertain situations while inside read loop there we need to manually check for socket error and maximum timeout value. If the socket error continues and timeout elapses we need to break it.
I'm done with my receive timeout logic with non blocking mode.
Please correct me if I am wrong.
bool bReturn = true;
SetNonBlockingMode(true);
//check whether the socket is ready to receive
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
DWORD dwStartTime = GetTickCount();
DWORD dwCurrentTime = 0;
//if socket is not ready this line will be hit after 3 sec timeout and go to the end
//if it is ready control will go inside the read loop and reads data until data ends or
//socket error is getting triggered continuously for more than 3 secs.
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
dwCurrentTime = GetTickCount();
if ((iRcvLen == SOCKET_ERROR) && ((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000))
{
bReturn = false;
break;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
SetNonBlockingMode(false);
return bReturn;
I created 2 programs in c++ using visual studio 2008: a windows service based on this service which i added tcp server code to and a client program using MFC, which has a listbox that displays the ip address and hostname of the computer thats running the service. The user then chooses a server and clicks a connect button. Since there will be many servers running, I used some PHP script on a web host site. The service sends its ip address and host name to the web host, the web host puts the information into a list and the client then accesses this list.
all this works fine with the server code but when i put the server code into the windows service, the client program freezes and doesn't respond when the connect button is clicked. The ip address and host name still appear in the client listbox, i just can't connect to the server i select.
is the windows service stopping the server code from working?
here is the cpp file that contains the server code in the service program:
char* WebPost(char Website[], char Webpage[], char Request[], int RetLen)
{
// Sends an HTTP Post request with POST Data...
SOCKET WebSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct hostent *WebHost;
WebHost = gethostbyname(Website);
if (WebHost == NULL)
{
if (WSAGetLastError() == WSANOTINITIALISED)
printf("Error Not Connected!");
else
printf("Error: %d", WSAGetLastError());
Sleep(1000);
exit(0);
}
SOCKADDR_IN SockAddr;
SockAddr.sin_port = htons(80);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)WebHost->h_addr);
connect(WebSocket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr));
char PostRequest[1024];
sprintf(PostRequest,
"POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Content-Length: %hu\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\nD=%s\0",
Webpage, Website,
strlen(Request)+2, Request
);
send(WebSocket, PostRequest, strlen(PostRequest), 0);
// Get return data
char* Data = new char[RetLen];
recv(WebSocket, Data, 4, 0);
for (;;)
{ // Skip HTTP headers
Data[0] = Data[1];
Data[1] = Data[2];
Data[2] = Data[3];
recv(WebSocket, &Data[3], 1, 0);
if (Data[0] == '\r' && Data[1] == '\n'
&& Data[2] == '\r' && Data[3] == '\n')
break;
}
int DataLen = recv(WebSocket, Data, RetLen, 0);
Data[DataLen] = '\0'; // Return the data
shutdown(WebSocket, 2);
closesocket(WebSocket);
return Data;
}
void ServStart()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
printf("WSAStartup failed with error %ld.\n", WSAGetLastError());
exit(0);
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("The dll do not support the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
WSACleanup();
exit(0);
}
//Start listening
ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListeningSocket == INVALID_SOCKET)
{
printf("Error at socket, error code: %ld.\n", WSAGetLastError());
WSACleanup();
exit(0);
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
printf("bind failed. Error code: %ld.\n", WSAGetLastError());
closesocket(ListeningSocket);
WSACleanup();
exit(0);
}
if (listen(ListeningSocket, 5) == SOCKET_ERROR)
{
printf("listen: Error listening on socket %ld.\n", WSAGetLastError());
closesocket(ListeningSocket);
WSACleanup();
exit(0);
}
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
printf("Error when getting local host name: ", WSAGetLastError());
exit(0);
}
struct hostent *phe = gethostbyname(ac);
if (phe == 0)
{
printf("Error: ", WSAGetLastError());
exit(0);
}
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[0], sizeof(struct in_addr)); // use the first ip-address
printf("IP used by Server: %s\n", inet_ntoa(addr)); // inet_ntoa(addr) provides the local address.
MyIP = inet_ntoa(addr);
char SendBuf[32];
// * is used as a separator, because it's not allowed in the hostname.
//So it won't interfere with it.
sprintf(SendBuf, "%hhu|%s*%s", cAddIP, MyIP, ac); // Send the server the IP and host name
WebPost(WEBSITE, WEBPAGE, SendBuf, 0);
printf("listening for connections...\n\n");
}
void ShutDown() // Shut down the server (tells the web server I am offline)
{
char SendBuf[32]; // Remove my IP from the list of online servers...
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
printf("Error when getting local host name: ", WSAGetLastError());
exit(0);
}
sprintf(SendBuf, "%hhu|%s*%s", cRemIP, MyIP,ac);
WebPost(WEBSITE, WEBPAGE, SendBuf, 0);
printf("Successful shutdown\n");
Sleep(1000);
WSACleanup();
}
void ServLoop()
{
SOCKADDR_IN SenderInfo;
SOCKET NewConnection;
int ByteReceived, nlen;
char recvbuff[1024];
for (;;)
{
//Main program loop
NewConnection = SOCKET_ERROR;
while(NewConnection == SOCKET_ERROR)
{
NewConnection = accept(ListeningSocket, NULL, NULL); // this is a blocking function
printf("New client got connected, ready to receive and send data...\n\n");
ByteReceived = recv(NewConnection, recvbuff, sizeof(recvbuff), 0);
if (ByteReceived > 0)
{
getsockname(ListeningSocket, (SOCKADDR *)&ServerAddr, (int *)sizeof(ServerAddr));
memset(&SenderInfo, 0, sizeof(SenderInfo));
nlen = sizeof(SenderInfo);
getpeername(NewConnection, (SOCKADDR *)&SenderInfo, &nlen);
}
}
if (shutdown(NewConnection, 2) != 0)
printf("there is something wrong with the shutdown. The error code: %ld\n", WSAGetLastError());
else
printf("shutdown is working...\n");
}
}
// --------------------------------------------
BOOL ConsoleProc(DWORD Msg)
{
switch (Msg)
{
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
ShutDown();
return false;
}
return false;
}
// -----------------------------------------------------
CSampleService::CSampleService(PWSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue) :
CServiceBase(pszServiceName, fCanStop, fCanShutdown, fCanPauseContinue),
m_dwTimeout(10 * 1000)
{
// Create a manual-reset event that is not signaled at first to indicate
// the service is stopping.
m_hStoppingEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
{
throw GetLastError();
}
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
{
throw GetLastError();
}
}
CSampleService::~CSampleService(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
if (m_hStoppingEvent)
{
CloseHandle(m_hStoppingEvent);
m_hStoppingEvent = NULL;
}
}
void CSampleService::OnStart(DWORD dwArgc, LPWSTR *lpszArgv)
{
WriteErrorLogEntry(L"CSampleService::Start: function entry");
// Log a service start message to the Application log.
WriteEventLogEntry(L"CppWindowsService in OnStart", EVENTLOG_INFORMATION_TYPE);
// Queue the main service function for execution in a worker thread.
CThreadPool::QueueUserWorkItem(&CSampleService::ServiceWorkerThread, this);
WriteErrorLogEntry(L"CSampleService::Start: function exit");
}
void CSampleService::ServiceWorkerThread(void)
{
WriteErrorLogEntry(L"CSampleService::ServiceWorkerThread: running");
// Periodically check if the service is stopping.
while (WaitForSingleObject(m_hStoppingEvent, m_dwTimeout) == WAIT_TIMEOUT)
{
// Perform main service function here...
// Handle console events
SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleProc, TRUE);
ServStart(); // Main loop is in another thread
ServLoop(); // The never returning server loop
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
WriteErrorLogEntry(L"CSampleService::ServiceWorkerThread: done");
}
void CSampleService::OnStop()
{
ShutDown(); //shut down server
SetServiceStatus(SERVICE_STOP_PENDING, ERROR_SUCCESS, 30 * 1000);
WriteErrorLogEntry(L"CSampleService::Stop: function entry");
// Log a service stop message to the Application log.
WriteEventLogEntry(L"CppWindowsService in OnStop", EVENTLOG_INFORMATION_TYPE);
// Indicate that the service is stopping and wait for the finish of the
// main service function (ServiceWorkerThread).
SetEvent(m_hStoppingEvent);
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
SetServiceStatus(SERVICE_STOP_PENDING, ERROR_INVALID_DATA, 30 * 1000);
WriteErrorLogEntry(L"OnStop: Service Start", GetLastError());
throw GetLastError();
}
WriteErrorLogEntry(L"CSampleService::Stop: function exit");
}
The answer to your specific question is, yes, a windows service can run a TCP server.
The answer to why yours does not work is not clear. At first glance the service code looks reasonable. It's possible connections to your service are being blocked by your OS firewall (i.e. your console one is in the accept list but your service one is not). You might convert some of your error printf logic into something that prints to a file instead (e.g. fprintf?) so you can take a look at what's going on...
Sure, of course it can. Why would it not be possible?
I'm writing a TCP server (blocking socket model).
I'm having trouble implementing a valid normal program exit when the server is waiting (blocking) for new connection attempts on Accept (I use WSAccept).
The code for the server's listening socket is something like this (I omitted error handling and other irrelevant code):
int ErrCode = WSAStartup(MAKEWORD(2,2), &m_wsaData) ;
// Create a new socket to listen and accept new connection attempts
struct addrinfo hints, *res = NULL, *ptr = NULL ;
int rc, count = 0 ;
memset(&hints, 0, sizeof(hints)) ;
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_STREAM ;
hints.ai_protocol = IPPROTO_TCP ;
hints.ai_flags = AI_PASSIVE ;
CString strPort ;
strPort.Format("%d", Port) ;
getaddrinfo(pLocalIp, strPort.GetBuffer(), &hints, &res) ;
strPort.ReleaseBuffer() ;
ptr = res ;
if ((m_Socket = WSASocket(res->ai_family, res->ai_socktype, res->ai_protocol, NULL, 0, 0)) == INVALID_SOCKET)
{
// some error
}
if(bind(m_Socket, (SOCKADDR *)res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
{
// some error
}
if (listen(m_Socket, SOMAXCONN) == SOCKET_ERROR)
{
// some error
}
So far so good... Then I implemented the WSAccept call inside a thread like this:
SOCKADDR_IN ClientAddr ;
int ClientAddrLen = sizeof(ClientAddr) ;
SOCKET TempS = WSAAccept(m_Socket, (SOCKADDR*) &ClientAddr, &ClientAddrLen, NULL, NULL);
Of course the WSAccept blocks until a new connection attempt is made but if I wish to exit
the program then i need some way to cause WSAccept to exit. I have tried several different approaches:
Attempt to call shutdown and/or closesocket with m_Socket from within another thread failed (program just hangs).
using WSAEventSelect indeed solves this issue but then WSAccept delivers only non-blocking sockets - which is not my intention. (Is there a way to make the sockets blocking?)
I Read about APC and tried to use something like QueueUserAPC(MyAPCProc, m_hThread, 1)) but it didn't work either.
What am I doing wrong ?
Is there a better way to cause this blocking WSAccept to exit ?
Use select() with a timeout to detect when a client connection is actually pending before then calling WSAAccept() to accept it. It works with blocking sockets without putting them into non-blocking mode. That will give your code more opportunities to check if the app is shutting down.
Go with non-blocking accepting socket (WSAEventSelect as you mentioned) and use non-blocking WSAccept. You can make a non-blocking socket that WSAccept returns into blocking socket with ioctlsocket (see msdn).
Do all the other stuff you absoultely have to on shutdown, (maybe you have DB connections to close, or files to flush?), and then call ExitProcess(0). That will stop your listening thread, no problem.
See log4cplus source for my take on this issue. I basically wait on two event objects, one is signaled when connection is being accepted (using WSAEventSelect()) and another is there to interrupt the waiting. The most relevant parts of the source is below. See ServerSocket::accept().
namespace {
static
bool
setSocketBlocking (SOCKET_TYPE s)
{
u_long val = 0;
int ret = ioctlsocket (to_os_socket (s), FIONBIO, &val);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
removeSocketEvents (SOCKET_TYPE s, HANDLE ev)
{
// Clean up socket events handling.
int ret = WSAEventSelect (to_os_socket (s), ev, 0);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
socketEventHandlingCleanup (SOCKET_TYPE s, HANDLE ev)
{
bool ret = removeSocketEvents (s, ev);
ret = setSocketBlocking (s) && ret;
ret = WSACloseEvent (ev) && ret;
return ret;
}
} // namespace
ServerSocket::ServerSocket(unsigned short port)
{
sock = openSocket (port, state);
if (sock == INVALID_SOCKET_VALUE)
{
err = get_last_socket_error ();
return;
}
HANDLE ev = WSACreateEvent ();
if (ev == WSA_INVALID_EVENT)
{
err = WSAGetLastError ();
closeSocket (sock);
sock = INVALID_SOCKET_VALUE;
}
else
{
assert (sizeof (std::ptrdiff_t) >= sizeof (HANDLE));
interruptHandles[0] = reinterpret_cast<std::ptrdiff_t>(ev);
}
}
Socket
ServerSocket::accept ()
{
int const N_EVENTS = 2;
HANDLE events[N_EVENTS] = {
reinterpret_cast<HANDLE>(interruptHandles[0]) };
HANDLE & accept_ev = events[1];
int ret;
// Create event and prime socket to set the event on FD_ACCEPT.
accept_ev = WSACreateEvent ();
if (accept_ev == WSA_INVALID_EVENT)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
ret = WSAEventSelect (to_os_socket (sock), accept_ev, FD_ACCEPT);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
do
{
// Wait either for interrupt event or actual connection coming in.
DWORD wsawfme = WSAWaitForMultipleEvents (N_EVENTS, events, FALSE,
WSA_INFINITE, TRUE);
switch (wsawfme)
{
case WSA_WAIT_TIMEOUT:
case WSA_WAIT_IO_COMPLETION:
// Retry after timeout or APC.
continue;
// This is interrupt signal/event.
case WSA_WAIT_EVENT_0:
{
// Reset the interrupt event back to non-signalled state.
ret = WSAResetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Return Socket with state set to accept_interrupted.
return Socket (INVALID_SOCKET_VALUE, accept_interrupted, 0);
}
// This is accept_ev.
case WSA_WAIT_EVENT_0 + 1:
{
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Finally, call accept().
SocketState st = not_opened;
SOCKET_TYPE clientSock = acceptSocket (sock, st);
int eno = 0;
if (clientSock == INVALID_SOCKET_VALUE)
eno = get_last_socket_error ();
return Socket (clientSock, st, eno);
}
case WSA_WAIT_FAILED:
default:
set_last_socket_error (WSAGetLastError ());
goto error;
}
}
while (true);
error:;
DWORD eno = get_last_socket_error ();
// Clean up socket events handling.
if (sock != INVALID_SOCKET_VALUE)
{
(void) removeSocketEvents (sock, accept_ev);
(void) setSocketBlocking (sock);
}
if (accept_ev != WSA_INVALID_EVENT)
WSACloseEvent (accept_ev);
set_last_socket_error (eno);
return Socket (INVALID_SOCKET_VALUE, not_opened, eno);
}
void
ServerSocket::interruptAccept ()
{
(void) WSASetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
}
A not so neat way of solving this problem is by issuing a dummy WSAConnect request from the thread that needs to do the shutdown. If the dummy connect fails, you might resort to ExitProcess as suggested by Martin.
void Drain()
{
if (InterlockedIncrement(&drain) == 1)
{
// Make a dummy connection to unblock wsaaccept
SOCKET ConnectSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (ConnectSocket != INVALID_SOCKET) {
int iResult = WSAConnect(ConnectSocket, result->ai_addr, result->ai_addrlen, 0, 0, 0, 0);
if (iResult != 0) {
printf("Unable to connect to server! %d\n", WSAGetLastError());
}
else
{
closesocket(ConnectSocket);
}
}
}
}
I have a WinSock server setup, which is properly accepting clients and relaying the appropriate information. The server takes two clients, receives a fixed size buffer of 256 bytes, stores it, and then relays the other buffer to the client. (Ie. client1 sends its buffer, server saves it, then sends client1 the buffer for client2).
Anytime client1 changes its buffer, it takes roughly 4 seconds for client2 to receive the changes. If client2 makes a change, client1 receives the update almost instantly (less than 0.1s).
Nagle's algorithm is disabled and I've tried changing the order which the server processes the requests, but client1 always lags. The data always shows up intact, but takes too long. Below is the loop the server uses to process the data:
for(;;)
{
// check if more clients can join
if (numClients < MAX_CLIENTS)
{
theClients[numClients] = accept(listeningSocket, NULL, NULL);
if (theClients[numClients] == INVALID_SOCKET)
{
nret = WSAGetLastError();
JBS::reportSocketError(nret, "server accept()");
closesocket(listeningSocket);
WSACleanup();
exit(0);
}
// disable Nagle's algorithm
int flag = 1;
int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY,
(char *) &flag, sizeof(int));
if (result < 0)
{
nret = WSAGetLastError();
JBS::reportSocketError(nret, "client connect()");
closesocket(theClients[numClients]);
WSACleanup();
}
// make the socket non-blocking
u_long iMode = 1;
ioctlsocket(theClients[numClients],FIONBIO, &iMode);
cout << "Client # " << numClients << " connected." << endl;
numClients++;
started = true;
}
else
{
// we've received all the connections, so close the listening socket
closesocket(listeningSocket);
}
// process client2
if (theClients[1] != INVALID_SOCKET)
{
memset(keys2, 0, 255);
// receive the updated buffer
nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
receiveResult = WSAGetLastError();
if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
{
JBS::reportSocketError(receiveResult, "server receive keys2()");
shutdown(theClients[1],2);
closesocket(theClients[1]);
WSACleanup();
exit(0);
break;
}
// send client1's buffer to client2
send(theClients[1],keys1,sizeof(keys1),0);
sendResult = WSAGetLastError();
if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
{
JBS::reportSocketError(sendResult, "server send keys1()");
shutdown(theClients[1],2);
closesocket(theClients[1]);
WSACleanup();
exit(0);
break;
}
}
// process client1
if (theClients[0] != INVALID_SOCKET)
{
memset(keys1, 0, 255);
// receive the updated buffer
nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
receiveResult = WSAGetLastError();
if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
{
JBS::reportSocketError(receiveResult, "server receive keys1()");
shutdown(theClients[0],2);
closesocket(theClients[0]);
WSACleanup();
exit(0);
break;
}
// send client2's buffer to client1
send(theClients[0],keys2,sizeof(keys2),0);
sendResult = WSAGetLastError();
if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
{
JBS::reportSocketError(sendResult, "server send keys2()");
shutdown(theClients[0],2);
closesocket(theClients[0]);
WSACleanup();
exit(0);
break;
}
}
Sleep((float)(1000.0f / 30.0f));
}
Client sending code:
int nError, sendResult;
sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
JBS::reportSocketError(sendResult, "client send()");
shutdown(theSocket,2);
closesocket(theSocket);
WSACleanup();
}
I've pasted your code below, with some inline comments in it, mostly because I can't fit it all reaonsably in a comment. How are you determining that it's taking four seconds for changes to get from client1 to client2? Visual inspection? Does this mean that Client1 & Client2 are running on the same machine (no different network latency issues to worry about)?
I've highlighted some blocks that look wrong. They may not be, it may be because you've tried to simplify the code that you've posted and you've missed some bits. I've also made some suggestions for where you might want to add some logging. If the sockets are really non-blocking you should be coming back from all of the calls very quickly and failing to read data, unless the client has sent it. If you've got a 4 second delay, then the problem could be:
the client hasn't sent it... is Nagle disabled on the client? If this were the case, I'd expect successive calls to recv to happen, with no data.
The recv call is taking too long... is the socket really in non-blocking mode?
The send call is taking too long... is the socket in non-blocking mode, is it buffered, is the client trying to receive the data?
Having the times each section of code takes will help to track down where your problem is.
You can get the time, using something like this (borrowed from the web):
struct timeval tv;
struct timezone tz;
struct tm *tm;
gettimeofday(&tv, &tz);
tm=localtime(&tv.tv_sec);
printf(" %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min,
m->tm_sec, tv.tv_usec);
Your code:
for(;;)
{
/* This block of code is checking the server socket and accepting
* connections, until two? (MAX_CLIENTS isn't defined in visible code)
* connections have been made. After this, it is attempting to close
* the server socket everytime around the loop. This may have side
* effects (although probably not), so I'd clean it up, just in case
*/
/* LOG TIME 1 */
// check if more clients can join
if (numClients < MAX_CLIENTS)
{
theClients[numClients] = accept(listeningSocket, NULL, NULL);
if (theClients[numClients] == INVALID_SOCKET)
{
nret = WSAGetLastError();
JBS::reportSocketError(nret, "server accept()");
closesocket(listeningSocket);
WSACleanup();
exit(0);
}
// disable Nagle's algorithm
int flag = 1;
int result = setsockopt(theClients[numClients], IPPROTO_TCP, TCP_NODELAY,
(char *) &flag, sizeof(int));
if (result < 0)
{
nret = WSAGetLastError();
JBS::reportSocketError(nret, "client connect()");
closesocket(theClients[numClients]);
WSACleanup();
}
// make the socket non-blocking
u_long iMode = 1;
ioctlsocket(theClients[numClients],FIONBIO, &iMode);
cout << "Client # " << numClients << " connected." << endl;
numClients++;
/* This started variable isn't used, is it supposed to be wrapping
* this server code in an if statement?
*/
started = true;
}
else
{
// we've received all the connections, so close the listening socket
closesocket(listeningSocket);
}
/* LOG TIME 2 */
// process client2
if (theClients[1] != INVALID_SOCKET)
{
memset(keys2, 0, 255);
// receive the updated buffer
/* LOG TIME 3 */
nBytes = recv(theClients[1], keys2, sizeof(keys2), 0);
/* LOG TIME 4 */
receiveResult = WSAGetLastError();
if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
{
JBS::reportSocketError(receiveResult, "server receive keys2()");
shutdown(theClients[1],2);
closesocket(theClients[1]);
WSACleanup();
exit(0);
break;
}
// send client1's buffer to client2
/* LOG TIME 5 */
send(theClients[1],keys1,sizeof(keys1),0);
/* LOG TIME 6 */
sendResult = WSAGetLastError();
if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
{
JBS::reportSocketError(sendResult, "server send keys1()");
shutdown(theClients[1],2);
closesocket(theClients[1]);
WSACleanup();
exit(0);
break;
}
}
// process client1
/* If the client has been accepted (note that because this
* is part of the same block of code, and there's no protection
* around it, the first connection will process it's first
* receive/send combination before the second socket has been accepted)
*/
if (theClients[0] != INVALID_SOCKET)
{
memset(keys1, 0, 255);
// receive the updated buffer
/* You're trying a receive against a non-blocking socket. I would expect this
* to fail with WSAEWOULDBLOCK, if nothing has been sent by the client, but
* this block of data will still be sent to the client
*/
/* LOG TIME 7 */
nBytes = recv(theClients[0], keys1, sizeof(keys1), 0);
/* LOG TIME 8 */
receiveResult = WSAGetLastError();
if ((receiveResult != WSAEWOULDBLOCK) && (receiveResult != 0))
{
JBS::reportSocketError(receiveResult, "server receive keys1()");
shutdown(theClients[0],2);
closesocket(theClients[0]);
WSACleanup();
exit(0);
break;
}
// send client2's buffer to client1
/* The first time around the loop, you're sending the buffer to the
* first connected client, even though the second client hasn't connected yet.
* This will continue 30 times a second, until the second client connects. Does
* the client handle this correctly?
*/
/* LOG TIME 9 */
send(theClients[0],keys2,sizeof(keys2),0);
/* LOG TIME 10 */
sendResult = WSAGetLastError();
if((sendResult != WSAEWOULDBLOCK) && (sendResult != 0))
{
JBS::reportSocketError(sendResult, "server send keys2()");
shutdown(theClients[0],2);
closesocket(theClients[0]);
WSACleanup();
exit(0);
break;
}
}
Sleep((float)(1000.0f / 30.0f));
}
Client sending code:
int nError, sendResult;
/* There's no recv / loop in this section
*/
sendResult = send(theSocket, keys, sizeof(keys),0);
nError=WSAGetLastError();
if((nError != WSAEWOULDBLOCK) && (nError != 0))
{
JBS::reportSocketError(sendResult, "client send()");
shutdown(theSocket,2);
closesocket(theSocket);
WSACleanup();
}