.NET TCP socket with session - c#

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.

Related

UDP and port randomisation

I'm currently programming a UDP application which allows clients to login. After that, their endpoint gets stored in a list.
private void socket_Callback(IAsyncResult result_)
{
EndPoint remote = new IPEndPoint(IPAddress.Any, 0);
socket.EndReceiveFrom(result_, ref remote);
if (!listOfEndPoints.Contains(remote))
{
// registration process
// add it to list
listOfEndPoints.Add(remote)
}
else
{
// process packet
}
}
However, sometimes a client's NAT will assign every packet a different external end point. If the registration packet's source end point is 12.34.56.78:1000, that end point gets added to the list. If the same client then however sends another packet, the NAT will assign it a different port, so its source end point will be 12.34.56.78:1001.
This results in the server assuming the client is not registered and try to process the packet as a registration one. Needless to say this won't work.
A way of fixing this would be to send an ID (which could however be faked easily, if it's not super-cryptic) to the client. However, the client would have to add it to each packet it sends to the server.
So it wouldn't be very effective to do it like that.
Is there any other way of telling that the packet has come from the same client as the registration packet?
You should definitely not use the source IP address and port of a UDP packet to associate it with a logical connection. You should include the ID of the connection in each packet and update the IP and port you respond to if you receive a new IP and port for the same logical connection. If connection hi-jacking is an issue, you may need to implement some form of security, such as a secure checksum in the datagram.
TCP handles associating packets with connections for you. With UDP, you must associate datagrams with logical sessions yourself. I don't know why you think it "wouldn't be very effective to do it like that".
One of the tradeoffs of UDP is that if you need anything TCP provides, you have to code it yourself.
By the way, I've never seen ports shift in this way. Are you sure the client code isn't broken, perhaps opening a new socket for each datagram it sends.

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

Pass tcp connection to different computer as a paramter

I been trying to learn the TCP protocol. so I am able to transfer data between two computers on the same network as:
\\the server:
var server = new TcpListener(ipAddress, port); //create a server
server.Start(); //start the server
//wait here until a client get's connected to this computer
TcpClient someClient = server.AcceptTcpClient();
NetworkStream stream = someClient.GetStream(); \\create a network stream object in order to read and send data to the other connected computer
once the server is running, the client application will be able to connect to the server as:
//Client:
var client = new TcpClient(serverIP, port);
NetworkStream stream = client.GetStream();
//initialize data to by some byte array. the data you wish to send
stream.Write(data, 0, data.Length);
byte[] bytes = new bytes[1000] //instantiate a byte array
connection.stream.Read(bytes, 0, bytes.Length) // wait here until data is send
// in this line data has been received and it will be placed the bytes array
I label each node with a different letter. for example note how computer A and computer C happen to be on the same network:
Hope this picture helps me explain what I mean.
Establish connection between computer A and C:
If I want to establish a connection between computer A and C then that will probably be the easiest case. In other words I will not have to do any kind of port forwarding in order to establish the connection.
Establish connection between computer A and server S:
if the client computer happens to be computer A and the server computer happens to be computer S then computer A will be able to find server S. So this connection should be simple too.
Establish connection between computer A and computer B: (here is my problem)
So if computer A wants to find computer B then router Y will have to forward all traffic from port 'somePort P' to computer B and A will have to provide the ip address of router Y or the WAN ip address.
Until this point my title question should not make sense let me explain what I mean when I say "Pass tcp connection to different computer"
I actually need to establish a connection between computer A and computer B without having to configure the routers. I been thinking a lot and I think this should be possible: (it's a little crazy though)
if you remember from my previous code once the connection was established, everything that was send/written through the network stream is received by the other end of the connection. so because computer A is not able to connect to computer B then make A connect to the server S. So far there is a connection between computer A and server S. Ok now establish a tcp connection between computer B and server S. by now the server S should have two distinct tcp connections, one to computer A and the other to computer B.
Because the server has two distinct tcp connections it should have also two NetworkStream objects. let's say that the tcp connection between server S and computer A has the object NetworkStream streamA; and the tcp connection with computer B has the object NetworkStream streamB;
So here comes the crazy part. The server S serializes the instantiated and working object streamB and sends that object to computer A. then computer A deserializes that object and cast it to a NetworkStream object. Computer A should now have a copy of object streamB. WHAT WILL HAPPEN IF THEN YOU SEND DATA THROUGH OUT THAT STREAM!? theorically computer B should receive the data I think. I mean maybe I will have to modify each package header information so that router Y treats those packages as if they where coming from Server S!
the reason why I want to do this is because I have created a server application and client application where it will sync files between two computers on a specific folder. a lot of times users using this program do not know how to enable port forwarding. I also know I may be able to solve this using what is called as "tcp puch holing" but I have not been able to find a working method on c#....
The method you have suggested won't work. A and B are both sending traffic to S. No matter what S sends to A or B, A and B will still have their routers (X and Y) passing traffic only to S. What you want is, as you mentioned, NAT hole punching.
The basic idea of NAT hole punching is to have A send traffic to B and B send traffic to A simultaneously. If done correctly, each router (X and Y) will think the packet from the other side is a reply to the packet it sent, and they will establish NAT entries allowing A and B to talk to each other directly. This is almost impossible to do for TCP, but can be done for UDP.
Trying to send streams or connections over connections is akin to trying to send a telephone over a telephone line. It is what philosophers call a category mistake. You can't do it. A stream is an aspect of a socket, and a socket is an endpoint of an existing connection. Period. It can't be sent anywhere.

UDP data never sent?

I'm trying to send a HTTP GET request with UDP (since the reply from the listening server is irrelevant and I don't want to block the program)
This is the code:
System.Net.Sockets.UdpClient client = new System.Net.Sockets.UdpClient();
client.Connect("www.domainname.com", 80);
string request_header = "GET /ping.php HTTP/1.1\r\nHost: www.domainname.com\r\n\r\n";
byte[] stre = System.Text.Encoding.ASCII.GetBytes(request_header);
client.Send(stre, stre.Length);
System.Net.IPEndPoint RemoteIpEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
byte[] receiveBytes = client.Receive(ref RemoteIpEndPoint);
string returnData = System.Text.Encoding.ASCII.GetString(receiveBytes);
client.Close();
First, the request doesn't seem to be receieved at the server, so I'm thinking perhaps something goes wrong when sending it?
Second, the program hangs on client.Receive(ref RemoteIpEndPoint), and just waits there. No data seems to be received.
I have tried to change...
System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
to...
System.Net.IPEndPoint(System.Net.IPAddress.Any, 80);
...but with no luck.
If you don't want to block the client then use the async socket methods, but use TCP. I doubt you'll find a web server that listens on UDP for HTTP requests.
You might also want to look at WireShark which is a network traffic logging tool; you could have used this to see that your UDP datagram most probably WAS being sent but there was no response was generated by the server.
You could also use netstat on the server to see that it isn't listening on UDP port 80.
When a server listens at TCP port 80 it will never be affected by a UDP frame.
TCP and UDP are different protocols. Both support "port numbers", but these are not related.
You can verify this by reading a response on the UDP socket. You should get an error result. The corresponding error code indicate the problem, hat (usually) nobody is listening at UDP port 80.

How to recover gracefully from a C# udp socket exception

Context: I'm porting a linux perl app to C#, the server listens on a udp port and maintains multiple concurrent dialogs with remote clients via a single udp socket. During testing, I send out high volumes of packets to the udp server, randomly restarting the clients to observe the server registering the new connections. The problem is this: when I kill a udp client, there may still be data on the server destined for that client. When the server tries to send this data, it gets an icmp "no service available" message back and consequently an exception occurs on the socket.
I cannot reuse this socket, when I try to associate a C# async handler with the socket, it complains about the exception, so I have to close and reopen the udp socket on the server port. Is this the only way around this problem?, surely there's some way of "fixing" the udp socket, as technically, UDP sockets shouldn't be aware of the status of a remote socket?
Any help or pointers would be much appreciated. Thanks.
I think you are right in saying: 'the server should not be aware'. If you send an UDP packet to some IP/port which may or may not be open, there is no way of knowing for the server if it reached it's destination.
The only way for the server to know is to have the client send an ACK back. (Also both the client and server must have resend mechanisms in place in cases of lost packages).
So clearly something else is going on in your code (or with the .Net udp implementation)
EDIT:
After Nikolai's remark I checked the docs. And indeed there is a distinction in .Net to about being 'connected' or 'connectionless' when using UDP.
If you use code like this:
UdpClient udpClient = new UdpClient(11000); //sourceport
try{
udpClient.Connect("www.contoso.com", 11000); //'connect' to destmachine and port
// Sends a message to the host to which you have connected.
Byte[] sendBytes = Encoding.ASCII.GetBytes("Is anybody there?");
udpClient.Send(sendBytes, sendBytes.Length);
then apparently you are 'connected'
However if you use code like this:
UdpClient udpClientB = new UdpClient();
udpClientB.Send(sendBytes, sendBytes.Length, "AlternateHostMachineName", 11000);
then you can send to whomever you choose without 'connecting'.
I'm not sure what your code looks like, but it might be worthwhile to check if you are using the correct set of commands which doesn't assume a 'connection'

Categories

Resources