boost tcp client fails to detect disconnection with server sometime - boost

I am using boost::asio::tcp::socket to connect to a Server, and call
read_some() to receive binary data from Server, the following are codes:
len = sock->read_some(boost::asio::buffer(pBuf), error);
if (error == boost::asio::error::eof) {
break;
}
else if (error) {
break;
}
But after the Server closed the socket, the client still blocked in read_some method, fail to detect disconnection with server and get the error message. Why this problem occurs?

It's either because you failed to correctly implement the protocol you are using on top of TCP or because of a design flaw in that protocol. If the protocol says to wait forever for data from the server without sending anything, then the fault is in the protocol. If the protocol says to timeout the read or to send some data and you're not doing that, then the flaw is in your implementation of the protocol.
TCP does not guarantee that a connection loss can be detected by a side that is not trying to send any data. Protocols that use TCP must be designed around this.

Related

Can't build TCP connection with peers to send handshake message in golang, Bittorrent client

I am trying to build a bittorrent client. I wrote this function to verify that I can establish connection to send messages to other peers but its not working.
func handShake(torrent *gotorrentparser.Torrent, peer Peer, peedId []byte) {
conn,err := net.Dial("tcp", peer.ip + ":" + strconv.Itoa(int(peer.port)))
if err != nil {
panic(err)
}
defer conn.Close()
}
Here peer is a struct of string ip and uint16 port.
Getting the following error:
panic: dial tcp 152.57.73.47:27569: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Couldn't find any similar issues. I tried to fix my local port address to be same as what I used to send the announce request but that didn't work either.
Edit: I tried with a different torrent, it is failing for some peers, but now it is working for some other peers. Is the issue only when the peer is using utorrent as bittorrent clients?
If you're developing a bittorrent client you should start with a more reproducable setup such as running an existing client with a known infohash on your local machine and then connecting to that (without using a tracker). Once you got that to work you can work on a tracker client implementation and then put those pieces together.
The internet is unreliable and bittorrent consists of many moving parts, so a single connection failure won't tell you much, you'll have to ensure that each part works properly and try with torrents that you have tested in an existing client to narrow down the cause of problems.
After few days here is the problem that I found.
Not all peers are able to accept an inbound request as they are behind a NAT.
Even when I hosted a torrent from one of my computer and tried to download through another system, I couldn't download as there was no reply from the peer for the SYN message being sent.
I was only able to download, when both the clients were on the same network and local peer discovery was enabled, and the TCP connection was also build with the local IP address.

Can Windows Socket #10060 error (WSAETIMEDOUT - An attempt to connect timed out without establishing a connection) be a programming error at all?

