I'm working on a peer-to-peer file sharing application that can be used to transfer files without having a central server.
It works like a charm when it comes to one-to-one file transfer, however I originally planned it in a way, that the sender can send files to multiple clients.
The app uses TCP Hole Punching so port forwarding is not necessary. The problem is, that in order to make TCP hole punch work, I need to specify a local port for each TCPClient. Thus, I need to bind the sockets.
TCPClient client = new TCPClient(port);
The problem is, that when it comes to the creation of the a new client (after establishing a connection), I'll get the error which states I am unable to bind a new socket.
As far as I'm concerned, a socket is identified as a Local Port with Local IP AND Remote Port with Remote IP AND Protocol. Thus, if two sockets differ in one of these 4, they can be handled as separate connections. Yet, I still get exception.
How can I do TCP Hole Punching with multiple clients? According to
Here is the code for my Connect()
method in my custom NetClient
class. (It contains a TcpClient
and represents a connection with a remote endpoint.)
private async Task<bool> Connect(string _ip)
{
IPEndPoint _endPoint = new IPEndPoint(IPAddress.Any, port);
tcpClient = new TcpClient(_endPoint);
int tick = timeOut;
while (tick >= 0)
{
try { await tcpClient?.ConnectAsync(_ip, port); }
catch { }
if (tcpClient.Connected) return true;
tick--;
await Task.Delay(1000);
}
return false;
}
Still, no luck. Can someone help please?
CodePudding user response:
Okay, I've found a solution! For those who might stuck with the same problem, apparently there is an option to tell the TCP socket that the local endpoint can be reused.
Depending on the OS and the TCP implementation, you can add the option SO_REUSEADDRESS
and SO_REUSEPORT
for the socket. This will make the socket ignore endpoint duplication. (As far as I'm concerned it only works on the local endpoint.)
Head over this website for further information on the topic, it is a very good read.
Now for that, you will need to set these properties BEFORE binding the socket. So you will need to call socket.Bind()
separately from the TcpClient constructor.
Use TcpClient.Client
to access the socket of the tcpClient.
Proper way of creating a TcpClient with reusable local endpoint:
TcpClient client = new TcpClient();
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.Client.Bind(new IPEndPoint(IPAddress.Any, port));
After these steps, you should not experience any problem with socket binding. Hope that helped!