以下是我自己封装的一个XTcpListener类.1.当我想断开远程客户端时,无法断开(上网搜过资料,有说在接到NetWorkStream流后,关闭流就可以断掉远程结点)2.当远程客户端断开连接时,我这里总报一些错,比如BeginAcceptTcpClient,BeginRead处....3.收发数据都已正常(网络稳定的情况下)4.请各位高手们帮忙指正~~~飘过的也请留个记号~~~~由于很久没逛CSDN了,分不太多.....
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;namespace XNet
{
public class XListener
{
#region 同步连接设定
/// <summary>
/// 设置为同步连接,true为同步,false为异步
/// </summary>
private bool _isSync = true;
/// <summary>
/// 设置为同步连接,true为同步,false为异步
/// </summary>
public bool IsSync
{
get { return _isSync; }
set { _isSync = value; }
}
#endregion #region 是否正在监听
/// <summary>
/// 是否正在监听
/// </summary>
private bool _isListened = false;
/// <summary>
/// 是否正在监听
/// </summary>
public bool IsListened
{
get { return _isListened; }
set { _isListened = value; }
}
#endregion #region 挂起队列的最大长度
/// <summary>
/// 挂起队列的最大长度
/// </summary>
private int _backlog = 50;
/// <summary>
/// 挂起队列的最大长度,默认值为50
/// </summary>
public int Backlog
{
get { return _backlog; }
set { _backlog = value; }
}
#endregion #region 监听服务对象
/// <summary>
/// 监听服务
/// </summary>
private TcpListener server = null;
/// <summary>
/// 监听服务对象属性访问器
/// </summary>
public TcpListener Server
{
get { return server; }
set { server = value; }
}
#endregion #region 缓冲区大小
private int _bufferLength = 1024;
/// <summary>
/// 缓冲区大小
/// </summary>
public int BufferLength
{
get { return _bufferLength; }
set { _bufferLength = value; }
}
#endregion #region 构造函数
/// <summary>
/// 构造函数,没有参数(基本不起任何作用)
/// </summary>
public XListener()
{
}
/// <summary>
/// 根据已有的TcpListener进行初始化
/// </summary>
/// <param name="listener">TcpListener</param>
public XListener(TcpListener listener)
{
server = listener;
}
/// <summary>
/// 监听本地端口(不推荐使用此方法,MSDN说此方法已过时)
/// </summary>
/// <param name="port">端口号</param>
public XListener(int port)
{
server = new TcpListener(port);
}
/// <summary>
/// 根据IP地址和Port端口进行初始化
/// </summary>
/// <param name="ipAddress">IP地址</param>
/// <param name="port">端口号</param>
public XListener(IPAddress ipAddress, int port)
{
server = new TcpListener(ipAddress, port);
}
/// <summary>
/// 根据本地结点进行初始化
/// </summary>
/// <param name="iep">本地结点 IPEndPoint类型</param>
public XListener(IPEndPoint iep)
{
server = new TcpListener(iep);
}
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;namespace XNet
{
public class XListener
{
#region 同步连接设定
/// <summary>
/// 设置为同步连接,true为同步,false为异步
/// </summary>
private bool _isSync = true;
/// <summary>
/// 设置为同步连接,true为同步,false为异步
/// </summary>
public bool IsSync
{
get { return _isSync; }
set { _isSync = value; }
}
#endregion #region 是否正在监听
/// <summary>
/// 是否正在监听
/// </summary>
private bool _isListened = false;
/// <summary>
/// 是否正在监听
/// </summary>
public bool IsListened
{
get { return _isListened; }
set { _isListened = value; }
}
#endregion #region 挂起队列的最大长度
/// <summary>
/// 挂起队列的最大长度
/// </summary>
private int _backlog = 50;
/// <summary>
/// 挂起队列的最大长度,默认值为50
/// </summary>
public int Backlog
{
get { return _backlog; }
set { _backlog = value; }
}
#endregion #region 监听服务对象
/// <summary>
/// 监听服务
/// </summary>
private TcpListener server = null;
/// <summary>
/// 监听服务对象属性访问器
/// </summary>
public TcpListener Server
{
get { return server; }
set { server = value; }
}
#endregion #region 缓冲区大小
private int _bufferLength = 1024;
/// <summary>
/// 缓冲区大小
/// </summary>
public int BufferLength
{
get { return _bufferLength; }
set { _bufferLength = value; }
}
#endregion #region 构造函数
/// <summary>
/// 构造函数,没有参数(基本不起任何作用)
/// </summary>
public XListener()
{
}
/// <summary>
/// 根据已有的TcpListener进行初始化
/// </summary>
/// <param name="listener">TcpListener</param>
public XListener(TcpListener listener)
{
server = listener;
}
/// <summary>
/// 监听本地端口(不推荐使用此方法,MSDN说此方法已过时)
/// </summary>
/// <param name="port">端口号</param>
public XListener(int port)
{
server = new TcpListener(port);
}
/// <summary>
/// 根据IP地址和Port端口进行初始化
/// </summary>
/// <param name="ipAddress">IP地址</param>
/// <param name="port">端口号</param>
public XListener(IPAddress ipAddress, int port)
{
server = new TcpListener(ipAddress, port);
}
/// <summary>
/// 根据本地结点进行初始化
/// </summary>
/// <param name="iep">本地结点 IPEndPoint类型</param>
public XListener(IPEndPoint iep)
{
server = new TcpListener(iep);
}
#endregion
#region 开始监听
/// <summary>
/// 开始监听服务,默认的挂起队列最大长度为50,可以通过Backlog属性修改或使用本方法的重载方法
/// </summary>
public void Start()
{
Start(_backlog);
}
/// <summary>
/// 开始监听服务
/// </summary>
/// <param name="backlog">挂起队列的最大长度</param>
public void Start(int backlog)
{
if (server == null)
{
throw new Exception("监听服务对象为空,不能进行监听服务");
}
else if (_isListened)
{
throw new Exception("系统已经开始监听");
}
else
{
try
{
server.Start(backlog);
_isListened = true;
if (_isSync)
{
SyncAccept();
}
else
{
server.BeginAcceptTcpClient(new AsyncCallback(OnAsyncAcceptTcpClient), null);
}
}
catch (System.Exception ex)
{
throw new Exception(ex.Message);
}
}
}
#endregion #region 字节计数
private long _receiveBytes = 0;
/// <summary>
/// 收到字节数
/// </summary>
public long ReceiveBytes
{
get { return _receiveBytes; }
set { _receiveBytes = value; }
}
#endregion #region 当监听到客户连接时,是否对客户端发送数据及发送什么数据
/// <summary>
/// 当监听到客户连接时,是否对客户端发送数据
/// </summary>
public bool IsAutoResponse = false;
/// <summary>
/// 当监听到客户连接时,对客户端发送的数据(UTF-8编码格式)
/// </summary>
public string IsAutoResponseData = "";
#endregion #region 同步方式处理接收的连接及收到的数据
/// <summary>
/// 同步阻塞接收连接
/// </summary>
private void SyncAccept()
{
while (true)
{
_isListened = true;
TcpClient client = server.AcceptTcpClient();
//触发有客户端连接上来的事件
if (client != null)
{
if (OnAcceptClientEvent != null)
{
IPEndPoint iep = (IPEndPoint)client.Client.RemoteEndPoint;
IPEndPoint localPoint = (IPEndPoint)client.Client.LocalEndPoint;
AcceptClientArgs e = new AcceptClientArgs(iep.Address, iep.Port, DateTime.Now, localPoint.Address, localPoint.Port);
OnAcceptClientEvent(this, e);
}
} //放入线程池处理接收到的连接
ThreadPool.QueueUserWorkItem(new WaitCallback(SyncProcessTcpClient), client);
}
}
/// <summary>
/// 同步方式处理接收到的数据
/// </summary>
/// <param name="obj"></param>
private void SyncProcessTcpClient(object obj)
{
TcpClient client = obj as TcpClient;
NetworkStream ns = client.GetStream();
try
{
if (ns.CanRead)
{
byte[] buffer = new byte[_bufferLength];
int length = ns.Read(buffer, 0, buffer.Length);
//对连接上的客户端发送数据
if (ns.CanWrite)
if (IsAutoResponse)
{
byte[] data = Encoding.UTF8.GetBytes("[UTF-8] " + IsAutoResponseData);
ns.Write(data, 0, data.Length);
} ns.Close();
Array.Resize(ref buffer, length);
//收到数据字节计数
_receiveBytes += buffer.Length; //处理接收到的数据
if (OnMessageArrivalEvent != null)
{
IPEndPoint iep = (IPEndPoint)client.Client.RemoteEndPoint;
IPEndPoint localPoint = (IPEndPoint)client.Client.LocalEndPoint;
MessageEventArgs e = new MessageEventArgs(iep.Address, iep.Port, buffer, DateTime.Now, localPoint.Address, localPoint.Port); OnMessageArrivalEvent(this, e);
} client.Close();
}
}
catch (System.Exception ex)
{
throw new Exception(ex.Message);
}
}
#endregion #region 异步接收连接并处理缓冲区数据
/// <summary>
/// 异步处理接收到的连接
/// </summary>
/// <param name="iar"></param>
private void OnAsyncAcceptTcpClient(IAsyncResult iar)
{
if (!_isListened)
{
if (server != null)
throw new Exception("监听服务未开启,无法处理接收到的异步连接");
}
else
{
try
{
if (server == null)
return;
TcpClient client = server.EndAcceptTcpClient(iar);
//触发有客户端连接上来的事件
if (client != null)
{
if (OnAcceptClientEvent != null)
{
IPEndPoint iep = (IPEndPoint)client.Client.RemoteEndPoint;
IPEndPoint localPoint = (IPEndPoint)client.Client.LocalEndPoint;
AcceptClientArgs e = new AcceptClientArgs(iep.Address, iep.Port, DateTime.Now, localPoint.Address, localPoint.Port);
OnAcceptClientEvent(this, e);
}
} //开始异步接收连接
server.BeginAcceptTcpClient(new AsyncCallback(OnAsyncAcceptTcpClient), null); NetworkStream ns = client.GetStream();
if (ns.CanRead)
{
byte[] buffer = new byte[_bufferLength];
ns.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnAsyncRead), new object[] { client, ns, buffer });
}
}
catch (System.NullReferenceException nrex)
{
Console.WriteLine("连接已断开");
}
catch (System.Exception ex)
{
throw new Exception(ex.Message);
}
}
}
/// <summary>
/// 异步读取数据并处理接收到的数据
/// </summary>
/// <param name="iar"></param>
private void OnAsyncRead(IAsyncResult iar)
{
object[] obj = iar.AsyncState as object[];
TcpClient client = obj[0] as TcpClient;
NetworkStream ns = obj[1] as NetworkStream;
byte[] buffer = obj[2] as byte[];
try
{
if (ns.CanRead)
{
int length = ns.EndRead(iar);
Array.Resize(ref buffer, length);
//收到数据字节计数
_receiveBytes += buffer.Length;
}
//对连接上的客户端发送数据
if (ns.CanWrite)
{
if (IsAutoResponse)
{
byte[] data = Encoding.UTF8.GetBytes("[UTF-8] " + IsAutoResponseData);
ns.Write(data, 0, data.Length);
}
}
if (server == null || _isListened == false)
return; byte[] buffer2 = new byte[_bufferLength];
if (ns.CanRead)
{
ns.BeginRead(buffer2, 0, buffer.Length, new AsyncCallback(OnAsyncRead), new object[] { client, ns, buffer2 });
}
//处理接收到的数据
if (OnMessageArrivalEvent != null)
{
IPEndPoint iep = (IPEndPoint)client.Client.RemoteEndPoint;
IPEndPoint localPoint = (IPEndPoint)client.Client.LocalEndPoint;
MessageEventArgs e = new MessageEventArgs(iep.Address, iep.Port, buffer, DateTime.Now, localPoint.Address, localPoint.Port); OnMessageArrivalEvent(this, e);
}
}
catch (System.Exception ex)
{
throw new Exception(ex.Message);
}
}
#endregion #region 停止监听
/// <summary>
/// 停止监听服务
/// </summary>
public void Stop()
{
try
{
//关闭现有的连接
server.Server.Shutdown(SocketShutdown.Both);
server.Server.Close();
server.Stop();
_isListened = false;
IsAutoResponse = false;
}
catch (System.Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
server = null;
}
}
#endregion /// <summary>
/// 数据到达事件
/// </summary>
public event EventHandler<MessageEventArgs> OnMessageArrivalEvent = null;
/// <summary>
/// 监听到有客户端连接
/// </summary>
public event EventHandler<AcceptClientArgs> OnAcceptClientEvent = null; //public event EventHandler<string> OnException = null;
}}
using System.Collections.Generic;
using System.Text;namespace XNet
{
public class ExceptionArgs : EventArgs
{
/// <summary>
/// 异常发生时间
/// </summary>
public DateTime ExceptionTime = DateTime.Now;
/// <summary>
/// 异常对象
/// </summary>
public Exception ex = null;
/// <summary>
/// 异常类
/// </summary>
/// <param name="ExceptionTime"></param>
/// <param name="exception"></param>
public ExceptionArgs(System.Exception exception)
{
this.ex = exception;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;namespace XNet
{
/// <summary>
/// 网络连接收到的数据
/// </summary>
public class MessageEventArgs : EventArgs
{
private System.Net.IPAddress _ip;
private int _port;
private byte[] _data;
private DateTime _dt;
private System.Net.IPAddress _localIp;
private int _localPort; /// <summary>
/// 远程IP地址
/// </summary>
public System.Net.IPAddress RemoteIP
{
get { return _ip; }
set { _ip = value; }
}
/// <summary>
/// 远程端口号
/// </summary>
public int RemotePort
{
get { return _port; }
set { _port = value; }
}
/// <summary>
/// 缓冲区数据
/// </summary>
public byte[] Data
{
get { return _data; }
set { _data = value; }
}
/// <summary>
/// 数据到达时间
/// </summary>
public DateTime ReceiveTime
{
get { return _dt; }
set { _dt = value; }
}
/// <summary>
/// 本地IP
/// </summary>
public System.Net.IPAddress LocalIP
{
get { return _localIp; }
set { _localIp = value; }
}
/// <summary>
/// 本地端口
/// </summary>
public int LocalPort
{
get { return _localPort; }
set { _localPort = value; }
} /// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">IP地址</param>
/// <param name="port">端口号</param>
/// <param name="data">缓冲区的字节数组</param>
/// <param name="dt">数据到达时间</param>
/// <param name="localIp">本地IP地址</param>
/// <param name="localPort">本地端口</param>
public MessageEventArgs(System.Net.IPAddress remoteIp, int remotePort, byte[] data, DateTime dt, System.Net.IPAddress localIp, int localPort)
{
_ip = remoteIp;
_port = remotePort;
_data = data;
_dt = dt;
_localIp = localIp;
_localPort = localPort;
}
}
}
当时我在做tcp通讯的时候也遇到了类似的问题,后来我采用了下面一些手段加以解决:
1、对tcpclient再外包一层类,里面附加了该连接的最后一次与我服务端通讯时间,产生的交互数据队列,以及处理逻辑等,这样Tcplistener层只负责接收连接,然后就生成我的外包一层的对象,并持有住。外包对象的业务处理封装在内部,全部是异步委托方式进行。
2在Tcplistener层内置一个timer,定时扫描所有的tcpclient外包对象,检查最后一次访问时间是否超过某一阙值,超过了就检查该连接是否已经失活,一旦失活就关闭其基础socket和流,并不再持有该外包对象(让其被GC自动回收。)代码如下:
try
{
Socket.Select(LinkBuff_Copy, null, null, 10000);
}
catch
{
LinkBuff_Copy.Clear();
}
finally
{
if (LinkBuff_Copy.Count > 0)
{
//一一释放tcpclient,并从正式连接缓冲中移除失活对象
}
}
呵呵,我的思路希望对你有所帮助。