根据.net 3.5中的异步Socket完成的一个模块,用队列来处理tcp数据包的粘包问题,用事件将数据包通知给上层调用;
代码写的不是很好,发出来希望和大家共同交流学习。
因为是实际项目中用的模块,所以有些地方是按照项目需求做的,大体的结构还是没问题的,除了代码写的差点外!!!
一,主要类,进行异步Socket的基本处理,.net3.5中的Socket完成端口模型的实现
{
private bool IsDisponse = false ;
~ NET_Server()
{
this .Dispose( false );
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this ); // 请求系统不要调用指定对象的终结器
}
protected virtual void Dispose( bool disponse)
{
if ( ! IsDisponse)
{
try {
this ._ServerRun = false ;
foreach (DictionaryEntry client in ClientsTable)
{
((Socket)client.Value).Shutdown(SocketShutdown.Both);
}
this .Server.Close();
this .ClientsTable.Clear();
clientpool.poolClear();
}
catch { }
IsDisponse = true ;
}
}
private Socket Server = null ;
private BufferManager Buffer;
private SocketAsyncEventArgsPool RWpool; // .net 3.5异步socket的关键
private IPEndPoint _iep;
private int Max_Recv_Buff_Size = 1024 ;
private int Max_Connected_Count = 512 ;
private halfbuffpool clientpool;
public NET_Server( string ipsrc, int port)
{
_iep = new IPEndPoint(IPAddress.Parse(ipsrc), port);
this .ClientsTable = new Hashtable();
clientpool = new halfbuffpool(); // 管理客户端的一个数组对象
}
public NET_Server( string ipsrc, int port, int nconn, int nbuff)
{
// 初始化
this .Max_Recv_Buff_Size = nbuff;
this .Max_Connected_Count = nconn;
_iep = new IPEndPoint(IPAddress.Parse(ipsrc), port);
this .ClientsTable = new Hashtable();
clientpool = new halfbuffpool();
}
// 数据收发的超时时间
private int _RecvOutTime = 3000 ;
public int RecvTimeout { get { return _RecvOutTime; } set { _RecvOutTime = value; } }
private int _SendTimeout = 1000 ;
public int SendTimeout { get { return _SendTimeout; } set { _SendTimeout = value; } }
public event ServerEventHandler Notification;
// 开启服务器
public bool StartServer()
{
this ._ServerRun = true ;
this .Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
bool result = false ;
if (IsDisponse)
{
throw new ObjectDisposedException( " Server is Disponse! " );
}
try
{
// 设定与服务器相关参数
this .Server.Bind( this ._iep);
this .Server.Listen( 5 );
this .Server.SendTimeout = this ._SendTimeout;
this .Server.ReceiveTimeout = this ._RecvOutTime;
result = true ;
// 创建并初始化缓冲区管理对象
Buffer = new BufferManager( this .Max_Connected_Count * this .Max_Recv_Buff_Size, this .Max_Recv_Buff_Size);
Buffer.Inint();
// 构造SocketAsyncEventArgs池
RWpool = new SocketAsyncEventArgsPool( this .Max_Connected_Count);
for ( int i = 0 ; i < this .Max_Connected_Count;i ++ )
{
SocketAsyncEventArgs asyniar = new SocketAsyncEventArgs();
asyniar.Completed += new EventHandler < SocketAsyncEventArgs > (Asyn_Completed);
RWpool.Push(asyniar);
}
Console.WriteLine( " Begin Listen.... " );
Accept();
}
catch (Exception ex) { Notification( null , new ServerEventArgs(ArgsType.EXCEPTION, ex.Message, null ));}
return result;
}
private void Accept() // 开始接受连接
{
if ( ! _ServerRun) return ;
if (RWpool.Count > 0 )
{
SocketAsyncEventArgs asyniar = RWpool.Pop();
if ( ! Server.AcceptAsync(asyniar)) // 弹出一个SocketAsyncEventArgs对象开始AcceptAsyn
{
BeginAccep(asyniar);
// 如果I/O挂起等待异步则触发AcceptAsyn_Asyn_Completed事件
// 此时I/O操作同步完成,不会触发Asyn_Completed事件,所以指定BeginAccept()方法
}
}
else
{
Console.WriteLine( " Had Max Connected! " );
}
}
private Hashtable ClientsTable;
private void BeginAccep(SocketAsyncEventArgs e)
{
try
{
if (e.SocketError == SocketError.Success)
{
string clientip = string .Format( " {0} " ,((IPEndPoint)e.AcceptSocket.RemoteEndPoint).Address);
if (ClientsTable.Contains(clientip))
{
e.AcceptSocket.Shutdown(SocketShutdown.Both);
e.AcceptSocket = null ; // 保持SocketAsynEventArgs池 的堆栈平衡
RWpool.Push(e);
return ;
}
else
this .ClientsTable.Add(clientip, e.AcceptSocket);
Notification( null , new ServerEventArgs(ArgsType.CLIENTCONN, clientip, null ));
// 查找客户如果不存在,则添加一个新客户
halfbuff client = clientpool.GetClient((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
if (client == null )
{
client = new halfbuff((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
client.HalfEvent += new EventHandler < HaflBuffEventArgs > (OnHalfEvent);
clientpool.poolAdd(client);
}
clientpool.poolAdd(client);
// 接受一个连接,发送欢迎信息
e.AcceptSocket.Send(Encoding.ASCII.GetBytes( " send " ));
if (Buffer.SetBuffer(e))
{
if ( ! e.AcceptSocket.ReceiveAsync(e)) // 是否触发 Asyn_Commpleted事件
{
BeginReceive(e);
}
}
}
else
{
e.AcceptSocket = null ; // 保持SocketAsynEventArgs池 的堆栈平衡
RWpool.Push(e);
Console.WriteLine( " Not Accept! " );
}
}
catch (Exception ex){
e.AcceptSocket = null ; // 保持SocketAsynEventArgs池 的堆栈平衡
RWpool.Push(e);
Notification( null , new ServerEventArgs(ArgsType.EXCEPTION, ex.Message, null ));
}
finally
{
Accept();
}
}
private void OnHalfEvent( object sender,HaflBuffEventArgs e)
{
Notification( null , new ServerEventArgs(ArgsType.RECEIVE, "" , e.BUFF));
}
private bool _debug = false ;
public bool Debug { get { return _debug; } set { _debug = value; } }
private void BeginReceive(SocketAsyncEventArgs e)
{
halfbuff client = clientpool.GetClient((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0 )
{
byte [] data = new byte [e.BytesTransferred];
Array.Copy(e.Buffer, e.Offset, data, 0 , data.Length); // 从e.Buffer块中复制数据出来,保证它可重用
if (_debug)
Notification( null , new ServerEventArgs(ArgsType.DEBUG, " debug " ,data));
if (data.Length > 3 ) // 排除心跳包 01
{
if (client != null )
client.pool.Enqueue(data);
}
if ( ! e.AcceptSocket.ReceiveAsync(e))
{
BeginReceive(e); //
}
}
else
{
string clientip = string .Format( " {0} " ,((IPEndPoint)e.AcceptSocket.RemoteEndPoint).Address);
this .ClientsTable.Remove(clientip);
Console.WriteLine( " A Client Disconnected,{0} " ,e.AcceptSocket.RemoteEndPoint);
Notification( null , new ServerEventArgs(ArgsType.CLIENTDISCONN, clientip, null ));
// 查找客户存在,则删除客户
if (client != null )
{
// e.AcceptSocket.Shutdown(SocketShutdown.Both);
clientpool.poolDel(client);
}
e.AcceptSocket = null ;
Buffer.FreeBuffer(e);
RWpool.Push(e);
if (RWpool.Count == 1 )
{
Accept();
}
}
}
private void Asyn_Completed( object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Accept:
BeginAccep(e);
break ;
case SocketAsyncOperation.Receive:
BeginReceive(e);
break ;
}
}
private bool _ServerRun = true ;
public bool ServerRun { get { return _ServerRun; } set { _ServerRun = value; } }
public void StopServer()
{
this ._ServerRun = false ;
foreach (DictionaryEntry client in ClientsTable)
{
((Socket)client.Value).Shutdown(SocketShutdown.Both);
}
if ( this .Server != null )
this .Server.Close();
this .ClientsTable.Clear();
Console.WriteLine( " The Server Stoped! " );
}
}
二,缓冲区管理类,动态管理缓冲区的
{
private Byte[] buffer;
private Int32 bufferSize;
private Int32 numSize;
private Int32 currentIndex;
private Stack < Int32 > freeIndexPool;
public BufferManager(Int32 numsize, Int32 buffersize)
{
this .numSize = numsize;
this .bufferSize = buffersize;
}
public void Inint()
{
buffer = new byte [numSize];
freeIndexPool = new Stack < int > (numSize / bufferSize);
}
internal void FreeBuffer(SocketAsyncEventArgs args)
{
freeIndexPool.Push(args.Offset);
args.SetBuffer( null , 0 , 0 );
}
internal Boolean SetBuffer(SocketAsyncEventArgs args)
{
if ( this .freeIndexPool.Count > 0 )
{
args.SetBuffer( this .buffer, this .freeIndexPool.Pop(), this .bufferSize);
}
else
{
if (( this .numSize - this .bufferSize) < this .currentIndex)
{
return false ;
}
args.SetBuffer( this .buffer, this .currentIndex, this .bufferSize);
this .currentIndex += this .bufferSize;
}
return true ;
}
public void Clear()
{
lock ( this )
{
freeIndexPool.Clear();
buffer = null ;
}
}
}
三,是.net 3.5异步socket中对SocketAsyncEventArgs 对象进行管理的类
/// Based on example from http://msdn2.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.socketasynceventargs.aspx
/// Represents a collection of reusable SocketAsyncEventArgs objects.
/// </summary>
internal sealed class SocketAsyncEventArgsPool
{
/// <summary>
/// SocketAsyncEventArgs栈
/// </summary>
Stack < SocketAsyncEventArgs > pool;
/// <summary>
/// 初始化SocketAsyncEventArgs池
/// </summary>
/// <param name="capacity"> 最大可能使用的SocketAsyncEventArgs对象. </param>
internal SocketAsyncEventArgsPool(Int32 capacity)
{
this .pool = new Stack < SocketAsyncEventArgs > (capacity);
}
/// <summary>
/// 返回SocketAsyncEventArgs池中的 数量
/// </summary>
internal Int32 Count
{
get { return this .pool.Count; }
}
/// <summary>
/// 弹出一个SocketAsyncEventArgs
/// </summary>
/// <returns> SocketAsyncEventArgs removed from the pool. </returns>
internal SocketAsyncEventArgs Pop()
{
lock ( this .pool)
{
return this .pool.Pop();
}
}
/// <summary>
/// 添加一个 SocketAsyncEventArgs
/// </summary>
/// <param name="item"> SocketAsyncEventArgs instance to add to the pool. </param>
internal void Push(SocketAsyncEventArgs item)
{
if (item == null )
{
throw new ArgumentNullException( " Items added to a SocketAsyncEventArgsPool cannot be null " );
}
lock ( this .pool)
{
this .pool.Push(item);
}
}
}
四,自定义的一个处理粘包和客户端管理的类
其中 tcp的粘包处理流程是:
Socket异步接收数据包-->将数据包添加到处理队列-->单独线程读取队列-->将数据包bytes的十六进制转为字符串模式-->根据数据包分隔符的字符对数据包进行分割处理
利用包头和包尾的标识符重新组合包-->用事件通知给上层调用
需要注意的地方:1, 客户端管理类的实现,每个客户连接都需要一个队列和一个处理包的线程;
2, C#中很方便的将包转为十六进制字符串形式进行重新的拆包组包,
3, 资源清理,即一个客户端断开连接或异常掉线后对它拥有的队列与线程的释放工作
/* 此类设计的目的主要是为了处理半个数据包造成的丢包问题
/*********************************************************************** */
public class HaflBuffEventArgs:EventArgs
{
public HaflBuffEventArgs( byte [] buff)
{
_buff = buff;
}
private byte [] _buff;
public byte [] BUFF { get { return _buff; } }
}
public class halfbuff
{
public event EventHandler < HaflBuffEventArgs > HalfEvent;
public halfbuff(IPEndPoint iep)
{
client = iep;
pool = new Queue < byte [] > ();
TimerCallback call = new TimerCallback(TimerCall);
mutex = new Mutex();
timer = new System.Threading.Timer(call, null , 0 , 500 );
}
public IPEndPoint client;
public Queue < byte [] > pool;
public System.Threading.Timer timer;
private System.Threading.Mutex mutex;
private string temphead = "" , tempend = "" , tempall = "" ;
private void TimerCall( object info)
{
mutex.WaitOne();
HaflBuffEventArgs e;
while (pool.Count > 0 )
{
byte [] data = pool.Dequeue();
if (data.Length > 180 ) continue ; // 如果数据包长度大于 180 则丢弃
string strdata = Hex.byteToHexStr(data);
string [] sArry = Regex.Split(strdata, " 11FF " );
if (sArry[ 0 ] != "" )
{
temphead = sArry[ 0 ];
tempall = tempend + temphead;
if (tempall.Substring( 0 , 4 ) == " 11FF " && tempall.Substring(tempall.Length - 4 , 4 ) == " EEFF " )
{
e = new HaflBuffEventArgs(Hex.ConvertHexToBytes(tempall));
HalfEvent( null , e);
temphead = "" ;
tempend = "" ;
tempall = "" ;
}
}
if (sArry.Length > 1 )
{
for ( int i = 1 ; i < sArry.Length; i ++ )
{
if (sArry[i].Contains( " EEFF " ))
{
// 一个完整的包
e = new HaflBuffEventArgs(Hex.ConvertHexToBytes( " 11FF " + sArry[i]));
HalfEvent( null , e);
}
else
{
if (i == sArry.Length - 1 )
{
tempend = " 11FF " + sArry[i];
}
}
}
}
}
mutex.ReleaseMutex();
}
}
public class halfbuffpool
{
private ArrayList arry;
public halfbuffpool()
{
arry = new ArrayList();
}
public void poolAdd(halfbuff e)
{
arry.Add(e);
}
public void poolDel(halfbuff e)
{
e.timer.Dispose();
arry.Remove(e);
}
public void poolClear()
{
arry.Clear();
}
public int Count { get { return arry.Count; } }
public halfbuff this [ int index] { get { return (halfbuff)arry[index]; } }
public halfbuff GetClient(IPEndPoint iep)
{
foreach (halfbuff e in arry)
{
if (e.client == iep)
return e;
}
return null ;
}
}
五,自定义的消息,事件,及其他,代码就不贴了,可下载项目文件查看
项目下载:/Files/cxwx/NetServers.rar
错误之处还请批评指正!