Automatically reconnecting asynchronous socket client - c#

I'm working with a windows form application in C#. I'm using a socket client which is connecting in an asynchronous way to a server.
I would like the socket to try reconnecting immediately to the server if the connection is broken for any reason.
My receive routine looks like this
public void StartReceiving()
{
StateObject state = new StateObject();
state.workSocket = this.socketClient;
socketClient.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(OnDataReceived), state);
}
private void OnDataReceived(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int iReadBytes = client.EndReceive(ar);
if (iReadBytes > 0)
{
byte[] bytesReceived = new byte[iReadBytes];
Buffer.BlockCopy(state.buffer, 0, bytesReceived, 0, iReadBytes);
this.responseList.Enqueue(bytesReceived);
StartReceiving();
receiveDone.Set();
}
else
{
NotifyClientStatusSubscribers(false);
}
}
catch (Exception e)
{
}
}
When NotifyClientStatusSubscribers(false) is called the function StopClient is executed:
public void StopClient()
{
this.canRun = false;
this.socketClient.Shutdown(SocketShutdown.Both);
socketClient.BeginDisconnect(true, new AsyncCallback(DisconnectCallback), this.socketClient);
}
private void DisconnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the disconnection.
client.EndDisconnect(ar);
this.socketClient.Close();
this.socketClient = null;
}
catch (Exception e)
{
}
}
Now I try reconnecting by calling the following functions:
public void StartClient()
{
this.canRun = true;
this.MessageProcessingThread = new Thread(this.MessageProcessingThreadStart);
this.MessageProcessingThread.Start();
this.socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.socketClient.LingerState.Enabled = false;
}
public void StartConnecting()
{
socketClient.BeginConnect(this.remoteEP, new AsyncCallback(ConnectCallback), this.socketClient);
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
// Signal that the connection has been made.
connectDone.Set();
StartReceiving();
NotifyClientStatusSubscribers(true);
}
catch(Exception e)
{
StartConnecting();
}
}
The socket reconnects when the connection is available, but after a couple of seconds I get the following unhandled exception:
"A connection request was made on an already connected socket."
How is this possible?

It's possible if you get an exception in ConnectCallback and you've actually connected successfully. Set a break point in the catch statement of the ConnectCallback and see if there is an exception that gets raised there- currently there is nothing that will tell you that you got an exception.

Related

Why is my form freezing when I invoke BeginConnect?

