我正在开发一个专门用于Tcp通信的小型.dll,
在我的项目中,我有一个使用TcpListener接受传入连接的服务器类。 传入连接存储在字典中并从那里进行处理。
每个连接的代码如下:
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);
}
}
*请注意,编码器是一个负责解码和编码数据包的类。
除了上述内容之外,我还有一个基于套接字的TcpClient(我希望稍后可以在Silverlight中重用),代码如下:
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);
}
}
我为两者创建了一组单元测试。
在其中一个测试中,我将数据从客户端发送到服务器。作品。 在另一个测试中,我将数据从服务器的连接发送到客户端。史诗失败。 我一直在Connection的on_data_received中获得Socket ObjectDisposed异常。说实话,我不知道发生了什么 - 因此我需要一些帮助。
我正在使用.Net 4,VS 2010,我的机器操作系统是Win7(如果此信息有任何帮助)
此致 Maciek
答案 0 :(得分:2)
我终于明白了。
问题看起来很无辜,请检查下面的代码。
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)); }
}
当处理(thx到using关键字)时,BinaryWriter或NetworkStream套接字将被释放(我不确定这是否是所需的行为) - 从而中断连接。删除“使用”子句解决了这个问题。
在此处发布答案,以防其他人遇到类似的事情。
答案 1 :(得分:1)
在on_data_received
处理程序中,您在Client.Client.BeginReceive(...)
之前致电Client.Client.EndReceive(...)
。
BeginReceive可以同步完成,导致异常并处理你的Connection
,所以你应该在EndReceive之后调用它。
private void on_data_received(IAsyncResult ar)
{
try
{
int bytesRead = Client.Client.EndReceive(ar);
if (bytesRead > 0)
{
byte[] data = new byte[bytesRead];
Array.Copy(m_message, data, bytesRead);
m_coder.Push(data);
Client.Client.BeginReceive(m_message, 0, m_message.Length,
SocketFlags.None, new AsyncCallback(on_data_received), null);
}
else
{
//TODO Close the connection
}
}
catch(Exception ex)
{
Console.WriteLine("Connection::on_data_received : {0}", ex.Message);
handle_connection_lost(new ConnectionLostArgs(Id));
}
}
注意:接收0字节时应关闭连接,这意味着远程端点已关闭。不这样做可能会导致无限循环。