I've managed to set it up so my unity app can take in tcp data. However when I run the project the entire application freezes and won't do anything until it has received a message. Then, once a message has been received, it prints that message out and won't allow my client to connect to it again.
This code is the entirety of my TCP reading class, is there something silly I'm doing here that is causing this?
Is there a better way to read in TCP data?
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System;
using System.IO;
public class TCPConnection: MonoBehaviour
{
public string ip_address = "";
public int port_number = 0;
void Start()
{
try
{
IPAddress ipAddress = IPAddress.Parse(ip_address);
TcpListener listener = new TcpListener(ipAddress, port_number);
listener.Start();
print("The server is running at port " + port_number);
print("The local end point is :" + listener.LocalEndpoint);
print("Waiting for connection.....");
Socket socket = listener.AcceptSocket();
print("Connection accepted from " + socket.RemoteEndPoint);
byte[] b = new byte[100];
int k = socket.Receive(b);
print("recieved...");
for (int i = 0; i < k; i++)
print (Convert.ToChar(b[i]));
ASCIIEncoding ascii = new ASCIIEncoding();
socket.Send(ascii.GetBytes("The string was recieved by the server"));
print("\nSent ack");
}
catch (Exception e)
{
print("error...." + e.Message);
}
}
}
listener.AcceptSocket
and socket.Receive
are a blocking calls. So it is normal that your UI freezes. And after you have accepted the first connection, you never call listener.AcceptSocke
again to get a new collection.
The common way to use these methods are using async calls, threads or Tasks.
EDIT
An example:
void Start()
{
Task.Factory.StartNew(() =>
{
TcpListener listener = new TcpListener(IPAddress.Any, 12345);
listener.Start();
while (true)
{
Socket socket = listener.AcceptSocket();
Task.Factory.StartNew(() =>
{
byte[] b = new byte[100];
int k = socket.Receive(b);
});
}
});
}
The socket.Receive(b);
and Accept()
will block the current thread until you receive data, you can do several things here:
1) Create a separated thread that received data, and put it into a queue that is checked by a main thread. But this isn't recommented with many client connecting.
2) You can use the asynchronous method of sockets: an example is shown here: http://csharp.vanlangen.biz/network-programming/async-sockets/asyncsocketreader/
There is also an example of a non-blocking listener.
an async listener look something like this:
public class SocketEventArgs : EventArgs
{
public Socket Socket { get; private set; }
public SocketEventArgs(Socket socket)
{
Socket = socket;
}
}
/// <author>Jeroen van Langen</author>
/// <source>http://http://csharp.vanlangen.biz/network-programming/async-sockets/easysocketlistener/</source>
public class EasySocketListener : IDisposable
{
private Socket _socket;
public void Start(int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Any, port));
_socket.Listen(8);
StartAccepting();
}
private void StartAccepting()
{
try
{
_socket.BeginAccept((asyncResult) =>
{
try
{
Socket clientSocket = _socket.EndAccept(asyncResult);
if (OnSocketAccept != null)
OnSocketAccept(this, new SocketEventArgs(clientSocket));
StartAccepting();
}
catch { }
}, null);
}
catch { }
}
public void Dispose()
{
if (_socket != null)
{
_socket.Dispose();
_socket = null;
}
}
public event EventHandler<SocketEventArgs> OnSocketAccept;
}
The OnSocketAccept will be called when there is a new client connected.