Multiple TcpClients with same local port in C# - c#

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 this wikipedia page, it should work, if the sender does not bind the sockets.
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?

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!

Related

Connect TcpClient to a remote Tcp server, binding to a specific local port

I built tcp client/server application for my organisation. The server opens and listens to a specific port, and each client establishes Tcp Connection to the server port. Nothing special. The application works beautifully. But today, one client wanted the Tcp client app to work over WiFi network with firewall. The WiFi firewall is configured to block all ports by default. If I want my application to work, I have to give their network administrator a list of ports to open for my application. The server listening port is configurable so it is easy. Once I configure the port, I can give them is this specific port for the server. However the client app is unable to connect to the server because each time a TcpClient establishes a connection, it creates a random local Tcp port that will be blocked by their firewall.
Their network admin will not open all ports for the machine because they said it created security risks for their organisation. Therefore, I am looking for a way to force the client to open a specific local port when it establishes a Tcp connection. I've both looked into MSDN docs and been Googling but I haven't found an adequate answer. Would you be able to suggest a workaround or a third party library that can do that? Thank heaps.
I'm not aware of any way to have this level of control with TcpClient. However, if you manually create the Socket object, you can bind to a local port of your choosing:
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Specify the local port to use
var endpoint = new IPEndPoint(IPAddress.Any, 9999);
sock.Bind(endpoint);
// And connect to the remote end point
sock.Connect("example.com", 80);
Of course, by doing this you limit yourself to one connection on the machine.
how about use bind() like this(if you are using c++)
struct sockaddr_in cli;
memset(&cli, 0x00, sizeof(cli));
cli.sin_family = AF_INET;
cli.sin_port = htons(YOUR PORT);
cli.sin_addr.s_addr = inet_addr(YOUR IP ADDRESS);
int on = 1;
if (setsockopt(hSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0){
perror("Set Error");
}
bind(hSocket, (struct sockaddr *)&cli, sizeof(struct sockaddr));
int nRet = connect(hSocket,(sockaddr*)&svr, sizeof(svr));

Binding same EndPoint for TcpListener and TcpClient

I have a listener that listens to the localhost on an assigned portnumber like this:
TcpListener listener = new TcpListener(localIP);
listener.Start();
while (true) {
listen:
if (!listener.Pending()) {
Thread.Sleep(100);
goto listen;
}
Socket socket = listener.AcceptSocket();
}
Now the same program should make a connection to other instances of this program like this:
TcpClient client = new TcpClient(localIP); //localendpoint should use same port
client.Connect("localhost", remotePort);
As you can see I'm binding the same IPEndPoint localIP for both the listener and client. So that whenever another instance of the program has a connection with this one, the socket in the listener could give me it's RemoteEndPoint. So that I don't get a random port number generated by the system. Of course this won't work and I get this error:
Only one usage of each socket address (protocol/network address/port)
is normally permitted
Since both listener and client are binded to the same IPEndPoint localIP.
Is there a workaround so that I can bind a listener and client to the same EndPoint? Because I want to get the portnumber from socket.RemoteEndPoint in the listener. This portnumber should be the portnumber that is assigned to each individual instance of the program.
Well, based on the comments from the question, the best solution would be just to send the port number the program is listening to over the socket connection.
I recommend switching to WCF for your communications. It gives you a lot of handy tools for abstracting over serialization, the transport protocol, security, configuration, etc. Eventually, WCF lets you build service-oriented apps for a diverse client ecosystem easier than doing all the hard work yourself.
One of the features WCF has to offer is a so-called Duplex Binding, which should give you what you want: a connecting client immediately announces a way to be called back.
If you still need to communicate your network topology, than you can still do so using WCF; And for that I recommend implementing a separate service interface that takes care of network topology exclusively.
A tutorial + example on getting started with WCF, and creating clean code can be found here and here.

Socket communication on a LAN environment

I have the following setup:
The server will connect via Bluetooth to several devices that are sending a discrete signal. Then there will be n clients that can inquiry the server (via web services probably) for which devices are connected and listen to the signal from the devices they want.
I think the best way to implement this is: when the server connects to a device via BT, it will open a Socket to a local port and send the data there. When a client ask for the available devices, the server will return a Dictionary and then the client just have to listen to that port.
But i'm having some problems with the socket implementation. How can I create and write the signal to a local port?
This is what I got so far:
class Device {
...
public EndPoint Connect() {
// create a bt connection to the device
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endPoint = new IPEndPoint(IPAddress.Parse(_localIp), 0);
_socket.Bind(endPoint);
return endPoint;
}
private void OnBtDataReceived(object sender, wclDataEventArgs e) {
_socket.Send(e.Data);
}
}
But when reaching the _socket.Send(e.Data); I receive the following exception:
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Is this the right approach but it needs to have someone connected to the socket to read the data?
You can use UDP Broadcast as the comments suggest where you don't have to establish a connection. The server justs sends the data out on one port and any interested client can receive that data on that port.
If you want to distinguish between your devices you might have to broadcast every devices data on a separate port. That might be a lot of (unnecessary) network traffic whn you have many devices.
The other option is using TCP.
Your server as to listen for incoming connections from your clients:
_socket.Bind(new IPEndPoint(IPAddress.Any, 1234));
_socket.Listen(10);
_socket.BeginAccept(onAccept, _socket);
In onAccept you have access to the clientsocket and can send data or store the reference to the socket somewhere to send data later on:
private static void onAccept(IAsyncResult ar)
{
Socket clientSocket = ((Socket) ar.AsyncState).EndAccept(ar);
// begin to accept the next client connection
_socket.BeginAccept(onAccept, _socket);
// this is the socket you can send data to the connected client:
clientSocket.Send(data);
}
The TCP approach has the advantage that your server only sends data when there are connected clients and your server is aware of the number of clients connected.
When using TCP, you don't need to call Bind(), but you need to call Connect().
_socket.Connect(endPoint);
This assumes that in your code _localIp is an IP address on the local network you want to connect to and not an IP address of a network adapter on the local computer that you want to use for the connection.
What Bind() does is to bind the socket to a local (i.e. on the current computer) end point. If you don't care what network adapter is used (which means you let the system decide that based on the target IP), and you also don't care what local port is used, there is no need to call Bind().

socket programming in C#

i'am trying to do Socket programming in C# and now i need to understand that do we need array of
sockets in Server-side in order to handle multiple clients or one socket in server-side is sufficient for
handling many clients;
And need to configure whether all data from server to client has been reached and the availability of server,discarding the client request .
Do we need to create multiple thread to handle each client also ? and i need to handle each client separetely.
Each connection will require a new socket. As spender says, you only listen with one socket, the socket API will create the other sockets when connection requests come in.
Read the documentation for accept
One listening socket can service many clients.
You can use one socket to response multiple clients. When a client connet with the server, your application creates a socket to attend this conection.
Please have a look at Writing scalable server applications using IOCP. This's a good tutorial on how to create a robust server application.
you create one listener(welcome socket) which creates a dedicate connection socket for every accepted client.
I think you need some thing like.
private void listen(){
TcpListener listener = new TcpListener(5000);
listener.Start();
while (true)
{
// Accept a new client
Socket clientSocket = listener.AcceptSocket();
//Create a thread for every client.
ParameterizedThreadStart pThreadStart = new ParameterizedThreadStart(handleClient);
Thread thread = new Thread(pThreadStart);
thread.Start(clientSocket);
}
}

.NET TCP socket with session

Is there any way of dealing with sessions with sockets in C#?
Example of my problem:
I have a server with a socket listening on port 5672.
TcpListener socket = new TcpListener(localAddr, 5672);
socket.Start();
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
TcpClient client = socket.AcceptTcpClient();
Console.WriteLine("Connected to client!");
And i have two clients that will send one byte. Client A send 0x1 and client B send 0x2.
From the server side, i read this data like this:
Byte[] bytes = new Byte[256];
String data = null;
NetworkStream stream = client.GetStream();
while ((stream.Read(bytes, 0, bytes.Length)) != 0)
{
byte[] answer = new ...
stream.Write(answer , 0, answer.Length);
}
Then client A sends 0x11.
I need a way to know that this client is the same that sent "0x1" before.
Each TcpClient instance is for a different connection. A connection in TCP consists of four things: source IP, source port, target IP, target port. So, even if you have the same target IP and port, and the same source port, you have two different connections.
Data sent by one client will not be mixed in with data sent by the other client. Data sent by a client on a connection will be received in order over that connection.
The only time that Sessions become an issue is to remember the client after the connection is closed.
You would probably need to implement your own protocol in order to identify the clients. Perhaps define a chunk of bytes, where in addition to the data you also include a client identifier.
You'll need some sort of authentication or pre-negotiated token, and all of this somehow encrypted with some salt.
There is a lot of existing literature on how sessions are implemented in the Web world (HTTP).
One key is whether you are closing the client connections, or are they persistent? If they are persistent, then simply identify them by their object reference. If not, then...
1) You can do simple sessions based on the source IP address. But if multiple clients are behind a NAT firewall, sharing the IP, then that doesn't work, see the next option.
2) Use a "cookie"
3) Use authentication to identify each client
Every option except IP based sessions requires adding something to the protocol itself.
Some things to remember with sockets. The remote IP + remote port uniquely identifies a client TCP socket. Multiple connections from the same remote client will have diffent remote ports. But you cannot rely on that if the socket closes, because the remote OS may recycle the remote port for a new connection once the old one times out.

Categories

Resources