I have 2 applications: Xamarin.Android client app that send value from SeekBar over Wifi and WinForms server that receive this value in real time. Everything works good but every time after ~40sec of data transferring android app throws System.Net.Sockets.SocketException with message "Too many open files".
My server code thats receive data:
public void StartReceiving()
{
IPAddress localAdd = IPAddress.Parse(SERVER_IP);
TcpListener listener = new TcpListener(localAdd, PORT_NO);
listener.Start();
while (true)
{
Socket client = listener.AcceptSocket();
client.NoDelay = true;
var childSocketThread = new Thread( () =>
{
byte[] datareceived = new byte[1];
int size = client.Receive(datareceived);
for (int i = 0; i < size; i++)
{
Console.WriteLine(datareceived[0].ToString());
}
Console.WriteLine();
//client.Close();
});
childSocketThread.Start();
}
}
Client code which sends value from SeekBar:
private void Seek1_ProgressChanged(object sender, SeekBar.ProgressChangedEventArgs e)
{
TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
NetworkStream nwStream = client.GetStream();
byte[] bytesToSend = new byte[1];
bytesToSend[0] = Convert.ToByte(e.Progress);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
}
So my question is, what causes this problem and how can I solve it?
Your problem is the following: you open a socket for each call of the ProgressChanged event handler. There is a limited number of sockets that you can open on a machine and if you open them fast enough, you will end up in a System.Net.Sockets.SocketException.
A solution to this problem would be to make sure that you close the TCP connection gracefully. In that way you will release the sockets for further usage.
My opinion is that TCP is a bit overkill for this kind of communication. You can use HTTP to transfer the data. Your desktop app will be the server and the Xamarin app will be the client. In that way you will be freed by things like synchronization, connections states, etc.
Related
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...who knows for what reason. If anyone else stumbles upon this problem (in a program like Unity, where functions are looping constantly and data is always being processed), consider that you're moving too much irrelevant data.
Original Post
I've run into quite the problem, and I'm hoping I can receive some guidance.
In short, I'm wondering how to use TCP to communicate two Unity apps over the same computer. I've gotten it functioning in editor, but when both apps are built, communication quickly breaks down.
This is really stumping me, because I don't understand why an app would work in the Editor environment, but not in the official build.
When I use TCP to communicate between two Unity apps (on the same computer), it works so long as one of them is kept in the Unity environment. That is, if I build one app, and open the other in the Unity editor, TCP communication works flawlessly.
Here is some more background: One of my apps is functioning as a User Interface, and the other is interfacing with a Looking Glass to provide a holographic display of in-game objects. Originally, they were combined into one App - but I had a lot of trouble getting Unity's multidisplay support to function between two monitors of different resolutions. Looking Glass factory even provides a prefab to do just this, but it is broken in the current SDK. So I have resorted to using sockets to interface between two apps, one for each monitor.
I'm using C#'s TCP listener class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
And TCP client class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
Presently, the UI is acting as the TCPListener, and the application that produces holograms is the TCPClient. Within each of these applications, I'm using two Queues - an IncomingMessages queue and an Outgoing Messages queue - which are global variables shared between the main thread and the networking thread.
TCP Listener:
private void Start()
{
incomingMessages = new Queue();
outgoingMessages = new Queue();
Application.runInBackground = true;
thread = new Thread(new ThreadStart(Receive));
thread.Start();
//stuff happens that's irrelevant to this question. And then...
}
void Receive()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// 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.
Debug.Log("About to reenter main while in Server...");
while (threadContinue)
{
Debug.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Debug.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Debug.Log("Received from Client: " + data);
lock (this)
incomingMessages.Enqueue(data);
string response = supplyData();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Debug.Log("Sent to Client: " + response);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Debug.Log("Exiting 'Receive'");
}
And here is the TCP Client. It attempts to connect a regular intervals, and also whenever new data is available. This is so that it can receive information from the server regularly and share new data whenever it is available:
void Start()
{
//prepare networking
Application.runInBackground = true;
outgoingMessages = new Queue();
incomingMessages = new Queue();
thread = new Thread(new ThreadStart(Connect));
thread.Start();
//stuff happens that's irrelevant to this question...
}
private void Connect()
{
String server = "127.0.0.1";
Int32 port = 13000;
string message = "";
while (threadContinue == true)
{
if (timeToConnect())
{
lastConnection = ourTime;
if (outgoingMessages.Count > 0)
message = outgoingMessages.Dequeue().ToString();
else
message = "Nothing to report.";
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Debug.Log("Sent to Server: " + message);
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
lock (this)
incomingMessages.Enqueue(responseData);
Debug.Log("Received from Server: " + responseData);
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Debug.Log("ArgumentNullException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
}
}
}
private bool timeToConnect()
{
if ((ourTime - lastConnection > NETWORK_DELAY) || (outgoingMessages.Count > 0))
return true;
return false;
}
Instantiated in separate threads so that Unity's main thread can continue unhindered.
Again - it works in Editor, but when I build it, it breaks.
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...just in the exported app. Who knows for what reason. If anyone else stumbles upon this problem...where you're bypassing Unity's multidisplay functionality by building multiple apps that communicate over network...consider that you're burdening your queues with too much data.
I believe what I am looking to create is a service that listens to a specific port, and when data is sent to that port, it sends off that data to another script for processing.
For some reason though, the service times out when I try to start it. My logs tells me TcpClient client = server.AcceptTcpClient(); is where it is stopping (actually, it is getting stuck on 'starting' in Services).
Since I have no experience with C#, making services, or working with servers in this manner, the code is pretty much just what I found online.
The OnStart method looks like this.
protected override void OnStart(string[] args)
{
try
{
TcpListener server = null;
// Set the TcpListener on port 13000.
Int32 port = 1234;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// 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)
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
}
finally
{
}
}
As per MSDN, TcpServer.AcceptTcpClient blocks, so you're probably never returning from your Service's OnStart method, which causes the service to never actually "start".
You might consider using another thread and return from OnStart as soon as possible.
Cheers
As far as creating the Windows service itself, you should be able to use this link, even though it's dated. This companion link shows how to have the service install and uninstall itself. Finally, use this link to understand how to have your service run constantly and how to properly respond to start and stop commands.
To have your service interact with the socket, you'll want to modify the WorkerThreadFunc() from the last link. This is where you should start listening for and processing inbound socket connections.
How can I make the server and the client to run unlimitedly and be able to exchange data(meaning, until the application is closed), instead of run for one exchange of information only.
Tried with while(true) but maybe didn't put it on the right place and then I can't really reach the methods for closing and stopping the socket and the listener.
Here's some of the code of the server:
public static void StartServer()
{
try
{
IPAddress ip = IPAddress.Parse("192.168.1.11");
TcpListener myListener = new TcpListener(ip, 8000);
myListener.Start();
Socket s = myListener.AcceptSocket();
byte[] b = new byte[100];
int k = s.Receive(b);
... some other actions ...
s.Close();
myListener.Stop();
}
and then then Main() where I invoke it.
With the Client is the same story.
You can create an infinite loop which contains the Receive function processing data, and returns to receive. That way the server always excepts data from the client until server, or client terminates.
while(true)
{
byte[] buffer = new byte[100];
s.Receive(buffer);
//Do something with data...
}
Beware through because in your current design only one client is supported. If you want to support multiple clients consider using threads.
I've been having some trouble lately while trying to learn how to do an asynchronous receive using visual C#. I have a console based server program that receives data from a client and then sends it back. My problem is on the client side. It has to send data to the server every 100 or so milliseconds and then receive it back.
The problem is getting it back because I can't have the program stop and wait for data. Here's what it looks like so far...
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 16487);
TcpClient client = new TcpClient();
bool blnOnOFF;
private void SendServerData()
{
string strData = "TEST DATA";
NetworkStream clientStream = client.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes(strData);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
// Ticks Every 100ms
private void tmrDataTransfer_Tick(object sender, EventArgs e)
{
SendServerData();
}
private void btnStart(object sender, EventArgs e)
{
if (blnOnOFF == false)
{
tmrDataTransfer.Start();
blnOnOFF = true;
}
else
{
tmrDataTransfer.Stop();
blnOnOFF = false;
}
}
As you can see, all it does right now is send "TEST DATA". If there is another way to receive the data that is simpler than asynchronous please let me know, also i would like to learn how to do this for future projects.
thanks in advanced.
EDIT: added client sorry i forgot about it
Ok, so, what you need to do is when your app is waiting for incoming data, you need to employ the TcpListener class.
Try this SO answer
The TcpListener class listens for incoming connections, and when it finds one, it creates a TcpClient which does its thing. Meanwhile, the listener has already gone back to looking for new connections. It's pretty much only ever doing just that, and moving the work off to other places.
I think (70% sure) that the TcpClient it creates for a new connection will be operating on a different port than the one your listener is using. You're thus always listening on the same port, while your processing is done on other threads on other ports.
Make sense? I can elaborate more if desired.
I have asked a couple of similar questions the last couple of days and received some really great help. I now understand my problem quite a bit better but I appear to have hit a snag. I have written a client server application that uses both a TCP and UDP connection. The TCP connection works fine over both LAN and WAN but the UDP connection fails over WAN. Based on the questions I asked previously I realized that my server needed to reply to the client at the EndPoint from which it received a communication. I set everything up to work that way. I will post code after the question. My problem is now that while I am using the EndPoint from the client connection and the client is establishing connection first I am still unable to make the UDP connection. It appeared to work over one network but then failed over all the others I have tried. Any help on figuring this out is appreciated. Here is the code.
Receive UDP Messages on the server
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);
int portNum = ((System.Net.IPEndPoint)Remote).Port;
string message = Encoding.ASCII.GetString(content);
string[] data = message.Split((char)124);
//UpdateStatus(data[0] + data[1]);
UserConnection sender = (UserConnection)clients[data[0]];
if (sender.PortNumber != portNum)
sender.PortNumber = portNum;
if (sender.RemoteEnd != Remote)
{
sender.RemoteEnd = Remote;//Stores the EndPoint from the client connection
}
if (data.Length > 2)
{
OnLineRecieved(sender, data[1] + "|" + data[2]);
}
else
{
OnLineRecieved(sender, data[1]);
}
}
}
Client Listens here
private void receiveUDP()
{
System.Net.IPEndPoint test = new System.Net.IPEndPoint(System.Net.IPAddress.Any,UDP_PORT_NUMBER);
System.Net.EndPoint serverIP = (System.Net.EndPoint)test;
server.Bind(serverIP);
server.Ttl = 50;
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);
}
}
EDIT: SERVER'S Sending function
public void SendData(string data)
{
if (RemoteEnd != null)//RemoteEnd is refreshed every time the client sends a UDP message
//Each Clients RemoteEnd is stored in a collection of Client objects in a server hashtable
{
//ipep = new IPEndPoint(ipAdd, PortNumber);
byte[] dataArr = Encoding.ASCII.GetBytes(data);
trans.SendTo(dataArr, dataArr.Length, SocketFlags.None, RemoteEnd);
}
}
There could be a lot of things wrong. Remember, UDP doesn't provide transmit pacing, retransmissions, or acknowledgements. So if you need them, you must provide them. If you have the client send first and then wait for responses to each query, your first lost packet will kill the connection.
You also kind of forgot to describe the problem. You say you fail to make the connection, but what does that mean? Does the server receive the client's first packet or not? Does the client receive the server's first reply or not?
You have to determine if this is a programming problem or a network configuration problem.
what I would do is run the client app on the server machine and the server app on the client machine (and switch the hosts they connect to).
If the server app no longer receives UDP messages from the client app, then you have a networking configuration problem.
If the server app can still receive messages from the client app, then you have a programming problem.