Hi,
I'm trying to write a UDP-client that listens to a port, and then display the incoming data in a textbox.
I have written a class; UDP_Receive() that starts a backgroundworker that listens to a specified port and receives the data. The code is based on this question; C# .Net receiving UDp packets in separater thread and application exit
I think I have solved the issues with the blocking .receive() by following the accepted answer in this question; How can I safely terminate UdpClient.receive() in case of timeout?
My question is how do I get the data I receive in the backgroundworkerthread (that I created in my class) back to the mainthread, so that I can display it in a textbox?
I thought of using Invoke, but I don't have any references to the textbox in my class.
textBox_UdpPositionInData.Invoke(new EventHandler(delegate
{
textBox_UdpPositionInData.Text = receivedData;
}));
I found a very similar question about a tcp-server, but I can't see how to apply that solution here Send data from a background thread to the main thread
Any thoughts or suggestions are of course much appreciated!
Many Thanks!
internal class UDP_Receive
{
private int _portToListen = 2003;
private volatile bool listening;
BackgroundWorker _backgroundWorker;
//Constructor
public UDP_Receive()
{
Debug.WriteLine("Constructor: UDP_Receive");
this.listening = false;
}
public void StartListener()
{
if ( (_backgroundWorker==null) || (!_backgroundWorker.IsBusy))
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += listenForUDPPackages_DoWork;
_backgroundWorker.RunWorkerCompleted +=listenForUDPPackages_RunWorkerCompleted;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.RunWorkerAsync();
Debug.WriteLine("Creates a new thread: " + _backgroundWorker.ToString() );
// We are listening
this.listening = true;
}
}
public void StopListener()
{
// The user cancelled the UDP Port listening
this.listening = false;
// Cancel the backgroundworker
_backgroundWorker.CancelAsync();
// Debug
Debug.WriteLine("Stops current thread: " + _backgroundWorker.ToString());
}
public bool IsListening
{
get { return this.listening; }
}
public int PortToListen
{
get { return this._portToListen; }
set { this._portToListen = value; }
}
private void listenForUDPPackages_DoWork(object sender, DoWorkEventArgs ev)
{
UdpClient? listener = null;
try
{
listener = new UdpClient(_portToListen);
}
catch (SocketException)
{
//do nothing
}
if (listener != null)
{
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _portToListen);
try
{
while (this.listening)
{
Debug.WriteLine("Waiting for UDP broadcast to port " + _portToListen);
byte[] receivedBytes = new byte[1024];
string receivedData;
bool timeTracker = TrackFunction(TimeSpan.FromSeconds(2), () =>
{
receivedBytes = listener.Receive(ref groupEP);
});
Debug.WriteLine("Timetracker result: " + timeTracker.ToString());
if (receivedBytes == null || receivedBytes.Length == 0)
{
// We did not recieve any data
Debug.WriteLine("No data received befor Time out ");
}
else
{
// We managed to receive some data!
// No we want to process the data and then send the result to the Thread that initiated the class.
receivedData = Encoding.Default.GetString(receivedBytes);
Debug.WriteLine("Data received: " + receivedData);
}
}
catch (Exception e)
{
Debug.WriteLine("Exception: " + e.ToString());
}
finally
{
listener.Close();
Debug.WriteLine("Finally: Done listening for UDP broadcast");
}
}
else
{
Debug.WriteLine("Error: UdpClient(_portToListen) returned null");
}
}
private void listenForUDPPackages_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e == null || e.Result == null)
{
Debug.WriteLine("listenForUDPPackages_RunWorkerCompleted e = null");
}
else
{
if (e.Cancelled)
{
Debug.WriteLine("Operation was canceled");
}
else if (e.Error != null)
{
Debug.WriteLine("Error: " + e.Error.Message);
}
else
{
Debug.WriteLine("Result: " + e.Result.ToString());
}
}
}
private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
}
You can write this kind of code to keep the UI separate from the UDP_Receive class:
internal class UDP_Receive
{
private Action<string> _invoke;
public UDP_Receive(Action<string> invoke)
{
_invoke = invoke;
}
public void Foo()
{
_invoke("Hello");
}
}
When you declare the class you then do it like this:
var ur = new UDP_Receive(t => textBox.Invoke(() => textBox.Text = t));
Now it's just a matter of calling ur.Foo() (in my example) to update the UI.
Currently my android app communicates with a server via tcp socket with synchronous read and write methods
private val addressString : String = "192.168.1.200"
private val port : Int = 33322
private var socketAddress: InetSocketAddress = InetSocketAddress(addressString, port)
private var socket: Socket? = null
fun connect(){
socket = Socket()
socketAddress = InetSocketAddress(addressString, port)
try{
socket!!.connect(socketAddress)
}catch(e : Exception){
socket!!.close()
}
}
fun disconnect(){
try {
socket?.shutdownOutput()
} catch (e: Exception) {}
try {
socket?.shutdownInput()
} catch (e: Exception) {}
try {
socket?.close()
} catch (e: Exception) {}
socket = null
}
fun send(data: ByteArray): Int {
var sentByteCount: Int = -1
try {
socket!!.getOutputStream().write(data)
sentByteCount = data.size
} catch (e: Exception) {
throw e
}
return sentByteCount
}
data class Wrapper<T>(
var value:T
)
fun receive(buffer: Wrapper<ByteArray>): Int {
val size = buffer.value.size
var receivedByteCount: Int = -1
try {
receivedByteCount = socket!!.getInputStream().read(buffer.value)
} catch (e: Exception) {
throw e
}
return receivedByteCount
}
However, the server, written in C#, always communicates with another device via socket but with an asynchronous reading method
public const int BUFFER_SIZE = 4096;
private string addressString = "192.168.1.200"
private int port = 33322
private int timeout = 5000
private byte[] _buffer = new byte[BUFFER_SIZE];
private TcpClient _client;
private SocketError _socketError;
private AsyncCallback _callback = new AsyncCallback(ReceiveCallback);
public void connect()
{
_client = TCpClient()
_client.ConnectAsync(addressString, port).Wait(timeout)
}
public void receive()
{
_client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, _socketError, _callback, null)
}
private void receiveCallback(IAsyncResult ar)
{
//callback method when it has finished reading asynchronously
}
public void send()
{
_client.GetStream().Write(_buffer, 0, _buffer.Length);
}
public void disconnect()
{
if (_client.Connected)
_client.Close();
}
What I need is to communicate between my app and the device as the Server is already doing.
I tried to find out if even in kotlin there was the possibility of creating an asynchronous connection that would give me the possibility of being able to do an asynchronous reading as it can be done in C # and I found AsynchronousSocketChannel.
From the documentation, however, it has been deduced that the possibility of using this socket is linked to the fact that the server is an AsynchronousServerSocketChannel socket, in my case it is not possible to use it as I can only create the client.
Are there any other possibilities to recreate something similar to the C # code shown above in Kotlin?
I create client application for read message from server. The client application has class SocketClient which has function ReadDataAsync() for read message from server like this.
namespace SocketAsync
{
public class SocketClient
{
IPAddress mServerIPAddress;
int mServerPort;
TcpClient mClient;
public SocketClient()
{
mClient = null;
mServerPort = -1;
mServerIPAddress = null;
}
public IPAddress ServerIPAddress
{
get
{
return mServerIPAddress;
}
}
public int ServerPort
{
get
{
return mServerPort;
}
}
public bool SetServerIPAddress(string _IPAddressServer)
{
IPAddress ipaddr = null;
if (!IPAddress.TryParse(_IPAddressServer, out ipaddr))
{
Debug.WriteLine("Invalid server IP supplied.");
return false;
}
mServerIPAddress = ipaddr;
return true;
}
public bool SetPortNumber(string _ServerPort)
{
int portNumber = 0;
if (!int.TryParse(_ServerPort.Trim(), out portNumber))
{
Debug.WriteLine("Invalid port number supplied, return.");
return false;
}
if (portNumber <= 0 || portNumber > 65535)
{
Debug.WriteLine("Port number must be between 0 and 65535.");
return false;
}
mServerPort = portNumber;
return true;
}
public async Task ConnectToServer()
{
if (mClient == null)
{
mClient = new TcpClient();
}
try
{
await mClient.ConnectAsync(mServerIPAddress, mServerPort);
Debug.WriteLine(
string.Format("Connected to server IP/Port: {0} / {1}",
mServerIPAddress, mServerPort));
ReadDataAsync(mClient);
}
catch (Exception excp)
{
Console.WriteLine(excp.ToString());
throw;
}
}
private async Task ReadDataAsync(TcpClient mClient)
{
try
{
StreamReader clientStreamReader = new StreamReader(mClient.GetStream());
char[] buff = new char[64];
int readByteCount = 0;
while (true)
{
readByteCount = await clientStreamReader.ReadAsync(buff, 0, buff.Length);
if (readByteCount <= 0)
{
Debug.WriteLine("Disconnected from server.");
mClient.Close();
break;
}
Debug.WriteLine(
string.Format("Received bytes: {0} - Message: {1}",
readByteCount, new string(buff)));
Array.Clear(buff, 0, buff.Length);
}
}
catch (Exception excp)
{
Console.WriteLine(excp.ToString());
throw;
}
}
}
}
ReadDataAsync() can show message in output but I want to show message in Form1. So I put the label1 in Form. I connect to server when click button1 like this.
namespace TestClient
{
public partial class Form1 : Form
{
SocketClient client = new SocketClient();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(!client.SetServerIPAddress("127.0.0.1")||
!client.SetPortNumber("23000"))
{
Debug.WriteLine("Wrong IP Address or port");
return;
}
client.ConnectToServer();
string strInputUser = null;
}
Can I show message in ReadDataAsync() to Form1. How to do that?
Use events.
Create your own EventArgs to transport the messages:
public class MessageEventArgs : EventArgs
{
public string Message { get; private set; }
public MessageEventArgs(string message)
{
this.Message = message;
}
}
In your SocketClient class declare the event:
namespace SocketAsync
{
public class SocketClient
{
IPAddress mServerIPAddress;
int mServerPort;
TcpClient mClient;
public event EventHandler<MessageEventArgs> Message;
// your other code
}
}
Also in your SocketClient class, use (raise) the event in ReadDataAsync():
private async Task ReadDataAsync(TcpClient mClient)
{
try
{
StreamReader clientStreamReader = new StreamReader(mClient.GetStream());
char[] buff = new char[64];
int readByteCount = 0;
while (true)
{
readByteCount = await clientStreamReader.ReadAsync(buff, 0, buff.Length);
if (readByteCount <= 0)
{
Message?.Invoke(this, new MessageEventArgs("Disconnected from server."));
Debug.WriteLine("Disconnected from server.");
mClient.Close();
break;
}
Message?.Invoke(this, new MessageEventArgs(string.Format("Received bytes: {0} - Message: {1}",
readByteCount, new string(buff))));
Debug.WriteLine(
string.Format("Received bytes: {0} - Message: {1}",
readByteCount, new string(buff)));
Array.Clear(buff, 0, buff.Length);
}
}
catch (Exception excp)
{
Console.WriteLine(excp.ToString());
throw;
}
}
Finally, in your form, consume the event:
namespace TestClient
{
public partial class Form1 : Form
{
SocketClient client = new SocketClient();
public Form1()
{
InitializeComponent();
client.Message += Client_Message;
}
private void Client_Message(object sender, MessageEventArgs e)
{
label1.Invoke(new Action() => label1.Text = e.Message);
}
// your other code
}
}
Disclaimer: My C# isn't even close to as good as my C++
I am trying to learn how to do async sockets in C# in order to write a test app for a component of mine. My former attempts using TcpClient ended in failure and you can read the outstanding questions on that here:
TcpClient.NetworkStream Async operations - Canceling / Disconnect
Detect errors with NetworkStream.WriteAsync
Since, I could not get that working, I tried using Socket.BeginX and Socket.EndX instead. I got much further along. My problem now is that in the listing below, when it comes time to disconnect, which in turn calls shutdown and close on the socket, async operations are still outstanding and they will throw an object disposed exception or an object is set to null exception.
I found a similar post about that here:
After disposing async socket (.Net) callbacks still get called
However, I do not accept that answer, because if you are using exceptions for intended behavior, then 1) They are not exceptional 2) You cannot tell if the exception was thrown for your intended case or if it was thrown because you actually used a disposed object or a null reference in your async method, other than the socket.
In C++ with async socket code, I'd keep track of the number of outstanding async operations with Interlocked and when it came time to disconnect, I'd call shutdown, then wait for the interlocked to hit 0, and then close and destroy any members I needed to.
How would I go about waiting in my Disconnect method for all outstanding Async operations to complete in C# in the following listing?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.Net.Sockets;
using System.Net;
namespace IntegrationTests
{
public class Client2
{
class ReceiveContext
{
public Socket m_socket;
public const int m_bufferSize = 1024;
public byte[] m_buffer = new byte[m_bufferSize];
}
private static readonly ILog log = LogManager.GetLogger("root");
static private ulong m_lastId = 1;
private ulong m_id;
private string m_host;
private uint m_port;
private uint m_timeoutMilliseconds;
private string m_clientId;
private Socket m_socket;
private uint m_numOutstandingAsyncOps;
public Client2(string host, uint port, string clientId, uint timeoutMilliseconds)
{
m_id = m_lastId++;
m_host = host;
m_port = port;
m_clientId = clientId;
m_timeoutMilliseconds = timeoutMilliseconds;
m_socket = null;
m_numOutstandingAsyncOps = 0;
}
~Client2()
{
Disconnect();
}
public void Connect()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(m_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], (int)m_port);
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_socket.ReceiveTimeout = (int)m_timeoutMilliseconds;
m_socket.SendTimeout = (int)m_timeoutMilliseconds;
try
{
m_socket.Connect(endpoint);
log.Info(string.Format("Connected to: {0}", m_socket.RemoteEndPoint.ToString()));
// Issue the next async receive
ReceiveContext context = new ReceiveContext();
context.m_socket = m_socket;
m_socket.BeginReceive(context.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, m_id, e.ToString()));
}
}
public void Disconnect()
{
if (m_socket != null)
{
m_socket.Shutdown(SocketShutdown.Both);
// TODO - <--- Error here in the callbacks where they try to use the socket and it is disposed
// We need to wait here until all outstanding async operations complete
// Should we use Interlocked to keep track of them and wait on it somehow?
m_socket.Close();
m_socket = null;
}
}
public void Login()
{
string loginRequest = string.Format("loginstuff{0})", m_clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
public void MakeRequest(string thingy)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket);
}
void OnReceive(IAsyncResult asyncResult)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = context.m_socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context.m_buffer, 0, bytesReceived);
ReceiveContext newContext = new ReceiveContext();
newContext.m_socket = context.m_socket;
m_socket.BeginReceive(newContext.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
catch(SocketException e)
{
if(e.SocketErrorCode == SocketError.ConnectionAborted) // Check if we disconnected on our end
{
return;
}
}
catch (Exception e)
{
// Error
log.Error(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, m_id, e.ToString()));
}
}
void OnSend(IAsyncResult asyncResult)
{
Socket socket = (Socket)asyncResult.AsyncState;
try
{
int bytesSent = socket.EndSend(asyncResult);
}
catch(Exception e)
{
log.Error(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, m_id, e.ToString()));
}
}
}
}
Main:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
namespace IntegrationTests
{
class Program
{
private static readonly ILog log = LogManager.GetLogger("root");
static void Main(string[] args)
{
try
{
XmlConfigurator.Configure();
log.Info("Starting Component Integration Tests...");
Client2 client = new Client2("127.0.0.1", 24001, "MyClientId", 60000);
client.Connect();
client.Login();
client.MakeRequest("StuffAndPuff");
System.Threading.Thread.Sleep(60000); // Sim work until user shutsdown
client.Disconnect();
}
catch (Exception e)
{
log.Error(string.Format("Caught an exception in main. Exception: {0}"
, e.ToString()));
}
}
}
}
EDIT:
Here is my additional attempt using the answer proposed by Evk, to the best of my ability. It works fine as far as I can tell.
Problem with this is, that I feel like I basically made everything that was asynchronous into a synchronous call, because of the requirements to lock around anything that would change the counter or the state of the socket. Again, I am novice at C# compared to my C++, so please do point out if I completely missed the mark interpreting his answer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IntegrationTests
{
public class Client
{
class ReceiveContext
{
public const int _bufferSize = 1024;
public byte[] _buffer = new byte[_bufferSize]; // Contains bytes from one receive
public StringBuilder _stringBuilder = new StringBuilder(); // Contains bytes for multiple receives in order to build message up to delim
}
private static readonly ILog _log = LogManager.GetLogger("root");
static private ulong _lastId = 1;
private ulong _id;
protected string _host;
protected int _port;
protected int _timeoutMilliseconds;
protected string _sessionId;
protected Socket _socket;
protected object _lockNumOutstandingAsyncOps;
protected int _numOutstandingAsyncOps;
private bool _disposed = false;
public Client(string host, int port, string sessionId, int timeoutMilliseconds)
{
_id = _lastId++;
_host = host;
_port = port;
_sessionId = sessionId;
_timeoutMilliseconds = timeoutMilliseconds;
_socket = null;
_numOutstandingAsyncOps = 0;
_lockNumOutstandingAsyncOps = new object();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(_disposed)
{
return;
}
if (disposing)
{
_socket.Close();
}
_disposed = true;
}
public void Connect()
{
lock (_lockNumOutstandingAsyncOps)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(_host);
IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], _port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.ReceiveTimeout = _timeoutMilliseconds;
_socket.SendTimeout = _timeoutMilliseconds;
try
{
_socket.Connect(endpoint);
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}"
, _id, e.ToString()));
return;
}
Debug.WriteLine(string.Format("Client #{0} connected to: {1}", _id, _socket.RemoteEndPoint.ToString()));
// Issue the first async receive
ReceiveContext context = new ReceiveContext();
++_numOutstandingAsyncOps;
_socket.BeginReceive(context._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context);
}
}
public void Disconnect()
{
if (_socket != null)
{
// We need to wait here until all outstanding async operations complete
// In order to avoid getting 'Object was disposed' exceptions in those async ops that use the socket
lock(_lockNumOutstandingAsyncOps)
{
Debug.WriteLine(string.Format("Client #{0} Disconnecting...", _id));
_socket.Shutdown(SocketShutdown.Both);
while (_numOutstandingAsyncOps > 0)
{
Monitor.Wait(_lockNumOutstandingAsyncOps);
}
_socket.Close();
_socket = null;
}
}
}
public void Login()
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string loginRequest = string.Format("loginstuff{0}", _clientId);
var data = Encoding.ASCII.GetBytes(loginRequest);
Debug.WriteLine(string.Format("Client #{0} Sending Login Request: {1}"
, _id, loginRequest));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} Login was called, but Socket is null or no longer connected."
, _id));
}
}
}
public void MakeRequest(string thingy)
{
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
string message = string.Format("requeststuff{0}", thingy);
var data = Encoding.ASCII.GetBytes(message);
Debug.WriteLine(string.Format("Client #{0} Sending Request: {1}"
, _id, message));
++_numOutstandingAsyncOps;
_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket);
}
else
{
Debug.WriteLine(string.Format("Client #{0} MakeRequest was called, but Socket is null or no longer connected."
, _id));
}
}
}
protected void OnReceive(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
ReceiveContext context = (ReceiveContext)asyncResult.AsyncState;
string data = null;
try
{
int bytesReceived = _socket.EndReceive(asyncResult);
data = Encoding.ASCII.GetString(context._buffer, 0, bytesReceived);
// If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received,
// the EndReceive method will complete immediately and return zero bytes
if (bytesReceived > 0)
{
StringBuilder stringBuilder = context._stringBuilder.Append(data);
int index = -1;
do
{
index = stringBuilder.ToString().IndexOf("#");
if (index != -1)
{
string message = stringBuilder.ToString().Substring(0, index + 1);
stringBuilder.Remove(0, index + 1);
Debug.WriteLine(string.Format("Client #{0} Received Data: {1}"
, _id, message));
}
} while (index != -1);
}
}
catch (SocketException e)
{
// Check if we disconnected on our end
if (e.SocketErrorCode == SocketError.ConnectionAborted)
{
// Ignore
}
else
{
// Error
Debug.WriteLine(string.Format("Client #{0} SocketException caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
}
catch (Exception e)
{
// Error
Debug.WriteLine(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
// Issue the next async receive
lock (_lockNumOutstandingAsyncOps)
{
if (_socket != null && _socket.Connected)
{
++_numOutstandingAsyncOps;
ReceiveContext newContext = new ReceiveContext();
_socket.BeginReceive(newContext._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext);
}
}
}
protected void OnSend(IAsyncResult asyncResult)
{
lock (_lockNumOutstandingAsyncOps)
{
try
{
int bytesSent = _socket.EndSend(asyncResult);
}
catch (Exception e)
{
Debug.WriteLine(string.Format("Client #{0} Exception caught OnSend. Exception: {1}"
, _id, e.ToString()));
Disconnect();
}
finally
{
--_numOutstandingAsyncOps;
Monitor.Pulse(_lockNumOutstandingAsyncOps);
}
}
}
}
}
You can use Monitor.Wait and Monitor.Pulse for that:
static int _outstandingOperations;
static readonly object _lock = new object();
static void Main() {
for (int i = 0; i < 100; i++) {
var tmp = i;
Task.Run(() =>
{
lock (_lock) {
_outstandingOperations++;
}
// some work
Thread.Sleep(new Random(tmp).Next(0, 5000));
lock (_lock) {
_outstandingOperations--;
// notify condition might have changed
Monitor.Pulse(_lock);
}
});
}
lock (_lock) {
// condition check
while (_outstandingOperations > 0)
// will wait here until pulsed, lock will be released during wait
Monitor.Wait(_lock);
}
}
I've been trying to implement the Emit Broadcast and On methods on C#.
I KNOW there is a package called SocketIO4DotNet and I do not wish to use it as it is deprecated. I rather understand how to do it.
I have tried to read the code of that package but it uses too dependancies over dependencies and it is going nowhere.
Currently I have the following code:
public class SocketIO
{
protected string host;
protected int port;
TcpClient client;
public SocketIO(string host, int port)
{
this.host = host;
this.port = port;
}
public bool connect()
{
if (isConnected())
{
return false;
}
try
{
client = new TcpClient();
client.Connect(host, port);
return true;
}
catch (Exception ex)
{
client = null;
return false;
}
}
public bool close()
{
if (!isConnected())
{
return false;
}
try
{
client.Close();
}
catch (Exception ex)
{
}
finally
{
client = null;
}
return true;
}
public void Emit(string message, string data)
{
if (!isConnected())
{
return;
}
NetworkStream nwStream = client.GetStream();
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(data);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
string received = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
}
protected bool isConnected()
{
if (client == null)
{
return false;
}
return true;
}
I need to understand how to create the listener (On method) and how to use the Emit and Broadcast in a way that I send a message along with the data.
So since I am unity but I do not want to use SocketIO as MonoBehaviour, I tweaked the Unity SocketIO package for unity to work without the Unity itself.
This is what I did:
First I have downloaded the package itself:
https://www.assetstore.unity3d.com/en/#!/content/21721
Then I changed the SocketIOComponent Awake method to be its constructor and changed its signature to accept the url (more params can be sent)
public SocketIOComponent(string url)
The next thing I did was creating a wrapper which I have called SocketIOAdapter
This is the code:
public class SocketIOAdapter {
SocketIOComponent socketIO;
protected Thread socketThread;
public SocketIOAdapter(string url)
{
socketIO = new SocketIOComponent(url);
}
public void Connect()
{
socketIO.Connect();
socketThread = new Thread(socketIO.Update);
socketThread.Start();
}
public void Emit(string eventName, JSONObject json)
{
socketIO.Emit(eventName, json);
}
public void On(string eventName, System.Action<SocketIOEvent> callback)
{
socketIO.On(eventName, callback);
}
}
And lastly I have tweaked the Update method of SocketIOComponent to sleep and continue rather than return, like this:
if (ackList.Count == 0)
{
Thread.Sleep(100);
continue;
}
if (DateTime.Now.Subtract(ackList[0].time).TotalSeconds < ackExpirationTime)
{
Thread.Sleep(100);
continue;
}
Finally I have used it like this:
socketIO = new SocketIOAdapter(serviceManager.getConfig().get("servers.socket") as string);
socketIO.Connect();
socketIO.On("connect", this.OnConnect);
Listened like this:
void OnConnect(SocketIOEvent e)
{
socketIO.Emit("shoot", new JSONObject("{}"));
}
So far so good, it's working well.
Hope this helps someone out there.