SendAsync and SocketAsyncEventArgs correct usage with fixed tickrate - c#

im currently working on a TCP Server using Sockets in C#.
I encountered a problem and i'm not able to solve it..
My Server accepts connections and creates a new NetEntity for each client.
These NetEntities handle Sending/Receive packets and are updated in a loop that ticks 30 times per second.
Problem :
After the 4th client is connected i receive this exception(only on the 4th NetEntity).
An asynchronous socket operation is already in progress using this SocketAsyncEventArgs instance.
If i use if (Interlocked.Read(ref m_sending) == 1) return; to make sure it is done sending, the 4th NetEntity never send any data.
When deleting this line the exception occurs.
I just cant wrap my head around using SendAsync and SocketAsyncEventArgs properly with a fixed tickrate.
public void Send(Packet packet)
{
OutgoingQueue.Enqueue(packet);
}
private void QueueSend()
{
Interlocked.Exchange(ref m_sending, 1);
var sendTask = new Task(Send);
sendTask.ContinueWith((t) => t.Dispose());
sendTask.Start();
}
private void Send()
{
if (Interlocked.Read(ref m_sending) == 1) return;
if (!Socket.Connected) return;
if (OutgoingQueue.TryDequeue(out var packet))
{
packet.Serialize(m_packetWriter);
}
var writerBuffer = m_packetWriter.ToArray();
Array.Copy(writerBuffer, m_sendBuffer, writerBuffer.Length);
try
{
SendArgs.SetBuffer(SendArgs.Offset, writerBuffer.Length);
if (!Socket.SendAsync(SendArgs))
{
SendCompleted(Socket, SendArgs);
}
}
catch (SocketException)
{
//Client disconnect
}
catch (Exception ex)
{
Log.Error($"NetEntity {m_id} send exception: {ex.Message} - {ex.StackTrace} - {ex.InnerException}");
}
}
private void SendCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
if (OutgoingQueue.Count > 0)
{
//QueueSend();
}
m_packetWriter.Reset();
}
else
{
Interlocked.Exchange(ref m_sending, 0);
}
}
Setup looks like this
SendArgs = new SocketAsyncEventArgs();
SendArgs.SetBuffer(m_sendBuffer, 0, BufferSize);
SendArgs.Completed += SendCompleted;
I appreciate any help and code Improvements.
Thanks!

Related

Xamarin.Forms (Android) Bluetooth intermittently working