I'm using this example from Asynchronous Client Socket Example
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
// State object for receiving data from remote device.
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient {
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient() {
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.GetHostEntry("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect( remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client,"This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
I'm in the process of programming the networkside of my application, right now just some testing, but I have an odd problem. My UI freezes when I call the StartClient method. This example is in a seperate class and just call it in my mainform with: AsynchronousClient.Startclient();
I need to mention that right now there is not a server listening, but why is the form freezing, afterall i'm not using the synchronous example. I have read on the internet that it could be because the form thread must be run from the main thread, but I haven't changed anything regarding to this. In my opinion the form should just continue to function even if it can't connect.
How do I achieve this? I can give you my code, but I think it's not very relevant. Just as I said I call the StartClient method from the AsynchronousClient class, it's static, and then call it somewhere in my main class. (in a button right now).
Not relevant I think, but I know the packets are send, I have checked this with wireshark.
Per my comment, AsynchronousClient.Startclient() is not async since it uses ManualResetEvent to block.
I personally recommend to not use the Socket directly. It really depends on what you are doing but there are better APIs for remote communication.
HTTP, Json-RPC, gRPC, ZeroMQ, etc...
Depends on your use case. Using raw TCP sockets is almost always an overkill.
Anyway, to make the method truly asynchronous you would have to get rid of the WaitOne calls.
The reason the WaitOne calls are there in the first place, I believe, is to make the code more readable, but you could theoretically do the "Sending" logic in the "Connecting" callback and do the "Receiving" logic in the "Sending" callback.
Like this
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
// State object for receiving data from remote device.
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient {
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient() {
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.GetHostEntry("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect( remoteEP,
new AsyncCallback(ConnectCallback), client);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// we do it here now...
Send(client,"This is a test<EOF>");
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// after all is done, we shut down the socket
client.Shutdown(SocketShutdown.Both);
client.Close();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// We do it here now...
Receive(client);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
This should work. With this, StartClient doesn't block. It returns right after it calls BeginConnect.
However, this is a very old and dirty API, that is why I would think twice before using native Sockets.
You can explain your goal better and I'll try to offer something better.

asynchronous client interacting with form

I've searched for the better part of the day trying to figure this out, and I'm certain I'm missing a simple solution somewhere.
I'm using an asynchronous socket client connection to monitor incoming data from a TCP connection. I've implemented a ManualResetEvent and Callback method as recommended by the msdn sample, but the Callback method is not able to invoke the methods used to output the received data to my Windows form. How do I take the data received from the socket, and send it to the text box in my form?
I'm certain there's a simple trick that I'm missing somewhere. The sample code is written for a console application. How do I have the form react to incoming data from the Socket?
Update:
I think you've got me on the right track. I tried putting in code to use delegates, but I clearly don't quite understand how delegates work, as it keeps throwing the following errors:
An object reference is required for the non-static field, method, or property 'APRS_SMS_Gateway.MainForm.SockOutputDelegate'
Can you get me a little closer? It's the ConnectCallBack Handler that I'm trying to work on right now, but I want to use the same method (SockOutput) from all the CallBacks.
public partial class MainForm : Form
{
private AutoResetEvent receiveNow;
public delegate void SockOutputDelegatetype(string text); // Define delegate type
public SockOutputDelegatetype SockOutputDelegate;
// The port number for the remote device.
private const int port = 14580;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
public MainForm()
{
InitializeComponent();
SockOutputDelegate = new SockOutputDelegatetype(SockOutput);
}
private void Form1_Load(object sender, EventArgs e)
{
CommSetting.APRSServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//StartClient();
}
private void StartClient()
{
try
{
IPHostEntry ipHostInfo = Dns.GetHostEntry("rotate.aprs.net");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
//Socket APRSServer = new Socket(AddressFamily.InterNetwork,
// SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
CommSetting.APRSServer.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), CommSetting.APRSServer);
connectDone.WaitOne();
//Show the connect string from the host
Receive(CommSetting.APRSServer);
//receiveDone.WaitOne();
//Show the connection response
//SockOutput(response);
}
catch (Exception e)
{
SockOutput(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket APRSServer = (Socket)ar.AsyncState;
// Complete the connection.
APRSServer.EndConnect(ar);
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
MainForm.Invoke(MainForm.SockOutputDelegate, e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
//throw new ApplicationException(e.ToString());
response = e.ToString();
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
response = e.ToString();
}
}
void SockOutput(string text)
{
txtSockLog.AppendText(text);
txtSockLog.AppendText("\r\n");
}
}
}
It sounds like your socket side is working OK, its just updating the winform that's causing problems. You may find your answer here Update WinForm Controls from another thread _and_ class.

Releasing a TCP port, asynchronous call

I have a TCP server which constantly receives TCP streams from different sources. I have an external event that triggers a stop, and when that happens, the TCP port need to be released. The code below works properly, except for the closure. The close is not releasing the port, and the TCP port is stuck in the listening state. How can I fix this?
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace TestTest
{
public class tcpserver
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 2097152;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
public Form10 m_parent;
Socket parentlistener;
// Incoming data from client.
public static string data = null;
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener()
{
}
public void StartListening(Form10 frm10)
{
m_parent = frm10;
try
{
// Data buffer for incoming data.
byte[] bytes = new Byte[2097152];
int port = Convert.ToInt32(m_parent.textBox2.Text.Trim());
IPAddress ipv4 = IPAddress.Parse(m_parent.comboBox1.SelectedItem.ToString().Trim());
IPEndPoint localEndPoint = new IPEndPoint(ipv4, port);
// Create a TCP/IP socket.
parentlistener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
parentlistener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
LingerOption lo = new LingerOption(false, 0);
parentlistener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lo);
// Setting the array to have this ip and port
m_parent.m_parent.setUsedIPport(
m_parent.comboBox1.SelectedItem.ToString().Trim() +
":" + m_parent.textBox2.Text.Trim());
// Bind the socket to the local endpoint and
// listen for incoming connections.
try
{
parentlistener.Bind(localEndPoint); // THIS IS THE PORT I WOULD LIKE
// TO RELEASE ON AN EXTERNAL TRIGGER
parentlistener.Listen(100);
while (m_parent.getStopState() == false)
{
//MessageBox.Show("L");
allDone.Reset();
//// Start an asynchronous socket to listen for connections.
parentlistener.BeginAccept(
new AsyncCallback(AcceptCallback), parentlistener);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
catch (Exception) {
MessageBox.Show("Error binding to tcp port", "ERROR"); return;
}
}
public void AcceptCallback(IAsyncResult ar)
{
try
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
LingerOption lo = new LingerOption(false, 0);
handler.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Linger,
lo);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback(ReadCallback), state);
}
catch (Exception er) {
//MessageBox.Show(er.ToString());
}
}
public void ReadCallback(IAsyncResult ar)
{
try
{
String line = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
string temporaryString = "";
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
line = state.sb.ToString(); // This goes into an external
// store, and code is not here
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize,
0, new AsyncCallback(ReadCallback), state);
}
}
}
catch (Exception er) {
//MessageBox.Show(er.ToString());
}
}
public void stopListener()
{
try
{
parentlistener.Shutdown(SocketShutdown.Both);
//parentlistener.Disconnect(true);
parentlistener.Close(); // PROBLEM !!! The code is not releasing
// the port, the tcp port continues to
// remain in the listening state.
parentlistener.Dispose();
}
catch (Exception Ex) {
//MessageBox.Show(Ex.ToString());
}
}
}
}
}
We had this exact problem just a few days ago. In our case we had to set the Linger mode to TRUE (you have it set to false) with a timeout of 0 (which you already have).
It's a bit counter-intuitive, but it worked.