I have Delphi application that uses Indy HTTP components (that uses Windows socket). And from time to time I am receiving #10060 socket error (WSAETIMEDOUT - An attempt to connect timed out without establishing a connection) upon execution of Indy procedure:
CheckForSocketError(IdWinsock2.Connect(ASocket, #LAddr, SizeOf(LAddr)));
...
connect : TconnectProc;
...
TconnectProc = function ( const s: TSocket; const name: PSockAddr; const namelen: Integer): Integer; stdcall;
Actually all this is just wrap around Windows connect function https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect that gives error and message WSAETIMEDOUT. So - my question is - can this be programming error at all? Even if I have server running on the other computer and even that server has trouble serving request, even in such cases the low level connect should execute normally, if the server can not serve the GET/POST request, then, of course, the errors should be but those errors should spring in only during execution of other Socket functions not in the connect function, isn't so?
I am trying to solve my problem https://serverfault.com/questions/973648/is-it-possible-that-unencrypted-traffic-can-cause-windows-socket-10057-10060?noredirect=1#comment1266907_973648 and now I am seeking whats happening in my code.
My server side code is very simple - it is just TIdHttpServer component with implemented (I provide event name only here):
MyForm.IdHTTPServerCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
So - what can be worng with my implemention, what can lead to the appearance of WSAETIMEDOUT for connect? Yes, my procedure can be long sometimes, but it for years it returned the answer sucessfully and there were no communication errors. And I guess, that connect function can even not depend (does not use/raise) OnCommandGet event, so, I have no control how the server side handles socket connect function from the client?
It may be possible that this is connected with TCP (not HTTP) keepalive, maybe some Windows updates have reduced client-side settings of Windows TCP keepalive for the clients and now this manifests as such error.
Indy TCP clients, like TIdHTTP, have a public ConnectTimeout property, which is set to 0 (infinite) by default. If no timeout is specified, a hard-coded 2 minute timeout is used if the client's TIdTCPClient.Connect() method is called in the main UI thread and TIdAntiFreeze is active, otherwise no timeout is used.
If a timeout is used, Indy calls Winsock's connect() function in a worker thread and waits for that thread to terminate. If Indy's timeout elapses, the socket is closed to abort the connect(), and then EIdConnectTimeout is raised to the caller. If connect() exits before Indy's timeout elapses, an exception is raised to the caller only if connect() failed.
If no timeout is used, Indy calls Winsock's connect() directly, waits for it to exit on its own accord, and then raises an exception only if failed.
So, the ONLY way you can get a WSATIMEDOUT error from Indy when it is calling Winsock's connect() function is if Winsock itself timed out internally before Indy's own timeout elapses. That does not necessarily indicate a problem in your code. It just means that the Host you are trying to connect to is simply not reachable at that moment in time. If the server were reachable, but could not accept your connection, you would get a different error, such as WSAECONNREFUSED.
If your server is behind a firewall or router, make sure it is not blocking connections from reaching your server. Try running a packet sniffer on the server machine, such as WireShark, and make sure the 3-way TCP handshake from TIdHTTP is reaching the server machine correctly.

Boost asio network disconnection handling

I am using boost asio for my TCP Server, in this I am using async_read_some for reading .
Application is working fine when network is connected, normal connection closing are handled correctly like (EOF,abrupt closing).
But my problem is I am not getting any error when network cable is unplugged. socket is open , and I get error when I am writing on socket. This is the way socket work.
Question: Can this is handled in Boost asio by any method?
You may want to set "keep alive" on the socket, see Socket closed notification. Try:
socket.set_option(boost::asio::socket_base::keep_alive(true));

TCP Hole Punching

I'm trying to implement TCP hole punching with windows socket using mingw toolchain. I think the process is right but the hole doesn't seems to take. I used this as reference.
A and B connect to the server S
S sends to A, B's router IP + the port it used to connect to S
S does the same for B
A start 2 threads:
One thread tries connecting to B's router with the info sent by S
The other thread is waiting for an incoming connection on the same port used to connect to its router when it connected to S
B does the same
I have no issue in the code I think since:
A and B does get each other ip and port to use
They are both listening on the port they used to connect to their router when they contacted the server
They are both connecting to the right ip and port but get timed out (code error 10060)
I am missing something ?
EDIT: With the help of process explorer, I see that one of the client managed to establish a connection to the peer. But the peer doesn't seems to consider the connection to be made.
Here is what I captured with Wireshark. For the sake of the example, the server S and the client A are on the same PC. The server S listens on a specific port (8060) redirected to that PC. B still tries to connect on the right IP because it sees that the public address of A sent by S is localhost and therefore uses the public IP of S instead. (I have replaced the public IPs by placeholders)
EDIT 2: I think the confusion is due to the fact that both incoming and outcoming connection request data are transfered on the same port. Which seems to mess up the connection state because we don't know which socket will get the data from the port. If I quote msdn:
The SO_REUSEADDR socket option allows a socket to forcibly bind to a
port in use by another socket. The second socket calls setsockopt with
the optname parameter set to SO_REUSEADDR and the optval parameter set
to a boolean value of TRUE before calling bind on the same port as the
original socket. Once the second socket has successfully bound, the
behavior for all sockets bound to that port is indeterminate.
But talking on the same port is required by the TCP Hole Punching technique to open up the holes !
A start 2 threads:
One thread tries connecting to B's router with the info sent by S
The other thread is waiting for an incoming connection on the same port used to connect to its router when it connected to S
You can't do this with two threads, since it's just one operation. Every TCP connection that is making an outbound connection is also waiting for an incoming connection. You simply call 'connect', and you are both sending outbound SYNs to make a connection and waiting for inbound SYNs to make a connection.
You may, however, need to close your connection to the server. Your platform likely doesn't permit you to make a TCP connection from a port when you already have an established connection from that same port. So just as you start TCP hole punching, close the connection to the server. Bind a new TCP socket to that same port, and call connect.
A simple solution to traverse into NAT routers is to make your traffic follow a protocol that your NAT already has an algorithm for forwarding, such as FTP.
Use Wireshark to check tcp connection request(3-way Handhsake process) is going properly.
Ensure your Listener thread is having select() to de-multiplex the descriptor.
sockPeerConect(socket used to connect Other peer) is FD_SET() in Listener Thread.
Ensure your are checking
int Listener Thread()
{
while(true)
{
FD_SET(sockPeerConn);
FD_SET(sockServerConn);
FD_SET(nConnectedSock );
if (FD_ISSET(sockPeerConect)
{
/// and calling accept() in side the
nConnectedSock = accept( ....);
}
if (FD_ISSET(sockServerConn)
{
/// receive data from Server
recv(sockServerConn );
}
if (FD_ISSET(nConnectedSock )
{
/// Receive data from Other Peer
recv(nConnectedSock );
}
}
}
5.Ensure you are simultaneously starting peer connection A to B and B to A.
6.Start your Listener Thread Prior to Connection to server and Peer and have Single Listener Thread for receiving Server and Client.
not every router supports tcp hole punching, please check out the following paper which explains in detail:
Peer-to-Peer Communication Across Network Address Translators

Receiving datagrams using Udp connection

In order to receive datagrams through an UDP connection I have created an object of type UDPClient.
receivedNotificationSock = new UdpClient();
However once done and on using the receive method:
receivedHostNameBuffer=receivedNotificationSock.Receive(ref receivedNotificationIP);
I am getting an exception saying that I must call the bind method.
But there is no bind method in the UDPClient class.
Could You guys please provide me with the code if possible as to what should be done to overcome this exception.
You need I think to know some more about sockets.
All sockets possess a port number. First, you create a socket - which is almost useless on its own. It just floats there. But then you bind it - you assign it a port number. Now it's useful - now you can send and receive data on it.
Remember, all UDP communications are defined by the quad data set of the IP and port of the source and the IP and port of the destination. A freshly created socket doesn't have an IP address or port; binding gives it an IP address and port.
Unfortunately, I'm not a C# programmer, so I can't properly answer your question. But at least you know why it's important.
Pass the port number into the constructor of your UDP client.
receivedNotificationSock = new UdpClient(21000);
You may need to change firewall settings to allow the bind, though a popup window normally opens when you first run this on your dev machine.
For Socket proramming you need to know the sequence of syscalls you need to do on client side and on the server side.
If you are writting a client :
you open a socket with a socket call.
you then connect to the server port with a connect call
once connect is successful
then you send the request to the server using either a send or sendto or a write
which results in reception of data that you can read using a receive or read
On Server Side
you create a socket
bind it to a port
start listening on the socket for incoming connections from various clients using a listen.
There is a non blocking way of listening for connections as well with a select syscall.
Once the you establish a connection you can essentially read the request and start processing it.
Here's an example in C# that may be useful to you.
http://www.developerfusion.com/article/3918/socket-programming-in-c-part-1/

Resources