Scenario:
I am building an Android app using Xamarin.Forms that will be deployed to a group of devices. All but one of the devices will be doing some data collection, and the remaining device will be the "hub" to aggregate all of the data and do some reporting. I am using Bluetooth for the device-to-device communication. The 'hub', labelled the master, acts as the client, and all of the collectors act as the server. I have a prototype working with a single server and client...almost.
Occasionally the client/master will be unable to read from the server/collector. I am struggling to find the reason for why this is and would appreciate any help.
Symptoms:
The client's call to .Read() from the InputStream will occasionally block indefinitely, even though the server has written to the output stream. I've added a timeout to this call to prevent the app from getting stuck entirely.
This happens intermittently, but I've found some pattern to when it works and when it doesn't
It seems to be related to the 'server' app, and not the client. The client can remain open, running, and initiate the request to connect to the server as often as needed.
It always works the first time the 'server' app is launched and connected to. It ususally works the second time. By the third connection, .Read() will consistently block/timeout. Closing and reopening the app on the server "cleans the slate" so to speak and it will work again.
Once it starts failing, it seems to be 'stuck' in a failed state.
Removing the app from the foreground (but not closing/killing it) seems to correct the faulted state, and the connection/read will happen successfully as long as the app/UI remains in the background. Once restored to the foreground, it starts failing again.
Code:
All of the bluetooth handling is done by a single class/service that I'm injecting using Xamarin.Forms DependencyService. All of the devices will, on startup (via the constructor of this class), loop indefinitely on a background thread, waiting for connections and repeating. Much of this bluetooth code is based on the Bluetooth Chat example, as well as some other online resources I've found (some android native/java, some Xamarin/C#)
The master will, on demand (triggered by press of a button in the UI), attempt to connect to any collectors (via bonded bluetooth devices) and read data from them. There is also a simple UI component which essentially serves as a console log.
Here is the service class in its entirety.
public class GameDataSyncService : IGameDataSyncService
{
private const string UUID = "8e99f5f1-4a07-4268-9686-3a288326e0a2";
private static Task acceptLoopTask;
private static Task syncDataTask;
private static readonly object locker = new object();
private static bool running = false;
public event EventHandler<DataSyncMessage> MessageBroadcast;
public GameDataSyncService()
{
// Every device will listen and accept incoming connections. The master will make the connections.
lock (locker)
{
if (acceptLoopTask == null)
{
acceptLoopTask = Task.Factory.StartNew(AcceptLoopWorker, TaskCreationOptions.LongRunning);
}
}
}
public void SyncData()
{
lock (locker)
{
if (running)
{
BroadcastMessage("Previous data sync is still running.", DataSyncMessageType.Warning);
return;
}
else
{
running = true;
syncDataTask = Task.Factory.StartNew(SyncDataWorker);
}
}
}
private void BroadcastMessage(string message, DataSyncMessageType type = DataSyncMessageType.Info)
{
MessageBroadcast?.Invoke(this, new DataSyncMessage { Text = message, Type = type });
}
private async Task AcceptLoopWorker()
{
int count = 0;
while (true)
{
BluetoothServerSocket serverSocket = null;
BluetoothSocket clientSocket = null;
try
{
BroadcastMessage($"Listening for incoming connection...", DataSyncMessageType.Debug);
serverSocket = BluetoothAdapter.DefaultAdapter.ListenUsingRfcommWithServiceRecord(nameof(GameDataSyncService), Java.Util.UUID.FromString(UUID));
clientSocket = serverSocket.Accept(); // This call blocks until a connection is established.
BroadcastMessage($"Connection received from {clientSocket.RemoteDevice.Name}. Sending data...", DataSyncMessageType.Info);
var bytes = Encoding.UTF8.GetBytes($"Hello World - {string.Join(" ", Enumerable.Repeat(Guid.NewGuid(), ++count))}");
await clientSocket.OutputStream.WriteAsync(bytes, 0, bytes.Length);
clientSocket.OutputStream.Flush();
// Give the master some time to close the connection from their end
await Task.Delay(1000*3);
}
catch (Exception ex)
{
BroadcastMessage($"{ex.GetType().FullName}: {ex.Message}", DataSyncMessageType.Debug);
}
finally
{
try { clientSocket?.InputStream?.Close(); } catch { }
try { clientSocket?.InputStream?.Dispose(); } catch { }
try { clientSocket?.OutputStream?.Close(); } catch { }
try { clientSocket?.OutputStream?.Dispose(); } catch { }
try { clientSocket?.Close(); } catch { }
try { clientSocket?.Dispose(); } catch { }
try { serverSocket?.Close(); } catch { }
try { serverSocket?.Dispose(); } catch { }
BroadcastMessage($"Connection closed.", DataSyncMessageType.Debug);
}
}
}
private async Task SyncDataWorker()
{
BroadcastMessage($"Beginning data sync...");
foreach (var bondedDevice in BluetoothAdapter.DefaultAdapter.BondedDevices.OrderBy(d => d.Name))
{
BluetoothSocket clientSocket = null;
try
{
clientSocket = bondedDevice.CreateRfcommSocketToServiceRecord(Java.Util.UUID.FromString(UUID));
BroadcastMessage($"Connecting to {bondedDevice.Name}...");
try
{
clientSocket.Connect();
}
catch
{
BroadcastMessage($"Connection to {bondedDevice.Name} failed.", DataSyncMessageType.Error);
}
while (clientSocket.IsConnected)
{
byte[] buffer = new byte[1024];
var readTask = clientSocket.InputStream.ReadAsync(buffer, 0, buffer.Length);
if (await Task.WhenAny(readTask, Task.Delay(1000)) != readTask)
{
BroadcastMessage($"Read timeout...", DataSyncMessageType.Error);
break;
}
int bytes = readTask.Result;
BroadcastMessage($"Read {bytes} bytes.", DataSyncMessageType.Success);
if (bytes > 0)
{
var text = Encoding.UTF8.GetString(buffer.Take(bytes).ToArray());
BroadcastMessage(text, DataSyncMessageType.Success);
break;
}
}
}
catch (Exception ex)
{
BroadcastMessage($"{ex.GetType().FullName}: {ex.Message}", DataSyncMessageType.Debug);
}
finally
{
try { clientSocket?.InputStream?.Close(); } catch { }
try { clientSocket?.InputStream?.Dispose(); } catch { }
try { clientSocket?.OutputStream?.Close(); } catch { }
try { clientSocket?.OutputStream?.Dispose(); } catch { }
try { clientSocket?.Close(); } catch { }
try { clientSocket?.Dispose(); } catch { }
}
}
await Task.Delay(1000 * 3);
BroadcastMessage($"Data sync complete!");
lock (locker)
{
running = false;
}
}
}
What I've tried (nothing below has had any effect):
Most of these were from 'solutions' from other stackoverflow posts.
Adding arbitrary delays into the mix
Making sure to explicitly close/dispose everything, in order, including the streams
Tried replacing the socket handling with their 'Insecure' counterparts.
Adjusting my read timeout to something arbitrarily long, in case a second wasn't enough.
Disabling/Re-enabling bluetooth on the server/collector before .Accept() ing a new connection (resorted to trying random stuff by this point)
Video:
I took a video of this happening.
The tablet in the back is the collector/server The tablet in the foreground is the master/client. When the video starts, the client is displaying some previous attempts, and the server app is in the background (but running). I demonstrate that the .Read works when the collector/server app is in the background, but not the foreground. Each request to begin data sync has a corresponding entry to the "console" (or a warning if I pressed it too soon)
https://youtu.be/NGuGa7upCU4
Summary:
To the best of my knowledge, my code is correct. I have no idea what else to change/fix to get this working more reliably. The actual connection seems like it is successful (based on logs from the server/collector, unfortunately not shown in the video), but the issue lies somewhere in the .Write (or .Read). ANy help, suggestions, or insight would be awesome.
Try the following, changed all to using:
private async Task AcceptLoopWorker()
{
int count = 0;
while (true)
{
try
{
BroadcastMessage("Listening for incoming connection...", DataSyncMessageType.Debug);
using (var serverSocket = BluetoothAdapter.DefaultAdapter.ListenUsingRfcommWithServiceRecord(nameof(GameDataSyncService), Java.Util.UUID.FromString(UUID)))
using (var clientSocket = serverSocket.Accept()) // This call blocks until a connection is established.
{
BroadcastMessage(string.Format("Connection received from {0}. Sending data...", clientSocket.RemoteDevice.Name), DataSyncMessageType.Info);
var bytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Hello World - {0}", string.Join(" ", Enumerable.Repeat(Guid.NewGuid(), ++count))));
await clientSocket.OutputStream.WriteAsync(bytes, 0, bytes.Length);
}
await Task.Delay(1000 * 3); // Give the master some time to close the connection from their end
}
catch (Java.IO.IOException ex)
{
BroadcastMessage(string.Format("IOException {0}: {1}", ex.GetType().FullName, ex.Message), DataSyncMessageType.Debug);
}
catch (Java.Lang.Exception ex)
{
BroadcastMessage(string.Format("Exception {0}: {1}", ex.GetType().FullName, ex.Message), DataSyncMessageType.Debug);
}
}
}
private async Task SyncDataWorker()
{
BroadcastMessage("Beginning data sync...");
foreach (var bondedDevice in BluetoothAdapter.DefaultAdapter.BondedDevices.OrderBy(d => d.Name))
{
try
{
using (var clientSocket = bondedDevice.CreateRfcommSocketToServiceRecord(Java.Util.UUID.FromString(UUID)))
{
BroadcastMessage(string.Format("Connecting to {0}...", bondedDevice.Name));
if (!clientSocket.IsConnected)
{
clientSocket.Connect();
}
if (clientSocket.IsConnected)
{
byte[] buffer = new byte[1024];
var readTask = clientSocket.InputStream.ReadAsync(buffer, 0, buffer.Length);
if (await Task.WhenAny(readTask, Task.Delay(1000)) != readTask)
{
BroadcastMessage("Read timeout...", DataSyncMessageType.Error);
break;
}
int bytes = readTask.Result;
BroadcastMessage(string.Format("Read {0} bytes.", bytes), DataSyncMessageType.Success);
if (bytes > 0)
{
var text = System.Text.Encoding.UTF8.GetString(buffer.Take(bytes).ToArray());
BroadcastMessage(text, DataSyncMessageType.Success);
break;
}
}
else
{
BroadcastMessage("Not Connected...", DataSyncMessageType.Error);
}
}
}
catch (Java.IO.IOException ex)
{
BroadcastMessage(string.Format("IOException {0}: {1}", ex.GetType().FullName, ex.Message), DataSyncMessageType.Debug);
}
catch (Java.Lang.Exception ex)
{
BroadcastMessage(string.Format("Exception {0}: {1}", ex.GetType().FullName, ex.Message), DataSyncMessageType.Debug);
}
}
await Task.Delay(1000 * 3);
BroadcastMessage("Data sync complete!");
lock (locker)
{
running = false;
}
}

Instead of scanning multiple ports I'm scanning only one and when port is closed my app closes

I'm trying to scan multiple ports at once using asynchronymous scanning. The problem is that I can only display the first working port and then waiting like 20 seconds my app is closing with out telling me that the port is closed.
What could be wrong with this code?
private void btnStart_Click(object sender, EventArgs e)
{
for (int port = 80; port < 100; port++)
{
ScanPort(port);
}
}
private void ScanPort(int port)
{
var client = new TcpClient();
try
{
client.BeginConnect(IPAddress.Parse("74.125.226.84"), port, new AsyncCallback(CallBack), client);
}
catch (SocketException)
{
client.Close();
}
}
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
client.EndConnect(result);
if (client.Connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}
In your callback method, I would make sure close the connection and dispose of the TcpClient. Also TcpClient.EndConnect(IAsyncResult) can also throw exceptions. I also do not see where capturing the port number for display to the user. I would write the callback something like this.
Edit: I didn't actually compile or execute my code (sorry). I also found this other article that shows how to create a port scanner in C#, http://www.dijksterhuis.org/building-a-simple-portscanner-in-c/ There is a comment in this post stating,
There is a gotcha here : The .NET implementation of TCPClient.Close() function does not actually close the connection properly. So we need to do the additional steps of obtaining the stream representing the connection and closing this as well before calling TCPClient.Close.
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
bool connected = false;
try
{
client.EndConnect(result);
connected = client.Connected;
}
catch (SocketException)
{
}
catch (ObjectDisposedException)
{
}
finally
{
if (client.Connected)
{
client.Close();
}
client.Dispose();
}
if (connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}

Cancelling a background worker which is continuously reading from a network stream

I have a TCP server running which spits out messages of 2 bytes at regular intervals.
I'm trying to create a client side form which connects to the server and continuously reads from the stream until I click a disconnect button on the form.
So far the client works fine except that I cannot disconnect. I set the CancellationPending to true but it seems to reset back to false before the dowork method gets a chance to set e.Cancel.
I'm also sure there must be a more acceptable way of continuously reading the stream and writing to the form - at the moment I am calling RunWorkerAsync within the Worker Completed method to achieve the loop!
private void Disconnect()
{
commsWorker1.CancelAsync();
}
private void ReadFromStream()
{
try
{
commsWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
writeToBox("Error: " + ex.Message);
}
}
//background worker dowork method
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
if (true == commsWorker1.CancellationPending)
{
e.Cancel = true;
}
else
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
}
}
}
//background worker workercompleted method
private void BackGroundDisplayMessages(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close connection here
}
else
{
writeToBox((String)e.Result);
commsWorker1.RunWorkerAsync();
}
}
}
}
Can't you just loop inside the background worker method?
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
return;
}
}
}
Then upon disconnect simply close the socket. This will cause the Exception to be thrown in the while loop and you can exit gracefully through the catch block.
Edit: Then you can update the GUI from the loop after each message is read. Make sure the handle to the control you are updating is available (assuming it's called box):
delegate void updateDelegate(String p);
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
box.BeginInvoke(new updateDelegate(writeToBox), reply);
}
catch (Exception ex)
{
return;
}
}
}
BeginInvoke is required in this case because you are trying to update the GUI from another thread, which is not allowed. This method forwards the update to the GUI thread.
It seems that you are invoking RunWorkerAsync() in the worker complete method and that resets your CancellationPending prop. I think you can try to fix this by adding to Disconnect() method some disconnectFlag = true; and in WorkerComplete method you should add:
if (e.Cancelled || disconnectFlag)
{
disconnectFlag = false;
//close connection here
} else ...

