I'm working on a small .dll dedicated to Tcp communication,
In my project I've got a server class that uses a TcpListener to accept incoming connections. Incoming connections are stored in a Dictionary and handled from there.
Each connection's code looks like :
public class Connection : ConnectionBase<Coder.Coder>
{
public Connection(TcpClient client, Guid id) : base()
{
Id = id;
Client = client;
}
public void Start()
{
IsConnected = true;
Client.Client.BeginReceive(m_message, 0, m_message.Length, SocketFlags.None, new AsyncCallback(on_data_received), null);
}
public void Stop()
{
try
{
Client.Close();
handle_connection_lost(new ConnectionLostArgs(Id));
}
catch
{ }
}
public void Send(byte[] data)
{
try
{
using (NetworkStream s = Client.GetStream())
{
using (BinaryWriter w = new BinaryWriter(s))
{
var buffer = m_coder.Encode(data);
w.Write(buffer);
w.Flush();
}
}
}
catch
{ handle_connection_lost(new ConnectionLostArgs(Id)); }
}
public Guid Id { get; set; }
public TcpClient Client { get; set; }
private byte[] m_message = new byte[1024];
private void on_data_received(IAsyncResult ar)
{
try
{
Client.Client.BeginReceive(m_message, 0, m_message.Length,
SocketFlags.None, new AsyncCallback(on_data_received), null);
int bytesRead = Client.Client.EndReceive(ar);
if (bytesRead > 0)
{
byte[] data = new byte[bytesRead];
Array.Copy(m_message, data, bytesRead);
m_coder.Push(data);
}
}
catch(Exception ex)
{
Console.WriteLine("Connection::on_data_received : {0}", ex.Message);
handle_connection_lost(new ConnectionLostArgs(Id));
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
Stop();
}
catch
{ }
}
base.Dispose(disposing);
}
}
*Please note that the Coder is a class responsible for decoding and encoding data packets.
Apart from the above, I've got a socket-based TcpClient (which I'm hoping to reuse later with Silverlight), The code is as follows :
public class TcpSocketClient : TcpClientBase<Coder.Coder>
{
public static TcpSocketClient Create(string host, int port)
{
if (port == 0)
return null;
return new TcpSocketClient(host, port);
}
private TcpSocketClient(string host, int port) : base()
{
IsConnected = false;
RemoteEndpoint = new DnsEndPoint(host, port);
}
public void Start()
{
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
byte[] buffer = new byte[1024];
SocketAsyncEventArgs e = new SocketAsyncEventArgs()
{
RemoteEndPoint = RemoteEndpoint,
UserToken = m_socket,
};
e.SetBuffer(buffer, 0, buffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_connect_completed);
m_socket.ConnectAsync(e);
}
public void Stop()
{
try
{
m_socket.Close();
m_socket.Dispose();
}
catch (ObjectDisposedException)
{ }
}
public void Send(byte[] data)
{
try
{
var buffer = m_coder.Encode(data);
SocketAsyncEventArgs e = new SocketAsyncEventArgs()
{
BufferList = new List<ArraySegment<byte>>() { new ArraySegment<byte>(buffer) },
UserToken = m_socket
};
m_socket.SendAsync(e);
}
catch (Exception ex)
{ handle_client_disconnected(ex.Message); }
}
#region Properties
public DnsEndPoint RemoteEndpoint { get; private set; }
#endregion
#region Fields
Socket m_socket;
#endregion
void handle_socket_connect_completed(object sender, SocketAsyncEventArgs e)
{
if (!m_socket.Connected)
{
handle_client_disconnected("Failed to connect");
return;
}
e.Completed -= handle_socket_connect_completed;
e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_async_receive);
handle_client_connected();
m_socket.ReceiveAsync(e);
}
void handle_socket_async_receive(object sender, SocketAsyncEventArgs e)
{
if (e.BytesTransferred == 0)
{
handle_client_disconnected("Connection closed by the remote host");
try { m_socket.Close(); }
catch { }
return;
}
try
{
byte[] buffer = new byte[e.BytesTransferred];
Array.Copy(e.Buffer, buffer, e.BytesTransferred);
m_coder.Push(buffer);
}
catch { }
m_socket.ReceiveAsync(e);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
RemoteEndpoint = null;
m_socket.Close();
m_socket.Dispose();
}
catch
{ }
}
base.Dispose(disposing);
}
}
I've created a set of unit tests for both.
In one of the tests I send data from the client to the server. Works. In another test I send data from the server's connection to a client. Epic fail. I keep getting Socket ObjectDisposed exceptions in Connection's on_data_received. To be honest I have no idea what's going on - therefore I require some aid.
I'm using .Net 4, VS 2010, My machine's OS is Win7 (if this info is of any help)
Regards, Maciek
I've figured it out - finally.
The problem was looking pretty innocent, examine the below code.
public void Send(byte[] data)
{
try
{
using (NetworkStream s = Client.GetStream())
{
using (BinaryWriter w = new BinaryWriter(s))
{
var buffer = m_coder.Encode(data);
w.Write(buffer);
w.Flush();
}
}
}
catch
{ handle_connection_lost(new ConnectionLostArgs(Id)); }
}
When disposing (thx to the using keyword) either the BinaryWriter or the NetworkStream the socket would get disposed (I'm not sure if this is the desired behavior) - and thus break the connection. Removing the "using" clauses solved the problem.
Posting the answer here in case anyone else runs into something similar.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With