I have written a socket server in c# that will be used as the basic design for a small game project I am part of. The socket server works fine on lan. I able to communicate completely fine between the server and the client. However on the WAN the server receives all the correct messages from the client, but the client receives no messages from the server. Both the client and the server are behind a router but only the server's router has the ports forwarded. When the client connects to the server I get the IP address of the connection. Because the client is behind a NAT, is there more information from the sender that I need to collect? I assume the client could set up port forwarding but that would be VERY counter-productive to the game. Any help I can get is appreciated. If you need code let me know. Thanks in advance.
Used to make the TCP connection from the client
public string ConnectionAttempt(string ServeIP, string PlayUsername)
{
username = PlayUsername;
try
{
connectionClient = new TcpClient(ServeIP,TCP_PORT_NUMBER);
connectionClient.GetStream().BeginRead(readbuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(DoRead), null);
Login(username);
ipAddress = IPAddress.Parse(((IPEndPoint)connectionClient.Client.RemoteEndPoint).Address.ToString());
servIP = new IPEndPoint(ipAddress,65002);
listenUDP = new IPEndPoint(ipAddress, 0);
UDPListenerThread = new Thread(receiveUDP);
UDPListenerThread.IsBackground = true;
UDPListenerThread.Start();
return "Connection Succeeded";
}
catch(Exception ex) {
return (ex.Message.ToString() + "Connection Failed");
}
}
Listens for a UDP message on a thread.
private void receiveUDP()
{
System.Net.IPEndPoint test = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
System.Net.EndPoint serverIP = (System.Net.EndPoint)test;
server.Bind(serverIP);
EndPoint RemoteServ = (EndPoint)servIP;
while (true)
{
byte[] content = new byte[1024];
int data = server.ReceiveFrom(content, ref RemoteServ);
string message = Encoding.ASCII.GetString(content);
result = message;
ProcessCommands(message);
}
}
Server TCP connection Listener:
private void ConnectionListen()
{
try
{
listener = new TcpListener(System.Net.IPAddress.Any, PORT_NUM);
listener.Start();
do
{
UserConnection client = new UserConnection(listener.AcceptTcpClient());
client.LineRecieved += new LineRecieve(OnLineRecieved);
UpdateStatus("Someone is attempting a login");
} while (true);
}
catch
{
}
}
Server UDP Listener:
private void receiveUDP()
{
System.Net.IPEndPoint test = new System.Net.IPEndPoint(System.Net.IPAddress.Any, UDP_PORT);
System.Net.EndPoint serverIP = (System.Net.EndPoint)test;
trans.Bind(serverIP);
System.Net.IPEndPoint ipep = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
System.Net.EndPoint Remote = (System.Net.EndPoint)ipep;
while (true)
{
byte[] content = new byte[1024];
int recv = trans.ReceiveFrom(content,ref Remote);
string message = Encoding.ASCII.GetString(content);
string[] data = message.Split((char)124);
//UpdateStatus(data[0] + data[1]);
UserConnection sender = (UserConnection)clients[data[0]];
sender.RemoteAdd = Remote;
if (data.Length > 2)
{
OnLineRecieved(sender, data[1] + "|" + data[2]);
}
else
{
OnLineRecieved(sender, data[1]);
}
}
}
Setup Information for a user connection Server-side:
Socket trans = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp); //UDP connection for data transmission in game.
public PlayerLoc Location = new PlayerLoc();
public UserConnection(TcpClient client)//TCP connection established first in the Constructor
{
this.client = client;
ipAdd = IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString());
ipep = new IPEndPoint(ipAdd, ((IPEndPoint)client.Client.RemoteEndPoint).Port);
this.client.GetStream().BeginRead(readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(StreamReciever), null);
}
Method for sending data to individual users:
public void SendData(string data)//UDP only used during transmission
{
byte[] dataArr = Encoding.ASCII.GetBytes(data);
trans.SendTo(dataArr, dataArr.Length,SocketFlags.None, RemoteAdd);
}
The client must start the UDP communication so that it can get a "hole" in the router/firewall. And the UDP server must reply back using the end point referenced in ReceviedFrom
Related
I'm trying to broadcast a message from server to a remote client, my problem is that I'm not able to receive the message on the client, the approach I'm currently playing with is as follows:
//Server
UdpClient udpServer = new UdpClient();
udpServer.Client.Bind(new IPEndPoint(IPAddress.Any, port));
var send = Task.Run(() =>
{
var to = new IPEndPoint(IPAddress.Broadcast, port);
while (true)
{
var response = gameWorld.GetStateJson();
var responseInBytes = Encoding.ASCII.GetBytes(response);
udpServer.SendAsync(responseInBytes, responseInBytes.Length, to);
Console.WriteLine(response);
gameWorld.Update();
}
});
send.Wait();
And for the client:
//Client
UdpClient receivingUdpClient = new UdpClient(8081);
while (true)
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Parse("11.111.111.111"), 0);
try
{
byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
string returnData = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine(returnData.ToString());
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
So the current client code works as expected, but only for my LAN (if I understand correctly), I'm not sure what should I change in order to be receiving the messages over the internet "globally", advice would be highly appreciated.
Side question: does the server code as is broadcast the data "globally" ? Or is it also for my LAN only?
EDIT: Is this somehow possible or do I have to send data to each client explicitly, even though they're the same for all
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.
below is roughly how I normally do when trying to setup a connection on my server with my slient device:
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, nConst3rdPartyPort);
the3rdPartyListener = new Socket(localEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
the3rdPartyListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
the3rdPartyListener.Bind(localEP);
the3rdPartyListener.Listen(100);
the3rdPartyListener.BeginAccept(new AsyncCallback(AcceptConnectBack), the3rdPartyListener);
Any client device can connect to my server if they know the server ip and port number.
Now, I want to do filtering of client device. Only specific client device can connect to my server.
Is it that we can insert a unique ID to client so that the client with only that unique ID can connect to my server?
How do we ensure that the client is the specific client that we want?
This is for security reason to prevent unauthorized client from connecting to the server.
You can use IPEndPoint Address method to retrieve the remote IPAddress and make a whitelist to check who can connect your server.
string clientIP = ((IPEndPoint)(the3rdPartyListener.RemoteEndPoint)).Address.ToString();
------------------------------------------------------------------------------------------------------------------------------------
Updated:
I'm not sure Csharp Socket has verified device method or not. But I have a method on below.
Use NetworkStream to pass data from Client to Server that verified the client.
Server Side:
TcpClient client;
TcpListener server;
Thread thread;
Thread threadTwo;
byte[] datalength = new byte[4];
public Server()
{
InitializeComponent();
server = new TcpListener(IPAddress.Any, 2018);
server.Start();
thread = new Thread(() =>
{
client = server.AcceptTcpClient();
ServerReceive();
}
thread.Start();
}
void ServerReceive()
{
networkStream = client.GetStream();
threadTwo = new Thread(() =>
{
while ((i = networkStream.Read(datalength, 0, 4)) != 0)
{
byte[] data = new byte[BitConverter.ToInt32(datalength, 0)];
networkStream.Read(data, 0, data.Length);
this.Invoke((MethodInvoker)delegate
{
if(Encoding.Default.GetString(data) == "unique ID")
{
//Do your job
}
}
}
}
threadTwo.Start();
}
Client Side:
NetworkStream networkStream;
TcpClient client;
byte[] datalength = new byte[4];
public Client()
{
InitializeComponent();
try
{
client = new TcpClient("your server ip", 2018);
ClientSend("unique ID");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void ClientSend(string msg)
{
try
{
networkStream = client.GetStream();
byte[] data;
data = Encoding.Default.GetBytes(msg);
int length = data.Length;
byte[] datalength = new byte[4];
datalength = BitConverter.GetBytes(length);
networkStream.Write(datalength, 0, 4);
networkStream.Write(data, 0, data.Length);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Problem:
I am trying to bind a udp socket on a specific address. I will broadcast out a message. That same socket will need to be able to receive messages.
Current code:
static void Main()
{
UdpClient Configuration = new UdpClient(new IPEndPoint(IPAddress.Parse(data.IPAddress), configuration.Port)); //set up the bind to the local IP address of my choosing
ConfigurationServer.EnableBroadcast = true;
Configuration.Connect(new IPEndpoint(IPAddress.Parse(data.BroadcastIP), configuration.Port);
Listen();
}
private void Listen()
{
Task.Run(async () =>
{
while (true)
{
var remoteIp = new IPEndPoint(IPAddress.Any, configuration.Port);
var data = await ConfigurationServer.ReceiveAsync();
// i would send based on what data i received here
int j = 32;
}
}
});
}
I am not receiving data on listen thread. I know that the code on the other side is functional, and sending a directed UDP message to the IP/Port combo.
It can simply be done as
int PORT = 9876;
UdpClient udpClient = new UdpClient();
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, PORT));
var from = new IPEndPoint(0, 0);
var task = Task.Run(() =>
{
while (true)
{
var recvBuffer = udpClient.Receive(ref from);
Console.WriteLine(Encoding.UTF8.GetString(recvBuffer));
}
});
var data = Encoding.UTF8.GetBytes("ABCD");
udpClient.Send(data, data.Length, "255.255.255.255", PORT);
task.Wait();
I turned on my mobile's hotspot and connected my computer to the hotspot
and used this code to create server but InetAddress became "/0.0.0.0":
ServerSocket ss = null;
try {
ss= new ServerSocket(4444);
//texto.append("\n"+ss.getInetAddress());
Log.d("TcpServer", ss.getInetAddress()+"");
log= ss.getInetAddress().toString();
//ss.setSoTimeout(10000);
//accept connections
Socket s = ss.accept();
Log.i("TcpServer", "Receiving");
//texto.append("\n"+"Receiving");
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//receive a message
Log.i("TcpServer", in.readLine());
final String incomingMsg = in.readLine() + System.getProperty("line.separator");
Log.i("TcpServer", "received: " + incomingMsg);
runOnUiThread(new Runnable() {
public void run() {
// texto.append("received: " + incomingMsg);
}
});
s.close();
Yes. If you just create a socket, the default is to listen on all network devices/all assigned IP addresses, which is reflected by listening on IP 0.0.0.0.