Edit: realized I need an implementation of this on C#:
https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT
I'll try to find one, if anyone has one I'll appreciate it, thanks!
I might have just made up that name on the spot.
What I'm trying to do is have a computer that is behind NAT and will need to provide a service connect to a server.
Then, I would connect to that server using a third computer and interact with the TCP stream initiated by the first computer, in a way that would work just as if I had connected straight to it.
Found a way to create a forward proxy, which works great:
blog.brunogarcia.com/2012/10/simple-tcp-forwarder-in-c.html
using System;
using System.Net;
using System.Net.Sockets;
namespace TcpProxy
{
class Program
{
static void Main(string[] args)
{
new TcpForwarderSlim().Start(
new IPEndPoint(IPAddress.Parse("127.0.0.1"), int.Parse("69")),
new IPEndPoint(IPAddress.Parse("91.198.174.192"), int.Parse("80")));
}
}
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
Could anyone point me to the right direction?
UPNP can open a port on a properly configured router.
STUN, ICE, and TURN use intermediary servers to connect when ports can't be opened. There are open source server implementations.
UDP can use UDP Hole Punching.
This answer provides a more detailed explaination but not much C# help.
Related
I have Implemented a simple TCP forwarder which is connected to a backend server. This server is set on http://localhost:5000 and this TCP forwarder is listening to http://localhost:5001. Using another machine to generate load using wrk which is a HTTP benchmarking tool to generator load, I have 2 different results. When I send the load directly to the service which is based on asp.net core web api on the kestrel more than 230K requests/second are handled but when I send the load to this TCP forwarder 83Krequest/second can be handled. Here is the code:
using System;
using System.Net;
using System.Net.Sockets;
namespace BrunoGarcia.Net
{
static void Main(string[] args)
{
new TcpForwarderSlim().Start(
new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])),
new IPEndPoint(IPAddress.Parse(args[2]), int.Parse(args[3])));
}
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
What is the problem do you think?! How can I improve the result when I use TCP forwarder?!Or is there a better way to make a tunnel or a forwarder to listen to a port and send the TCP requests to one of 2 or more back end service?!
You do not start listening for a more data till state.DestinationSocket.Send completes. You can start listening for more data as soon as you start processing the Send, the order of multiple BeginSend calls is preserved so if you switched to that it would allow you to start processing the next request before the previous one finished.
Important note! You will now need to create a new buffer (or use a pool of buffers) for each new BeginReceive request. Below is untested code but hopefully is close enough to get you on the right path.
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
//Start an asyncronous send.
var sendAr = state.DestinationSocket.BeginSend(state.Buffer, 0, bytesRead, SocketFlags.None,null,null);
//Get or create a new buffer for the state object.
var oldBuffer = state.ReplaceBuffer();
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
//Wait for the send to finish.
state.DestinationSocket.EndSend(sendAr);
//Return byte[] to the pool.
state.AddBufferToPool(oldBuffer);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
private readonly ConcurrentBag<byte[]> _bufferPool = new ConcurrentBag<byte[]>();
private readonly int _bufferSize;
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
_bufferSize = Math.Min(SourceSocket.ReceiveBufferSize, DestinationSocket.SendBufferSize);
Buffer = new byte[_bufferSize];
}
/// <summary>
/// Replaces the buffer in the state object.
/// </summary>
/// <returns>The previous buffer.</returns>
public byte[] ReplaceBuffer()
{
byte[] newBuffer;
if (!_bufferPool.TryTake(out newBuffer))
{
newBuffer = new byte[_bufferSize];
}
var oldBuffer = Buffer;
Buffer = newBuffer;
return oldBuffer;
}
public void AddBufferToPool(byte[] buffer)
{
_bufferPool.Add(buffer);
}
}
}
My udp class is not receiving the updated value from the stream. Device sends data continuously and when any alarm activated it add the code in stream and sends the updated value but my class is not receiving the updated value unless I restart the program.
here is my UDPListener class.
public class
{
public static int PORT_NUMBER { get; set; }
public string IpAddress { get; set; }
private readonly UdpClient udp;
public event Action<object, EventArgs> msgChanged;
IAsyncResult ar_ = null;
public UDPListener(string ipaddr, int port)
{
IpAddress = ipaddr;
PORT_NUMBER = port;
udp = new UdpClient(PORT_NUMBER);
Start();
}
public void Start()
{
StartListening();
}
public void Stop()
{
try
{
udp.Close();
}
catch { /* not necessary */ }
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
try
{
Thread.Sleep(150);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(IpAddress), PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
string message = Encoding.ASCII.GetString(bytes);
//raise event..
if (message.StartsWith("S"))
if (msgChanged != null)
msgChanged(message, EventArgs.Empty);
}
catch (Exception ex)
{
Debug.WriteLine("Error in UDPListner..." + ex.Message);
}
finally
{
StartListening();
}
}
}
Now what is happening when the program starts it will receive data "S0000.." but when alarm raises data changes to "S8000..etc" but this class continuously receiving the same "S000.." data unless I restart the class.
When I run the udp listener in while loop its works perfectly fine, it receives the updated stream and changes when alarm goes off.
here is the code for while loop udp.
while (!StopRunning)
{
Thread.Sleep(150);
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
var ep = default(IPEndPoint);
var data = udp.Receive(ref ep);
udp.Close();
string msg = Encoding.ASCII.GetString(data);
if (msgChanged != null)
msgChanged(msg, EventArgs.Empty);
}
But I cannot make use of while loop because I have to fit this program in window service.
The main difference in your UDPListener and while loop is that in loop you are creating udp variable each time you are connecting to the UDP:
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
In Receive(IAsyncResult ar) you only connect with the same client, so you still have the same data.
I think that you can rewrite your class something like this:
private void StartListening()
{
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
ar_ = udp.BeginReceive(Receive, new object());
}
Make sure that you're disposing the udp connection after receive with Close() method:
byte[] bytes = udp.EndReceive(ar, ref ip);
udp.Close();
I create a class which connects with other computer in LAN. When I induce a method Connect first time all is ok, but after work class the variable lose a reference to this object.
But if I try do create this object again and I induce a method Connect I have an error which is saying "Only one using each socket adress(protocol / network address / port) is permitted"
this error is when I try to do
partner.Connect(partnerIPEndPoint);.
What is bad in my class? I would appreciate if anybody could help me.
Thanks in advance.
Here is my class:
using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace AudioNetwork
{
class ConnectToServer : IDisposable
{
public int BeepAmonut { get; set; }
public int Speed { get; set; }
public IPAddress MyIPAddress { get; set; }
public IPAddress PartnerIPAddress { get; set; }
private NetworkStream stream;
private TcpClient partner;
public void Connect()
{
IPEndPoint myIPEndPoint = new IPEndPoint(MyIPAddress, 800);
IPEndPoint partnerIPEndPoint = new IPEndPoint(PartnerIPAddress, 800);
partner = new TcpClient(myIPEndPoint);
partner.Connect(partnerIPEndPoint);
stream = partner.GetStream();
Messanger(stream);
}
protected virtual void Messanger(NetworkStream myStream)
{
byte[] data = { 2, 1 };
myStream.Write(data, 0, data.Length);
Play(2000);
}
public void Play(int time)
{
Stopwatch sw = new Stopwatch();
sw.Start();
while (sw.ElapsedMilliseconds <= 2100)
{
if (sw.ElapsedMilliseconds >= time)
{
break;
}
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
stream.Dispose();
stream.Close();
partner.Client.Dispose();
partner.Close();
}
}
class ConnectToClient : ConnectToServer
{
protected override void Messanger(NetworkStream myStream)
{
byte[] data = new byte[256];
int bytes = myStream.Read(data, 0, data.Length);
if (data[0] == 2)
{
Play(1980);
}
}
}
}
You assign a hardcoded port to the local endpoint, and don't close it.
When you open the first connection, you have a local socket on port 800 connected to the remote socket at port 800.
You then forget to close this connection (as you never call Dispose on steam or partner - I don't see your Dispose function being called anywhere)
When you're trying to open the second connection on the local port 800, it is already taken by the fist connection, which leads to the error you're encountering.
The solution is to let the OS assign an automatic port number for the local socket. That way:
When you open the first connection, you have a local socket on an auto-assigned port number connected to the remote socket at port 800.
When you open the second connection, even if the first one isn't closed yet, the OS will assign a different port number to the second socket and connect it to the remote socket at port 800.
In code, that would be simply:
partner = new TcpClient();
And get rid of all that MyIPAddress stuff.
I am very new to sockets with C# and I have been working on trying to get a socket to work with localhost. Here is the main UDP code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine(IPAddress.Any.ToString());
udp = new UdpClient(IPAddress.Any.ToString(), port_global);
endPont = new IPEndPoint(IPAddress.Parse(ip_global), port_global);
udp.Connect(ip_global, port_global);
}
public NetUDP()
{
}
public void UDPinit(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine();
udp = new UdpClient("127.0.0.1", port_global);
endPont = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port_global);
udp.Connect("127.0.0.1", port_global);
}
public void sendDataUDP(string info)
{
Byte[] SendByte = Encoding.ASCII.GetBytes(info);
udp.Send(SendByte, SendByte.Length);
}
public string getDataUDP()
{
Byte[] get = udp.Receive(ref endPont);
return Encoding.ASCII.GetString(get);
}
public void closeUDP()
{
udp.Close();
}
}
}
The problem is every time I do this code when I try to retrieve info from another UDP C# program (on the same machine) it freezes. I know it keeps on looking for info coming it's way, but id there a way to put a timeout on the receive command or do I have to make an event handler to prevent this freezing? The ports connect just fine and do not show any form of error or exception. Thank you for your help in advance!
class Program
{
static void Main(string[] args)
{
var serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8500);
NetUDP udp = new NetUDP(serverEndPoint);
UdpClient localClient = new UdpClient();
Task.Delay(20000);
var dgram = Encoding.ASCII.GetBytes("Hello Server");
localClient.Send(dgram, dgram.Length, serverEndPoint); // Send "Hello Server" to Server
Console.Read();
}
}
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP() { }
public NetUDP(IPEndPoint serverEndPoint)
{
udp = new UdpClient(serverEndPoint); // Bind the EndPoint
// Receive any IPAddress send to the 8555 port
IPEndPoint receiveEndPoint = new IPEndPoint(IPAddress.Any, 8555);
Task.Run(() =>
{
try
{
while (true)
{
// This Receive will block until receive an UdpClient
// So, run in another thread in thread pool
var bytes = udp.Receive(ref receiveEndPoint);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
}
}
catch (Exception ex)
{
// TODO
}
finally
{
udp.Close();
}
});
}
}
Here is and UdpChat demo in github: UdpChat
I'm creating a UDP Daemon class in C#, after setting the breakpoint in Visual Studio I see "The attempted operation is not supported for the type of object referenced." within this::ip::Address::ScopeId::base. The ScopeId throws exception System.Net.Sockets.SocketException. The error code is 10045 / OperationNotSupported.
Calling Code:
namespace Foo.Tester
{
class Program
{
static void Main(string[] args)
{
var TestDaemon = new UDPDaemon();
TestDaemon.port = 9999;
TestDaemon.Start();
...
UDPDaemon Class:
{
public class UDPDaemon
{
public int receivedDataLength;
public byte[] data;
public IPEndPoint ip;
public Socket socket;
public IPEndPoint sender;
public EndPoint Remote;
public string raw;
public int port { get; set; }
public LogRow row;
public UDPDaemon()
{
ip = new IPEndPoint(IPAddress.Any, port);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sender = new IPEndPoint(IPAddress.Any, 0);
Remote = (EndPoint)(sender);
}
public void Start()
{
socket.Bind(ip);
while (true)
{
data = new byte[1024];
receivedDataLength = socket.ReceiveFrom(data, ref Remote);
raw = Encoding.ASCII.GetString(data, 0, receivedDataLength);
row = new LogRow(raw);
//Will eventually move to Queue, but just print it for now
Console.WriteLine(row.ClientIp);
}
}
}
}
What is causing this exception and what does the exception mean?
How come I only see the exception if I set a breakpoint in VS?
I have just started learning this language, so if anything else seems off in the code that would be good to know.
Since you want to use port inside the constructor, you need to pass it as a constructor argument, not to set it later, e.g.:
public class UDPDaemon
{
public int receivedDataLength;
public byte[] data;
public IPEndPoint ip;
public Socket socket;
public IPEndPoint sender;
public EndPoint Remote;
public string raw;
public int Port { get; private set; }
public LogRow row;
public UDPDaemon(int port)
{
Port = port;
ip = new IPEndPoint(IPAddress.Any, port);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sender = new IPEndPoint(IPAddress.Any, 0);
Remote = (EndPoint)(sender);
}
....