TCP Server : "Only one usage of each socket address (protocol/network address/port) is normally permitted."

I have a TCP server (implemented as a windows service) for listening to GPS devices in a vehicle tracking application, after random period of time from its operation I got the following error : "Only one usage of each socket address (protocol/network address/port) is normally permitted." While I am sure that I am closing every socket after using it. so can anyone tell me what is the problem here I have the MaxUserPort value in windows server 2008 registry with (65534) and the TCPTimeWaitDelay value with (30 seconds) ?
Here is the code:
1) The Main Thread :
private void MainThread() {
byte[] bytes = new Byte[1024];
IPEndPoint localEndPoint = new IPEndPoint(0, this.port);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Addedd on [20/03/2012] by Sherif Mosaad for handling socket exceptions ...
//listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1);
try {
listener.Bind(localEndPoint);
listener.Listen(100);
while (active) {
mainDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
while (active)
if (mainDone.WaitOne(100, true))
break;
}
listener.Shutdown(SocketShutdown.Both);
listener.Close();
Thread.Sleep(2000);
} catch (Exception e) {
if (OnError != null)
OnError(this, e.ToString());
LogManager.LogError(e, "TCPSimpleServer MainThread");
}
}
2) The AcceptCallback handler:
private void AcceptCallback(IAsyncResult ar) {
mainDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = null;
try
{
handler = listener.EndAccept(ar);
}
catch
{
try
{
listener.Shutdown(SocketShutdown.Both);
listener.Close();
Thread.Sleep(2000);
}
catch { return; }
}
if (OnConnect != null)
OnConnect(this, handler);
StateObject state = new StateObject();
state.workSocket = handler;
state.endPoint = (IPEndPoint)handler.RemoteEndPoint;
stateObjectDictionary.Add(state, state.workSocket);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
3) The ReadCallback Handler:
private void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = 0;
try
{
bytesRead = handler.EndReceive(ar);
}
catch (Exception e)
{
// Connection closed by client
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
return;
}
if (bytesRead > 0)
{
string data = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
if (OnDataAvailable != null)
OnDataAvailable(this, handler, data);
try
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (Exception e)
{
// Connection closed by client
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
return;
}
}
else
{
// Connection closed by peer
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
}
}
Finally the state object :
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public IPEndPoint endPoint;
}
Any help please?
There's a race condition. You call mainDone.Set which allows another thread to proceed to BeginAccept while the current thread moves towards EndAccept. Which will get there first? If you start accepting before finishing the previous accept, I suspect this error might pop up.
Fix? You need to set mainDone event after you call EndAccept
Even better, follow a simpler pattern without synchronization primitives. I outline one here.

