Socket communication on a LAN environment - c#

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().

Related

Multiple TcpClients with same local port in 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!

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));

Use c# application (TcpListener) as a web server

I have been trying to do this for a while. I'm using the MSDN example for the TCPListener, being able to connect to it within the same newtork (i.e: from a terminal on my mobile phone).
I also understand that this works because of them being connected to the same router; I have entered to my router configurations and forwarded port 13450, which is the port I'm using on my TCP Listener constructor, to my computer's IP Address, and I've also submitted Windows firewall rules to not block this port for any external application.
After saving the port forwarding, I change IP Address on my C# Console Application to my router's IP (Default Gateway)
I've tried from hyperterminal on windows or from a terminal on my mobile phone to connect to my application (Using my router's IP and the corresponding port), but have never been able to manage this.
I would really appreciate help for this, as I cannot encounter a useful solution searching on Google
Thanks.
This is the code
Int32 port = 13450;
IPAddress localAddr = IPAddress.Parse("189.148.67.13");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
while(true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");}
The IP is the public IP for my router; and on its config i've forwarded port 13450 to my local computer. Thanks

C# Sockets, How to get IP addresses and port # of all listening (sockets) computers

I am using C# sockets to have a connection between a device (client) and a computer (server).
All is working good except for the fact that I am trying to avoid the user to enter the IP address and port number wherein to connect to on the device. Instead I want a listbox or drop down list of all IP addresses with listening sockets. Just wondering if there's a way to get all the IP addresses and port numbers of hosts with listening (socket)?
Thanks for any help! :)
What you're asking to do is called a port scan. It basically involves testing each IP address in a range and each port and reporting the success of that attempt. It's slow and it will cause a lot of alarms if there is any kind of threat monitoring on the network because port scanning is one of the ways attackers try to find network vulnerabilities.
So in short, this is a bad idea and probably won't be responsive enough for you to use for this purpose. Instead what you might consider is using a central server as a "directory" that each server would register with.
Or you can send out a broadcast on your subnet and wait for servers to respond. This is how some of the peer networking works in Windows for example. Note that this assumes you are the developer of the server as well and you can add in the logic necessary for the server to listen for broadcasts.
By using UDP broadcast, you can send out the data to the Server which are listening at the fixed port. The following is the working example.
foreach (IPAddress ip in allLocalNetworkAddresses.AddressList)
{
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//Allow sending broadcast messages
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Broadcast, 1);
//Create endpoint, broadcast.
IPEndPoint AllEndPoint = new IPEndPoint(IPAddress.Broadcast, Port);
byte[] sendData = Encoding.ASCII.GetBytes("1");
//Send message to everyone on this network
client.SendTo(sendData, AllEndPoint);
Console.Write("Client send '1' to " + AllEndPoint.ToString() +
Environment.NewLine);

.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