Need help with a C# Asynchronous Socket Program that has multiple sends

So, I have a board game that uses Asynchronous socket to operate over LAN. The thing is, I have little to no understanding of Asynchronous socket programming, or of threads, but I do my best to try.
I based my program off a chat program, so I use that part to send multiple strings.
So, here's part of the code for the Client:
private void Connect(IAsyncResult iar)
{
try
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndConnect(iar);
g_bmsg = new byte[1024];
check = true;
string szData = "#Player " + lblName.Text + " connected.";
sendingFunction(szData);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Receive), g_client_conn);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void Receive(IAsyncResult iar)
{
if (g_bmsg.Length != 0)
{
SetLabelText(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
private void SetLabelText(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { SetLabelText(txt); }));
else
{
lblBuffer.Text = txt;
}
if (lblBuffer.Text.StartsWith("#"))
{
lblStatmsg.Text = lblBuffer.Text.Replace("#", "");
}
if (lblBuffer.Text.StartsWith("$"))
{
lblStatmsg.Text = "Server Settings Received.";
lblBuffer.Text = lblBuffer.Text.Replace("$", "");
option_Postn = int.Parse(lblBuffer.Text.Substring(0, 1));
option_First = int.Parse(lblBuffer.Text.Substring(2, 1));
}
if (lblBuffer.Text.StartsWith("#"))
{
MessageBox.Show(lblBuffer.Text);
}
}
And here's part of the code for the Server:
private void Accept(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), g_server_conn);
}
private void Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void Recieve(IAsyncResult iar)
{
try
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
SetLabelText(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void SetLabelText(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { SetLabelText(txt); }));
else
{
lblBuffer.Text = txt;
}
if (lblBuffer.Text.StartsWith("#"))
{
lblStatmsg.Text = lblBuffer.Text.Replace("#", "");
}
else if (lblBuffer.Text.StartsWith("#"))
{
MessageBox.Show(lblBuffer.Text);
}
else if (lblBuffer.Text.StartsWith("%"))
{
}
}
Basically, since the game sends more than messages (it can send settings, or game pieces, etc), I ran the sender function everytime I need to send something, and on the other side, the Receiver decodes the string sent based on the first character (# means the string is a setting, for example).
The problem is, after the first time both host and client sent something to one another, they can't seem to send again. No error, no message, no nothing. Just won't send. Is there something wrong with the sendingFunction? Or perhaps the delegate something? I don't know. Some advice would be appreciated, guys. And thanks in advance.
You're never calling BeginReceive again. The typical practice in async socket programming is to process the received data, then call BeginReceive again so that you can then process the next bit of data that comes in.

Socket not registering when the other end is closed

I'm working on a client/server application where the connections from the client to the server stay open until the client application is closed.
If the server application goes down unexpectedly, while the client is reading data, I want the client to treat this as an exception, but then to catch the exception and raise an event with the exception as the argument.
I've written a test that I think should test that this system works, but the object I'm testing doesn't seem to register that the socket is closed unless I put in a break point and then continue.
The important part of the test looks like this:
StreamingMonitor sm = new StreamingMonitor();
bool errored = false;
string msg = "";
sm.ErrorOccurred += (s, a) =>
{
errored = true;
msg = a.Exception.Message;
};
sm.Enabled = true;
client = listener.AcceptTcpClient();
client.GetStream().Write(BitConverter.GetBytes(10000), 0, 4);
client.Close();
while(!errored)
{}
Assert.AreEqual("A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call", msg);
The TcpListener object listener is listening to the loopback address.
The StreamingMonitor begins listening for the length of the data to retrieve when it is enabled. The length of data is always assumed to fit into a signed 32 bit integer.
When the message length is received then this methods is called.
private void GotMessageLength(IAsyncResult asyncResult)
{
try
{
client.Client.EndReceive(asyncResult);
if(firstMessage)
{
firstMessage = false;
if (Connected != null)
{
Connected(this, new EventArgs());
}
}
int msgLen = BitConverter.ToInt32(messageLength, 0);
byte[] message = new byte[msgLen];
List<byte> lbMessage = new List<byte>();
int bytesReturned = client.Client.Receive(message);
int remaining = (msgLen < bytesReturned) ? bytesReturned - msgLen : msgLen - bytesReturned;
if(remaining > 0)
{
if (bytesReturned > 0)
{
for (int i = 0; i < bytesReturned; i++)
{
lbMessage.Add(message[i]);
}
}
while(remaining > 0)
{
if(!client.Connected)
{
throw new SocketException((int)SocketError.Shutdown);
}
bytesReturned = client.Client.Receive(message);
remaining = (remaining < bytesReturned) ? bytesReturned - remaining : remaining - bytesReturned;
if (bytesReturned > 0)
{
for (int i = 0; i < bytesReturned; i++)
{
lbMessage.Add(message[i]);
}
}
}
message = lbMessage.ToArray();
}
MessageReceived(this, new MessageReceivedEventArgs(message));
if (Enabled)
{
client.Client.BeginReceive(messageLength, 0, 4, SocketFlags.None, GotMessageLength, null);
}
}
catch (SocketException ex)
{
if(ErrorOccurred != null)
{
ErrorOccurred(this, new ErrorEventArgs(ex));
}
}
catch (ObjectDisposedException)
{
}
}
The method reads data from the network stream until it has read the specified number of bytes. If the remote connection closes then it should raise a socket exception.
However, the unit test gets caught in a infinite loop, waiting for the error to occur, because the socket in the StreamingMonitor never realises that the other end has closed.
How can I make the SteamingMonitor realise that the server has gone?
Is this possible on the loopback address?
Sorry for all the code, I couldn't think how to cut the method down.
I can give some general pointers on the area that might help.
Loopback (or just using localhost) in general does not act the same way as a real network. Scenarios like how much data is send/received in each call to the socket api. So always test or real network connections.
The socket api will only find out if the other side is disconnected upon trying to send to it ( i think that is correct). So some sort of heartbeat functionality comes in handy =)
Edit: You can also get the SocketException to determine if the the other side is disconnected by trying to receive (did some basic test on some old code of mine).
protected void ReceiveCallback(IAsyncResult ar)
{
var so = (StateObject)ar.AsyncState;
if (!so.Socket.Connected) return;
try
{
int read = so.Socket.EndReceive(ar);
if (read > 0)
ProcessBuffer(so, so.Buffer, read);
so.Socket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, ReceiveCallback, so);
}
catch (SocketException e)
{
Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: SocketException");
Output.WriteLine(e.Message);
RaiseDisconnected();
}
catch (ObjectDisposedException e)
{
Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: ObjectDisposedException");
Output.WriteLine(e.Message);
RaiseDisconnected();
}
}
This will call my disconnect function if the other side crashes for some reason.
Hope it helps

Categories