I have implemented TCP hole punching in C#. What I am getting is, the clients are sending the SYN messages to each other but they are dropped at the opposite routers. The scenario is something like this:
Client:A--------Router:A--------Server--------Router:B--------Client:B
If client:A sends the SYN packet to Client:B, it is dropped at Router:B, which is obvious NAT behavior. But, according to the hole punch scenarios, now if the client:B sends SYN to client:A, it should pass through the NAT at router:A. Here in my case, router:A ,too, drops this packet. In netstat, it shows the conenction with B as SYN_SENT and after couple of seconds, I get a message like "The client has not been responding. Connection failure"
As per my research in this, reason behind dropping, may be silent dropping or active rejection. If it is silent dropping, then the hole should be punched and SYN from B should be passed through NAT at A. And if it is active rejection, the router:A will get the notification message from router:B about dropping and A will close the hole.
The problem is I am not getting any notification message at A. (checked through router log).
I also tried to an already implemented TCP hole pucnhing example taken from
https://github.com/jasonpang/tcp-holepunching
But I am getting the similar results. Help me to find out the solution for this.
I am also doubtful about the timeout for the holes punched at routers.
namespace HolePunching
{
public class HolePunchingClient
{
private Socket _clientSocket;
private Socket _serverSocket;
private Socket _connectSocket;
//private Socket _serverSocket;
private Socket _holePunchedSocket;
public HolePunchingClient (IPEndPoint localEndPoint)
{
/*For the moment, only TCP Hole Punching is supported*/
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_clientSocket.Bind(localEndPoint);
//_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//_serverSocket.Bind(localEndPoint);
}
private void ProcessClientRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
IPEndPoint iep = (IPEndPoint)eventArgs.Argument;
_connectSocket.Connect(iep);
Console.WriteLine("Connected correctly with: " + iep);
_serverSocket.Close();
_holePunchedSocket = _connectSocket;
Console.WriteLine("\n\n\nException check clientRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessClientRequest: " + e.Message);
return;
}
}
private void ProcessServerRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
_serverSocket.Listen(10);
Socket s = _serverSocket.Accept();
Console.WriteLine("Received connection from: " + s.RemoteEndPoint);
_holePunchedSocket = s;
Console.WriteLine("\n\n\nException check serverRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessServerRequest: " + e.Message);
return;
}
}
private void ProcessRequest (object sender, DoWorkEventArgs eventArgs)
{
while (true)
{
byte[] bytes = new byte[2048];
_clientSocket.Receive(bytes);
MessageType messageType = (MessageType) bytes[0];
Console.WriteLine("MessageType received: " + messageType);
switch (messageType)
{
case MessageType.ConnectClient:
byte[] byteAddress = new byte[4];
byte[] bytePort = new byte[2];
Buffer.BlockCopy(bytes, 1, byteAddress, 0, 4);
Buffer.BlockCopy(bytes, 5, bytePort, 0, 2);
IPEndPoint remoteEndPoint = new IPEndPoint(new IPAddress(byteAddress), BitConverter.ToUInt16(bytePort, 0));
Console.WriteLine("HP will be done towards this address: " + remoteEndPoint);
_connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_connectSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_connectSocket.Bind(_clientSocket.LocalEndPoint);
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_serverSocket.Bind(_clientSocket.LocalEndPoint);
BackgroundWorker bwClient = new BackgroundWorker();
bwClient.DoWork += ProcessClientRequest;
BackgroundWorker bwServer = new BackgroundWorker();
bwServer.DoWork += ProcessServerRequest;
bwClient.RunWorkerAsync(remoteEndPoint);
bwServer.RunWorkerAsync();
break;
}
}
}
public void Connect (IPEndPoint serverEndPoint)
{
_clientSocket.Connect(serverEndPoint);
_clientSocket.Send(BitConverter.GetBytes((byte)MessageType.Register));
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += ProcessRequest;
bw.RunWorkerAsync();
}
public Socket HolePunch (IPAddress otherAddress)
{
byte[] bytes = new byte[5];
Buffer.BlockCopy(BitConverter.GetBytes((byte)MessageType.RequestClient), 0, bytes, 0, 1);
Buffer.BlockCopy(otherAddress.GetAddressBytes(), 0, bytes, 1, 4);
_clientSocket.Send(bytes);
while (_holePunchedSocket == null)
{
System.Threading.Thread.Sleep(1500);
}
return _holePunchedSocket;
}
}
}
This is the client code, which I have taken from the link above. I'm getting the exception message "No response from another client. Connection failure" while connecting to the client B/A.
client A and B both run the same copies of this program. Either of them will send a request to server to connect with the other client. Server will send the IP-Port combination to both. and the client code will proceed by sending the connect request to each other.(which eventually gets timed out)
Related
I have a small server that is received and sends the message back to the client.
this is the client-side
when I open the client it will connect to server through Connect()
public Form1()
{
InitializeComponent();
Connect();
CheckForIllegalCrossThreadCalls = false;
}
this is my connect
void Connect()
{
ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
server.Connect(ipep);
}
catch (SocketException e)
{
MessageBox.Show(Convert.ToString(e));
}
Thread listen = new Thread(Receive);
listen.IsBackground = true; ;
listen.Start();
}
and I have a receive like this
void Receive()
{
datarec = new byte[1024];
try
{
while (true)
{
string StringData;
rec = server.Receive(datarec);
StringData = Encoding.ASCII.GetString(data, 0, rec);
txtShow.Text = StringData;
}
}
catch
{
Close();
}
}
and I send data through a button have Send method
void Send(string s)
{
data = new byte[1024];
data = Encoding.ASCII.GetBytes(s);
server.Send(data, data.Length, SocketFlags.None);
}
Send button
private void button1_Click(object sender, EventArgs e)
{
string s = txtText.Text;
Send(s);
}
this is the server-side
I have a thread server
public static void Process(Socket client)
{
byte[] data = new byte[1024];
int recv;
string dataInput;
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("Connected with {0} at port {1}", clientep.Address, clientep.Port);
while (true)
{
try
{
recv = client.Receive(data);
dataInput = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(dataInput);
client.Send(data);
}
catch (SocketException e)
{
Console.WriteLine(e);
}
}
}
this is the server main
public static void Main()
{
byte[] rec = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9999);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(ipep);
server.Listen(10);
Console.WriteLine("Waiting for client...");
Console.WriteLine("LOG CHAT");
while (true)
{
Socket client = server.Accept();
Core core = new Core();
Thread t = new Thread(() => Core.Process(client));
t.Start();
}
}
the server receive message but when it sends a message back it has an error "An established connection was aborted by the software in your host machine"
Can you guys tell me where I was wrong and how can I fix it?
When you call client.Send(data) in your server code, you will send the whole 1024 byte buffer back to the client, not just the data received.
Encoding.ASCII.GetString in the client could fail when processing this garbage and the exception will close the connection.
Try to replace client.Send(data) by client.Send(data, recv, SocketFlags.None).
Also, you should not update UI controls directly from a background thread, Use Control.Invoke for this. Failing to do so will also throw an exception and close the connection.
I have a basic C#.NET UDP server and client code and I am trying to port it to UWP. For UWP, I experimented with DatagramSocket and regular Socket class. I am seeing almost 40% packet drops on incoming UDP packets on UWP Server using DatagramSocket class. If I use UWP Sockets, I see the packet loss of around 70%. The old .NET code shows 0% packet loss.
I am thinking maybe I am not using the UWP APIs correctly and hence I am getting poor performance. Could someone guide me on how to improve UWP server performance?
Right now, all the tests are done using loop-back. UWP doesn't allow loop-back by default so I am followed the instructions mentioned here to enable loop-back.
The server listens for packet on 50000 port and Client code sends packets of around 60 bytes to server from 50001.
The C#.NET client code is below:
try
{
byte[] buffer = new byte[1024];
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 50001);
IPAddress remoteIp = System.Net.IPAddress.Parse("127.0.0.1");
IPEndPoint remoteEP = new IPEndPoint(remoteIp, 50000);
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
sender.Bind(localEP);
for(int i = 0; i < 10000; i++)
{
//Thread.Sleep(1);
// Encode the data string into a byte array.
string ClientReq = "Client Request:: " + i.ToString() + " "+ Guid.NewGuid().ToString();
byte[] msg = Encoding.ASCII.GetBytes(ClientReq);
// Send the data through the socket.
int sentBytes = sender.SendTo(msg, remoteEP);
Console.WriteLine("{0}::{1} Client Request ({2} bytes):: {3}.",
remoteEP.Address.ToString(),
remoteEP.Port.ToString(),
sentBytes,
ClientReq
);
}
// Release the socket.
sender.Close();
//Console.ReadKey();
}
catch(ArgumentNullException ane)
{
Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
}
catch(SocketException se)
{
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch(Exception e)
{
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
C#.NET Server Code:
byte[] buffer = new Byte[1024];
IPAddress LocalIp = IPAddress.Any; //System.Net.IPAddress.Parse("127.0.0.1");
IPEndPoint localEndPoint = new IPEndPoint(LocalIp, 50000);
// Create a UDP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
// Bind the socket to the local endpoint and
// listen for incoming connections.
try
{
listener.Bind(localEndPoint);
Console.WriteLine("Bind Complete");
int messagesReceived = 0;
while(true)
{
EndPoint receiveEP = new IPEndPoint(IPAddress.Any, 0);
int receivedBytes = listener.ReceiveFrom(buffer, ref receiveEP);
messagesReceived++;
IPEndPoint receiveIPEP = receiveEP as IPEndPoint;
Console.WriteLine("{0}::{1}::{2} Client Request ({3} bytes):: {4}.",
messagesReceived,
receiveIPEP.Address.ToString(),
receiveIPEP.Port.ToString(),
receivedBytes,
System.Text.Encoding.UTF8.GetString(buffer, 0, receivedBytes));
}
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
With this above code, i see all the 10000 packets are received at server.
UWP DatagramSocket Code:
DgSocket = new DatagramSocket();
DgSocket.MessageReceived += SocketListener_MessageReceived;
await DgSocket.BindServiceNameAsync("50000");
private async void SocketListener_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
uint datalen = args.GetDataReader().UnconsumedBufferLength;
DataReader reader = args.GetDataReader();
byte[] dataBuffer = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(dataBuffer);
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lock(_lockObject)
{
DgPackets++;
DgPacketList.Add(Encoding.ASCII.GetString(dataBuffer));
}
});
}
UWP Socket Code:
public void SetupSimpleSocketListener()
{
try
{
IPAddress LocalIp = IPAddress.Any;
IPEndPoint localEndPoint = new IPEndPoint(LocalIp, 50000);
SimpleSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
byte[] buffer = new Byte[1024];
SimpleSocket.Bind(localEndPoint);
Debug.WriteLine("Bind Complete");
SocketAsyncEventArgs sockarg = new SocketAsyncEventArgs();
sockarg.Completed += Sockarg_Completed;
sockarg.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
sockarg.SetBuffer(buffer, 0, buffer.Length);
if(!SimpleSocket.ReceiveFromAsync(sockarg))
{
Sockarg_Completed(this, sockarg);
}
}
catch(Exception e)
{
Debug.WriteLine(e.ToString());
}
}
private async void Sockarg_Completed(object sender, SocketAsyncEventArgs e)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
lock(_lockObject)
{
SimpleSocketPackets++;
SimpleSocketPacketList.Add(Encoding.ASCII.GetString(e.Buffer, 0, e.BytesTransferred));
}
});
if(e.SocketError == System.Net.Sockets.SocketError.Success)
{
if(!SimpleSocket.ReceiveFromAsync(e))
{
Sockarg_Completed(this, e);
}
}
}
I have posted the full source code on github:
The form from this code does not appear. How can I get it to show? Through the console, everything works.
void Form1_Load(object sender, EventArgs e)
{
String host = Dns.GetHostName();//name kompa
//ip pc
IPAddress ip = Dns.GetHostByName(host).AddressList[0];
const int port = 2222;
label1.Text = host;
label2.Text = "Сервер запущен на " + ip.ToString() + ":" + port.ToString();
server.serversocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
server.serversocket.Bind(new IPEndPoint(ip, port));
server.serversocket.Listen(100);
//ошибочка в цикле
while (server.work)
{
MessageBox.Show("dsff");
Socket handle = server.serversocket.Accept();
richTextBox1.AppendText("новое подключение" + handle.RemoteEndPoint.ToString());
new user(handle);
}
As Hans Passant said, you are blocking the GUI thread with a while loop. That will just not do. Events must return asap and can never run indefinitely. And this Form_Load can not return until the server stops working.
It is very common mistake and basically the reason we all went crazy for multitasking and multithreading this century. You can't even do a word processor without it anymore.
You are blocking the ui thread. Using a separate thread for the synchronous socket server would be a solution. The other option would be to use an asynchronous socket so that the execution of the application is not blocked while it waits for a connection. Based on the socket code samples.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitSocketServer();
}
internal void InitSocketServer()
{
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
var ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
try
{
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(100);
listener.BeginAccept(AcceptCallback, listener);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void AcceptCallback(IAsyncResult ar)
{
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
string data = null;
while (true)
{
var bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data.IndexOf("<EOF>") > -1)
{
break;
}
}
MessageBox.Show(handler.RemoteEndPoint + " : " + data);
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Test button click");
}
}
I am creating a C# application in which I send files over a LAN connection via sockets, but I encounter an "System.NotSupportedException" when I call the Send function
This is the function of the receiver of the file.
Socket _newSocket;
List<byte> endBuffer;
// Used to connect to the server
private void button1_Click(object sender, EventArgs e)
{
_newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_newSocket.Connect(IPAddress.Parse("127.0.0.1"), _PORT);
}
// Used to Receive the file
private void button3_Click(object sender, EventArgs e)
{
_newSocket.Receive(endBuffer.ToArray(), SocketFlags.None);
MessageBox
.Show(Encoding.ASCII.GetString(endBuffer.ToArray(), 0, endBuffer.Count));
}
This is the function I used to start the server.
void SetupServer(object sender)
{
Button temp = (Button)sender;
temp.Enabled = false;
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(new IPEndPoint(IPAddress.Parse(txtConnectToIP.Text), _PORT));
_serverSocket.Listen(5);
_serverSocket.BeginAccept(AcceptCallback, null);
}
void AcceptCallback(IAsyncResult AR)
{
Socket socket;
try
{
socket = _serverSocket.EndAccept(AR);
}
catch
{
MessageBox.Show("Not Working!");
return;
}
_clientSockets.Add(socket);
int id = _clientSockets.Count - 1;
IPAddress ip = IPAddress.Parse(_serverSocket.LocalEndPoint.ToString().Split(':')[0]);
string HostName = Dns.GetHostEntry(ip).HostName.Split('.')[0];
string finalName = HostName + " (" + ip.ToString() + ")";
AddClientForSelectionCallback(finalName);
}
This is the send function.
byte[] postBuffer = Encoding.ASCII.GetBytes("Sending Complete!");
void Send(string filePath)
{
try
{
_serverSocket.SendFile(filePath, null, postBuffer, TransmitFileOptions.ReuseSocket);
}
catch (Exception ec)
{
MessageBox.Show("Failed with error message:\n "+ ec);
}
}
Lastly, how would Receive the file sent because _newSocket.Receive() does not seem to work. I have looked at storing its result in a var, but Receive returns nothing and I will try using ConnectAsync.
Help would really be appreciated.
In your program, _serverSocket is the socket that accepts incoming connections, you cannot send data to it.
As soon as a connection is accepted, you get the socket for that connection:
socket = _serverSocket.EndAccept(AR);
You can use this socket instance to communicate with that specific client.
I have a program that multiple clients would be able to connect to a server using a socket:
private void performConnect()
{
while (true)
{
if (myList.Pending())
{
thrd = thrd + 1;
tcpClient = myList.AcceptTcpClient();
IPEndPoint ipEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint;
string clientIP = ipEndPoint.Address.ToString();
nStream[thrd] = tcpClient.GetStream();
currentMsg = "\n New IP client found :" + clientIP;
recieve[thrd].Start();
this.Invoke(new rcvData(addNotification));
try
{
addToIPList(clientIP);
}
catch (InvalidOperationException exp)
{
Console.Error.WriteLine(exp.Message);
}
Thread.Sleep(1000);
}
}
}
then the server could send data (chat messages) to a chosen client, using this code.
private void sendData(String data)
{
IPAddress ipep =IPAddress.Parse(comboBox1.SelectedItem.ToString());
Socket server = new Socket(AddressFamily.InterNetwork , SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipept = new IPEndPoint( ipep, hostPort);
NetworkStream nStream = tcpClient.GetStream();
ASCIIEncoding asciidata = new ASCIIEncoding();
byte[] buffer = asciidata.GetBytes(data);
if (nStream.CanWrite)
{
nStream.Write(buffer, 0, buffer.Length);
nStream.Flush();
}
}
the problem is that whatever IP i choose from the combo box, the message i send would always be directed/sent to the last IP that connected to the server.. Please somebody pinpoint my error! all help would be appreciated.
Look at those lines:
IPAddress ipep =IPAddress.Parse(comboBox1.SelectedItem.ToString());
Socket server = new Socket(AddressFamily.InterNetwork , SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipept = new IPEndPoint( ipep, hostPort);
NetworkStream nStream = tcpClient.GetStream();
You are creating a new socket but you are sending the data to the socket stored in the variable tcpClient that is global (since it was not defined in the method), thus totally ignoring the IPEndPoint parsed from the combobox.
You should not create a new socket in order to send data to the clients. Instead, store all clients in a collection and retrieve the appropriate one based on the input of the combobox.