I want to develop a client server app and I want to make it as robust as possible. There are multiple questions come up for me, and I just can't find an unambiguous answer on the internet.
Let's say, that the server is running on a while(TRUE) loop and check for command existance is it's commands queue, if there is one, it sends it, if there isn't one, it just continue to the head of the loop.
But what if the other end went down, or there is a connection error between the two, what happen to the socket value, does it become INVALID_SOCKET?
while (TRUE) {
if (ReqQueue->size() != 0 && ReqQueue->front() != string("STOP")) { // there is some command in the ReqQueue which is NOT STOP.
int sent = send(ClientSocket, ReqQueue->front().c_str(), (int)strlen(ReqQueue->front().c_str()), 0);
if (sent == (int)strlen(ReqQueue->front().c_str()))
ReqQueue->pop(); // Next Command.
else if (int err = WSAGetLastError() == WSAETIMEDOUT){
shutdown(ClientSocket, SD_BOTH);
closesocket(ClientSocket);
return;
}
else
continue;
}
else if (ReqQueue->size() == 0) {
continue;
}
else if(ReqQueue->front() == string("STOP"))
{
if (send(ClientSocket, "STOP", strlen("STOP"), 0) == strlen("STOP")) {
/*Message received indication from target*/
shutdown(ClientSocket, SD_BOTH);
closesocket(ClientSocket);
return;
}
}
}
shutdown(ClientSocket, SD_BOTH);
closesocket(ClientSocket);
return 0;
that's the source :)
what I want to ask is, there is a better way to implement the above goal, maybe I can change the while loop condition to something like while(the socket is OK) or while(there is still a connection).
what happen to the socket value
Nothing. A send() on that socket will eventually fail, and a recv() on it will deliver zero or -1, but the socket itself remains open, and the variable value is unaffected. There is no magic.
does it become INVALID_SOCKET?
No.
For me, better idea would be when you receive a request to your server from any client, just create a new thread and assign the task to that thread. By doing that you can make your server parallel processing of client request and work on multiple request from multiple client. Hence no client need to wait for server to complete the request of a client already submitted a request. If you implement like this you don’t need to bother what will happened if a connection broken. In normal case if a correction broken you will get this information in your server while sending the reply to the client and you can mark that process as failed and log it into server log.
Related
I'm trying winsock example from Microsoft docs,
client code
https://learn.microsoft.com/en-us/windows/win32/winsock/complete-client-code
server code
https://learn.microsoft.com/en-us/windows/win32/winsock/complete-server-code
Problem I'm facing is connect function in client code returns valid socket fd without accepting from server side, I removed the accept function but client still able to connect and return valid socket fd.
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
while (1)
{
Sleep(1000);
}
Any help is appreciated
If listen() returns success, the OS will accept requests for new connections for you in the background and put the new connections into an internal queue, which accept() will then pull from. So, even if the server code never calls accept(), new connections will still be accepted in the background as long as the queue is not full. If the queue does fill up, subsequent requests for new connections will fail with a (WSA)ECONNREFUSED error.
After developing a sample client server application which can exchange some data, I'm trying to implement the retry mechanism into it. Currently my application is following below protocol:
Client connects to server (non blocking mode) with 3 secs timeout and with 2 reties.
Start sending data from client with fixed length. Send has some error checking whether it is sending the complete data or not.
Receive response (timeout: 3secs) from server and verify that. If incorrect response received, re-send the data and wait for response. Repeat this for two times if failed.
For the above implementation code sections look likes something below:
connect() and select() for opening connection
select() and send() for data send
select() and recv() for data receiving
Now I'm making the retries based on return types of the socket functions, and if send() or recv() fails I'm retring the same methods. But not recalling connect().
I tested the thing by restarting the server in between the data transfer, and as a result client fails to communicate with the server and it quits after several retries, I believe this is happening as because there is no connect() call on retry methods.
Any suggestions?
Example code for receiving socket data
bool CTCPCommunication::ReceiveSocketData(char* pchBuff, int iBuffLen)
{
bool bReturn = 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);
//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)))
{
DWORD dwStartTime = GetTickCount();
DWORD dwCurrentTime = 0;
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
dwCurrentTime = GetTickCount();
//receive failed due to socket error
if (iRcvLen == SOCKET_ERROR)
{
if((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000)
{
WRITELOG("Call to socket API 'recv' failed after 3 secs continuous retries, error: %d", WSAGetLastError());
bReturn = false;
break;
}
}
//connection closed by remote host
else if (iRcvLen == 0)
{
WRITELOG("recv() returned zero - time to do something: %d", WSAGetLastError());
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
else
{
WRITELOG("Call to API 'select' failed inside 'ReceiveSocketData', error: %d", WSAGetLastError());
bReturn = false;
}
return bReturn;
}
Currently my application is following below protocol:
Client connects to server (non blocking mode) with 3 secs timeout and with 2 retries.
You can't retry a connection. You have to close the socket whose connect attempt failed, create a new socket, and call connect() again.
Start sending data from client with fixed length. Send has some error checking whether it is sending the complete data or not.
This isn't necessary in blocking mode: the POSIX standard guarantees that a blocking-mode send() will send all the data, or fail with an error.
Receive response (timeout: 3secs) from server and verify that. If incorrect response received, re-send the data and wait for response. Repeat this for two times if failed.
This is a bad idea. Most probably all the data willl arrive including all the retries, or none of it. You need to make sure that your transactions are idempotent if you use this technique. You also need to pay close attention to the actual timeout period. 3 seconds is not adequate in general. A starting point is double the expected service time.
For the above implementation code sections look likes something below:
connect() and select() for opening connection
select() and send() for data send
select() and recv() for data receiving
You don't need the select() in blocking mode. You can just set a read timeout with SO_RCVTIMEO.
Now I'm making the retries based on return types of the socket functions, and if send() or recv() fails I'm retrying the same methods. But not recalling connect().
I tested the thing by restarting the server in between the data transfer, and as a result client fails to communicate with the server and it quits after several retries, I believe this is happening as because there is no connect() call on retry methods.
If that was true you would get an error that said so.
I'm having trouble with receiving data over a network using Winsock2, with Windows. I'm trying to use a simple client and server system to implement a file transfer program. With our current code, the last packet coming in doesn't get appended to the file because it's not the size of the buffer. So, the file transfer doesn't quite completely, throws an error, and breaks. It's not always the very last packet, sometimes it's earlier.
Here is a snippet of the Server code:
int iResult;
ifstream sendFile(path, ifstream::binary);
char* buf;
if (sendFile.is_open()) {
printf("File Opened!\n");
// Sends the file
while (sendFile.good()) {
buf = new char[1024];
sendFile.read(buf, 1024);
iResult = send(AcceptSocket, buf, (int)strlen(buf)-4, 0 );
if (iResult == SOCKET_ERROR) {
wprintf(L"send failed with error: %d\n", WSAGetLastError());
closesocket(AcceptSocket);
WSACleanup();
return 1;
}
//printf("Bytes Sent: %d\n", iResult);
}
sendFile.close();
}
And here is a snippet of the Client code:
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN] = "";
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if ( iResult > 0){
printf("%s",recvbuf);
myfile.write(recvbuf, iResult);
}
else if ( iResult == 0 ) {
wprintf(L"Connection closed\n");
} else {
wprintf(L"recv failed with error: %d\n", WSAGetLastError());
}
} while( iResult > 0 );
myfile.close();
When trying to transfer a file that is a dictionary, it can break at random times. For example, one run broke early in the S's and appended weird characters to the end, which isn't rare:
...
sayable
sayer
sayers
sayest
sayid
sayids
saying
sayings
╠╠╠╠╠╠╠╠recv failed with error: 10054
What can I do to handle these errors and weird characters?
The error is happening on the server side. You're getting a "Connection reset by peer" error.
This line - buf = new char[1024]; - is clearly problematic and is likely causing the server to crash because it runs out of memory. There is no clean up happening. Start by adding the appropriate delete statement, probably best placed after the send call. If that doesn't fix it I would use a small test file and step through that while loop in the server code.
P.S. A better solution than using new and delete in your loop is to reuse the existing buff. The compiler might optimize this mistake out but if it doesn't you're severely hindering the applications performance. I think you actually should just move buf = new char[1024]; outside of the loop. buf is a char pointer so read will continue to overwrite the contents of buf if you pass it buf. Re allocating the buffer over and over is not good.
With regard to the error MSDN says:
An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.
First, using the new operator in a loop might not be good, especially without a corresponding delete. I'm not a C++ expert, though (only C) but I think it is worth checking.
Second, socket error 10054 is "connection reset by peer" which tells me that the server is not performing what is called a graceful close on the socket. With a graceful close, WinSock will wait until all pending data has been received by the other side before sending the FIN message that breaks the connection. It is likely that your server is is just closing immediately after the final buffer is given to WinSock without any time for it to get transmitted. You'll want to look into the SO_LINGER socket options -- they explain the graceful vs non-graceful closes.
Simply put, you either need to add your own protocol to the connection so that the client can acknowledge receipt of the final data block, or the server side needs to call setsocketopt() to set a SO_LINGER timeout so that WinSock will wait for the TCP/IP acknowledgement from the client side for the final block of data before issuing the socket close across the network. If you don't do at least ONE of those things, then this problem will occur.
There's also another article about that here that you might want to look at:
socket error 10054
Good luck!
I'm writing a program using the Winsock API because a friend wanted a simple program to check and see if a Minecraft server was running or not. It works fine if it is running, however if it is not running, the program freezes until, I'm assuming, the connection times out. Another issue is, if I have something like this (pseudo-code):
void connectButtonClicked()
{
setLabel1Text("Connecting");
attemptConnection();
setLabel1Text("Done Connecting!");
}
it seems to skip right to attemptConnection(), completely ignoring whats above it. I notice this because the program will freeze, but it wont change the label to "Connecting".
Here is my actual connection code:
bool CConnectionManager::ConnectToIp(String^ ipaddr)
{
if(!m_bValid)
return false;
const char* ip = StringToPConstChar(ipaddr);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(isalpha(ip[0]))
{
ip = getIPFromAddress(ipaddr);
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(MINECRAFT_PORT);
if(m_socket == NULL)
{
return false;
}
if (connect(m_socket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
{
closesocket(m_socket);
return false;
}
else
{
closesocket(m_socket);
return true;
}
return true;
}
There is also code in the CConnectionManager's contructor to start up Winsock API and such.
So, how do I avoid this freeze, and allow me to update something like a progress bar during connection? Do I have to make the connection in a separate thread? I have only worked with threads in Java, so I have no idea how to do that :/
Also: I am using a CLR Windows Form Application
I am using Microsoft Visual C++ 2008 Express Edition
Your code does not skip the label update. The update simply involves issuing window messages that have not been processed yet, that is why you do not see the new text appear before connecting the socket. You will have to pump the message queue for new messages before connecting the socket.
As for the socket itself, there is no connect timeout in the WinSock API, unfortunately. You have two choices to implement a manual timeout:
1) Assuming you are using a blocking socket (sockets are blocking by default), perform the connect in a separate worker thread.
2) If you don't want to use a thread then switch the socket to non-blocking mode. Connecting the socket will always exit immediately, so your main code will not be blocked, then you will receive a notification later on if the connection was successful or not. There are several ways to detect that, depending on which API you use - WSAAsyncSelect(), WSAAsyncEvent(), or select().
Either way, while the connect is in progress, run a timer in your main thread. If the connect succeeds, stop the timer. If the timer elapses, disconnect the socket, which will cause the connect to abort with an error.
Maybe you want to read here:
To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. http://msdn.microsoft.com/en-us/library/ms740481%28v=VS.85%29.aspx
Since you are in the blocking mode there still might be some data...
I have written complex library for managing network communication based on iocp mechanism. Problem is that when server closes the connection by calling API method closesocket() this information is sometimes transmitted to client delayed by seconds or even minutes. My code for detecting connection closure looks like this (simplified):
ok = GetQueuedCompletionStatus(completion_port, &io_size, (PULONG_PTR)&context, &overlapped, 40);
if (!ok) {
// something went broken
DWORD err = GetLastError();
if (err == ERROR_CONNECTION_REFUSED) {
// connection failed
} else if (err == ERROR_SEM_TIMEOUT) {
// connection timeout
} else if (err == ERROR_NETNAME_DELETED) {
// connection closure - point of interest
} else if (err != WAIT_TIMEOUT) {
// unknown error
}
} else {
// process incomming or outgoing data
}
Why is this happening? I need to know about connection closure immediately to be able to connect to backup server (not so heavily loaded - disconnect is happening because of this).
How are you closing the connection?
If you're just calling closesocket() then you are initiating a shutdown sequence which will attempt to ensure that all data that is currently pending will reach the destination. This can take time, especially if the network connection has been overloaded and datagrams have been lost and TCP retransmission is occurring.
If you want to close the connection straight away, and lose any pending data, then set linger to 0 and then close the socket. This will issue an RST on the connection and you'll get that much quicker.
I tried to experiment with linger parameter as Len wrote but this did not help. Adding call of shutdown() function just before closesocket() helped me. After analyzing packets reaching network interface on client (with WireShark) I have found that RST packet was replaced by FIN packet. Curiously that RST packet was not delayed. So operating system knew that connection was closed but by some unknown reason this information was transferred to application layer very delayed. I measured delays between 10 seconds and 4 minutes.