Async socket crashes when I close the socket

The code shown below appears to almost work. If I create an instance of it and call "Connect" all works fine. When I call "Disconnect", sometimes everything is fine (mainly if I add a breakpoint and step through the function slowly). If I don't use a breakpoint the class(being hosted as a win forms app) seems to disappear (the form does) but visual studio still thinks it's running. In visual studio's output window I get "A first chance exception of type 'System.ObjectDisposedException' occurred in System.dll". Can anyone spot what I'm doing wrong?
// State object for reading client data asynchronously
public class StateObject
{
private Guid ID = Guid.NewGuid();
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
}
public class NetworkComms : IBasePanel
{
private static ILog _log = LogManager.GetCurrentClassLogger();
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
private static Socket _client = null;
private static IPEndPoint _endpoint = null;
public event ReceiveMessageEventHandler OnReceiveMessage;
public NetworkComms(string address, int port)
{
_endpoint = new IPEndPoint(GetIPAddress(address), port);
}
private IPAddress GetIPAddress(string address)
{
IPAddress ipAddress = null;
if (IPAddress.TryParse(address, out ipAddress))
{
return ipAddress;
}
else
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(address);
return ipHostInfo.AddressList[ipHostInfo.AddressList.Count() - 1];
}
}
private void ConnectCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
_log.DebugFormat("Socket connected to {0}", client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
private void Receive()
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = _client;
// Begin receiving the data from the remote device.
_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
private void ReceiveCallback(IAsyncResult ar)
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
ReceivedNewMessage(Encoding.Default.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
// Signal that all bytes have been received.
receiveDone.Set();
}
}
private static void SendCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
_log.DebugFormat("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
public void SendMessage(byte[] message)
{
_client.BeginSend(message, 0, message.Length, 0, new AsyncCallback(SendCallback), _client);
sendDone.WaitOne();
}
public void Connect()
{
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_client.BeginConnect(_endpoint, new AsyncCallback(ConnectCallback), _client);
connectDone.WaitOne();
Receive();
}
public void Disconnect()
{
try
{
_client.Shutdown(SocketShutdown.Both);
_client.Close();
}
finally
{
_client = null;
connectDone.Reset();
sendDone.Reset();
receiveDone.Reset();
}
}
private void ReceivedNewMessage(string message)
{
if (this.OnReceiveMessage != null)
{
this.OnReceiveMessage(message);
}
}
public bool IsConnected
{
get
{
if (_client == null) return false;
return _client.Connected;
}
}
}
All of your callbacks need to handle exceptions, which are relativly common in network programming.
In this case what is probably happening is that client.EndReceive(ar); is throwing an ObjectDisposedException because the socket is already closed when its called.

Categories

Resources