在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分-.NET教程,C#语言

来源:百度文库 编辑:神马文学网 时间:2024/04/30 08:38:32
////////////////////////////////////////////////////////////////////////////////////////// 
/* 

标题:在c#中使用异步socket编程实现tcp网络服务的c/s的通讯构架(一)----基础类库部分 

当看到.net中tcplistener和tcpclient的时候,我非常高兴,那就是我想要的通讯模式 
但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们. 

下面提供了一些类,可以很好的完成tcp的c/s通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们 

主要通过事件来现实整个的功能: 
服务器的事件包括: 

服务器满 
新客户端连接 
客户端关闭 
接收到数据 

客户端使用的事件包括: 

已连接服务器 
接收到数据 
连接关闭 

另外为了灵活的处理需求变化,还提供了编码器和报文解析器的实现方法. 
注意:该类库没有经过严格的测试,如出现bug,请发送给我,我会觉得你的整个行为是对我的鼓励和支持. 

*/ 
////////////////////////////////////////////////////////////////////////////////////////// 

///  
/// (c)2003-2005 c2217 studio 
/// 保留所有权利 
/// 
/// 文件名称: tcpcsframework.cs 
/// 文件id: 
/// 编程语言: c# 
/// 文件说明: 提供tcp网络服务的c/s的通讯构架基础类 
/// (使用异步socket编程实现) 
/// 
/// 当前版本: 1.1 
/// 替换版本: 1.0 
/// 
/// 作者: 邓杨均 
/// email: dyj057@gmail.com 
/// 创建日期: 2005-3-9 
/// 最后修改日期: 2005-3-17 
/// 
/// 历史修改记录: 
/// 
/// 时间: 2005-3-14 
/// 修改内容: 
/// 1.创建ibms.net.tcpcsframework命名空间和添加session对象. 
/// 2.修改neteventargs类,以适应新添加对象. 
/// 3.添加了会话退出类型,更适合实际的情况. 
/// 注意: 
/// * 强制退出类型是应用程序直接结束,比如通过任务管理器结束 
/// 程序或者程序异常退出等,没有执行正常的退出方法而产生的. 
/// * 正常的退出类型是应用程序执行正常的退出的方法关键在于 
/// 需要调用socket.shutdown( socketshutdown.both )后才调用 
/// socket.close()方法,而不是直接的调用socket.close()方法, 
/// 如果那样调用将产生强制退出类型. 
/// 
/// 时间: 2005-3-16 
/// 修改内容: 
/// 1.创建tcpcli,coder,datagramresover对象,把抽象和实现部分分离 
/// 2.文件版本修改为1.1,1.0版本仍然保留,更名为: 
/// tcpcsframework_v1.0.cs 
/// 3.在tcpserver中修改自定义的hashtable为系统hashtable类型 
/// 
///
 

using system; 
using system.net.sockets; 
using system.net; 
using system.text; 
using system.diagnostics; 
using system.collections; 

namespace ibms.net.tcpcsframework 


///  
/// 网络通讯事件模型委托 
///
 
public delegate void netevent(object sender, neteventargs e); 

///  
/// 提供tcp连接服务的服务器类 
/// 
/// 版本: 1.1 
/// 替换版本: 1.0 
/// 
/// 特点: 
/// 1.使用hash表保存所有已连接客户端的状态,收到数据时能实现快速查找.每当 
/// 有一个新的客户端连接就会产生一个新的会话(session).该session代表了客 
/// 户端对象. 
/// 2.使用异步的socket事件作为基础,完成网络通讯功能. 
/// 3.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网 
/// 络环境.初步规定该类支持的最大数据报文为640k(即一个数据包的大小不能大于 
/// 640k,否则服务器程序会自动删除报文数据,认为是非法数据),防止因为数据报文 
/// 无限制的增长而倒是服务器崩溃 
/// 4.通讯格式默认使用encoding.default格式这样就可以和以前32位程序的客户端 
/// 通讯.也可以使用u-16和u-8的的通讯方式进行.可以在该datagramresolver类的 
/// 继承类中重载编码和解码函数,自定义加密格式进行通讯.总之确保客户端与服务 
/// 器端使用相同的通讯格式 
/// 5.使用c# native code,将来出于效率的考虑可以将c++代码写成的32位dll来代替 
/// c#核心代码, 但这样做缺乏可移植性,而且是unsafe代码(该类的c++代码也存在) 
/// 6.可以限制服务器的最大登陆客户端数目 
/// 7.比使用tcplistener提供更加精细的控制和更加强大异步数据传输的功能,可作为 
/// tcplistener的替代类 
/// 8.使用异步通讯模式,完全不用担心通讯阻塞和线程问题,无须考虑通讯的细节 
/// 
/// 注意: 
/// 1.部分的代码由rational xde生成,可能与编码规范不符 
/// 
/// 原理: 
/// 
/// 
/// 使用用法: 
/// 
/// 例子: 
/// 
///
 
public class tcpsvr 

#region 定义字段 

///  
/// 默认的服务器最大连接客户端端数据 
///
 
public const int defaultmaxclient=100; 

///  
/// 接收数据缓冲区大小64k 
///
 
public const int defaultbuffersize = 64*1024; 

///  
/// 最大数据报文大小 
///
 
public const int maxdatagramsize = 640*1024; 

///  
/// 报文解析器 
///
 
private datagramresolver _resolver; 

///  
/// 通讯格式编码解码器 
///
 
private coder _coder; 

///  
/// 服务器程序使用的端口 
///
 
private ushort _port; 

///  
/// 服务器程序允许的最大客户端连接数 
///
 
private ushort _maxclient; 

///  
/// 服务器的运行状态 
///
 
private bool _isrun; 

///  
/// 接收数据缓冲区 
///
 
private byte[] _recvdatabuffer; 

///  
/// 服务器使用的异步socket类, 
///
 
private socket _svrsock; 

///  
/// 保存所有客户端会话的哈希表 
///
 
private hashtable _sessiontable; 

///  
/// 当前的连接的客户端数 
///
 
private ushort _clientcount; 

#endregion 

#region 事件定义 

///  
/// 客户端建立连接事件 
///
 
public event netevent clientconn; 

///  
/// 客户端关闭事件 
///
 
public event netevent clientclose; 

///  
/// 服务器已经满事件 
///
 
public event netevent serverfull; 

///  
/// 服务器接收到数据事件 
///
 
public event netevent recvdata; 

#endregion 

#region 构造函数 

///  
/// 构造函数 
///
 
/// 服务器端监听的端口号 
/// 服务器能容纳客户端的最大能力 
/// 通讯的编码方式 
public tcpsvr( ushort port,ushort maxclient, coder coder) 

_port = port; 
_maxclient = maxclient; 
_coder = coder; 



///  
/// 构造函数(默认使用default编码方式) 
///
 
/// 服务器端监听的端口号 
/// 服务器能容纳客户端的最大能力 
public tcpsvr( ushort port,ushort maxclient) 

_port = port; 
_maxclient = maxclient; 
_coder = new coder(coder.encodingmothord.default); 



//  
/// 构造函数(默认使用default编码方式和defaultmaxclient(100)个客户端的容量) 
///
 
/// 服务器端监听的端口号 
public tcpsvr( ushort port):this( port, defaultmaxclient) 



#endregion 

#region 属性 

///  
/// 服务器的socket对象 
///
 
public socket serversocket 

get 

return _svrsock; 



///  
/// 数据报文分析器 
///
 
public datagramresolver resovlver 

get 

return _resolver; 

set 

_resolver = value; 



///  
/// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改 
///
 
public hashtable sessiontable 

get 

return _sessiontable; 



///  
/// 服务器可以容纳客户端的最大能力 
///
 
public int capacity 

get 

return _maxclient; 



///  
/// 当前的客户端连接数 
///
 
public int sessioncount 

get 

return _clientcount; 



///  
/// 服务器运行状态 
///
 
public bool isrun 

get 

return _isrun; 




#endregion 

#region 公有方法 

///  
/// 启动服务器程序,开始监听客户端请求 
///
 
public virtual void start() 

if( _isrun ) 

throw (new applicationexception("tcpsvr已经在运行.")); 


_sessiontable = new hashtable(53); 

_recvdatabuffer = new byte[defaultbuffersize]; 

//初始化socket 
_svrsock = new socket( addressfamily.internetwork, 
sockettype.stream, protocoltype.tcp ); 

//绑定端口 
ipendpoint iep = new ipendpoint( ipaddress.any, _port); 
_svrsock.bind(iep); 

//开始监听 
_svrsock.listen(5); 

//设置异步方法接受客户端连接 
_svrsock.beginaccept(new asynccallback( acceptconn ), _svrsock); 

_isrun = true; 



///  
/// 停止服务器程序,所有与客户端的连接将关闭 
///
 
public virtual void stop() 

if( !_isrun ) 

throw (new applicationexception("tcpsvr已经停止")); 


//这个条件语句,一定要在关闭所有客户端以前调用 
//否则在endconn会出现错误 
_isrun = false; 

//关闭数据连接,负责客户端会认为是强制关闭连接 
if( _svrsock.connected ) 

_svrsock.shutdown( socketshutdown.both ); 


closeallclient(); 

//清理资源 
_svrsock.close(); 

_sessiontable = null; 




///  
/// 关闭所有的客户端会话,与所有的客户端连接会断开 
///
 
public virtual void closeallclient() 

foreach(session client in _sessiontable.values) 

client.close(); 


_sessiontable.clear(); 



///  
/// 关闭一个与客户端之间的会话 
///
 
/// 需要关闭的客户端会话对象 
public virtual void closesession(session closeclient) 

debug.assert( closeclient !=null); 

if( closeclient !=null ) 


closeclient.datagram =null; 

_sessiontable.remove(closeclient.id); 

_clientcount--; 

//客户端强制关闭链接 
if( clientclose != null ) 

clientclose(this, new neteventargs( closeclient )); 


closeclient.close(); 




///  
/// 发送数据 
///
 
/// 接收数据的客户端会话 
/// 数据报文 
public virtual void send( session recvdataclient, string datagram ) 

//获得数据编码 
byte [] data = _coder.getencodingbytes(datagram); 

recvdataclient.clientsocket.beginsend( data, 0, data.length, socketflags.none, 
new asynccallback( senddataend ), recvdataclient.clientsocket ); 



#endregion 

#region 受保护方法 
///  
/// 关闭一个客户端socket,首先需要关闭session 
///
 
/// 目标socket对象 
/// 客户端退出的类型 
protected virtual void closeclient( socket client, session.exittype exittype) 

debug.assert ( client !=null); 

//查找该客户端是否存在,如果不存在,抛出异常 
session closeclient = findsession(client); 

closeclient.typeofexit = exittype; 

if(closeclient!=null) 

closesession(closeclient); 

else 

throw( new applicationexception("需要关闭的socket对象不存在")); 




///  
/// 客户端连接处理函数 
///
 
/// 欲建立服务器连接的socket对象 
protected virtual void acceptconn(iasyncresult iar) 

//如果服务器停止了服务,就不能再接收新的客户端 
if( !_isrun) 

return; 


//接受一个客户端的连接请求 
socket oldserver = ( socket ) iar.asyncstate; 

socket client = oldserver.endaccept(iar); 

//检查是否达到最大的允许的客户端数目 
if( _clientcount == _maxclient ) 

//服务器已满,发出通知 
if( serverfull != null ) 

serverfull(this, new neteventargs( new session(client))); 



else 


session newsession = new session( client ); 

_sessiontable.add(newsession.id, newsession); 

//客户端引用计数+1 
_clientcount ++; 

//开始接受来自该客户端的数据 
client.beginreceive( _recvdatabuffer,0 , _recvdatabuffer.length, socketflags.none, 
new asynccallback(receivedata), client); 

//新的客户段连接,发出通知 
if( clientconn != null ) 

clientconn(this, new neteventargs(newsession ) ); 



//继续接受客户端 
_svrsock.beginaccept(new asynccallback( acceptconn ), _svrsock); 



///  
/// 通过socket对象查找session对象 
///
 
///  
/// 找到的session对象,如果为null,说明并不存在该回话 
private session findsession( socket client ) 

sessionid id = new sessionid((int)client.handle); 

return (session)_sessiontable[id]; 



///  
/// 接受数据完成处理函数,异步的特性就体现在这个函数中, 
/// 收到数据后,会自动解析为字符串报文 
///
 
/// 目标客户端socket 
protected virtual void receivedata(iasyncresult iar) 

socket client = (socket)iar.asyncstate; 

try 

//如果两次开始了异步的接收,所以当客户端退出的时候 
//会两次执行endreceive 

int recv = client.endreceive(iar); 

if( recv == 0 ) 

//正常的关闭 
closeclient(client, session.exittype.normalexit); 
return; 


string receiveddata = _coder.getencodingstring( _recvdatabuffer, recv ); 

//发布收到数据的事件 
if(recvdata!=null) 

session senddatasession= findsession(client); 

debug.assert( senddatasession!=null ); 

//如果定义了报文的尾标记,需要处理报文的多种情况 
if(_resolver != null) 

if( senddatasession.datagram !=null && 
senddatasession.datagram.length !=0) 

//加上最后一次通讯剩余的报文片断 
receiveddata= senddatasession.datagram + receiveddata ; 


string [] recvdatagrams = _resolver.resolve(ref receiveddata); 


foreach(string newdatagram in recvdatagrams) 

//深拷贝,为了保持datagram的对立性 
icloneable copysession = (icloneable)senddatasession; 

session clientsession = (session)copysession.clone(); 

clientsession.datagram = newdatagram; 
//发布一个报文消息 
recvdata(this,new neteventargs( clientsession )); 


//剩余的代码片断,下次接收的时候使用 
senddatasession.datagram = receiveddata; 

if( senddatasession.datagram.length > maxdatagramsize ) 

senddatasession.datagram = null; 



//没有定义报文的尾标记,直接交给消息订阅者使用 
else 

icloneable copysession = (icloneable)senddatasession; 

session clientsession = (session)copysession.clone(); 

clientsession.datagram = receiveddata; 

recvdata(this,new neteventargs( clientsession )); 


}//end of if(recvdata!=null) 

//继续接收来自来客户端的数据 
client.beginreceive( _recvdatabuffer, 0, _recvdatabuffer.length , socketflags.none, 
new asynccallback( receivedata ), client); 


catch(socketexception ex) 

//客户端退出 
if( 10054 == ex.errorcode ) 

//客户端强制关闭 
closeclient(client, session.exittype.exceptionexit); 



catch(objectdisposedexception ex) 

//这里的实现不够优雅 
//当调用closesession()时,会结束数据接收,但是数据接收 
//处理中会调用int recv = client.endreceive(iar); 
//就访问了closesession()已经处置的对象 
//我想这样的实现方法也是无伤大雅的. 
if(ex!=null) 

ex=null; 
//donothing; 






///  
/// 发送数据完成处理函数 
///
 
/// 目标客户端socket 
protected virtual void senddataend(iasyncresult iar) 

socket client = (socket)iar.asyncstate; 

int sent = client.endsend(iar); 


#endregion 




///  
/// 提供tcp网络连接服务的客户端类 
/// 
/// 版本: 1.0 
/// 替换版本: 
/// 
/// 特征: 
/// 原理: 
/// 1.使用异步socket通讯与服务器按照一定的通讯格式通讯,请注意与服务器的通 
/// 讯格式一定要一致,否则可能造成服务器程序崩溃,整个问题没有克服,怎么从byte[] 
/// 判断它的编码格式 
/// 2.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网 
/// 络环境. 
/// 用法: 
/// 注意: 
///
 
public class tcpcli 

#region 字段 

///  
/// 客户端与服务器之间的会话类 
///
 
private session _session; 

///  
/// 客户端是否已经连接服务器 
///
 
private bool _isconnected = false; 

///  
/// 接收数据缓冲区大小64k 
///
 
public const int defaultbuffersize = 64*1024; 

///  
/// 报文解析器 
///
 
private datagramresolver _resolver; 

///  
/// 通讯格式编码解码器 
///
 
private coder _coder; 

///  
/// 接收数据缓冲区 
///
 
private byte[] _recvdatabuffer = new byte[defaultbuffersize]; 

#endregion 

#region 事件定义 

//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅 

///  
/// 已经连接服务器事件 
///
 
public event netevent connectedserver; 

///  
/// 接收到数据报文事件 
///
 
public event netevent receiveddatagram; 

///  
/// 连接断开事件 
///
 
public event netevent disconnectedserver; 
#endregion 

#region 属性 

///  
/// 返回客户端与服务器之间的会话对象 
///
 
public session clientsession 

get 

return _session; 



///  
/// 返回客户端与服务器之间的连接状态 
///
 
public bool isconnected 

get 

return _isconnected; 



///  
/// 数据报文分析器 
///
 
public datagramresolver resovlver 

get 

return _resolver; 

set 

_resolver = value; 



///  
/// 编码解码器 
///
 
public coder servercoder 

get 

return _coder; 



#endregion 

#region 公有方法 

///  
/// 默认构造函数,使用默认的编码格式 
///
 
public tcpcli() 

_coder = new coder( coder.encodingmothord.default ); 


///  
/// 构造函数,使用一个特定的编码器来初始化 
///
 
/// 报文编码器 
public tcpcli( coder coder ) 

_coder = coder; 


///  
/// 连接服务器 
///
 
/// 服务器ip地址 
/// 服务器端口 
public virtual void connect( string ip, int port) 

if(isconnected) 

//重新连接 
debug.assert( _session !=null); 

close(); 


socket newsock= new socket(addressfamily.internetwork, 
sockettype.stream, protocoltype.tcp); 

ipendpoint iep = new ipendpoint( ipaddress.parse(ip), port); 
newsock.beginconnect(iep, new asynccallback(connected), newsock); 



///  
/// 发送数据报文 
///
 
///  
public virtual void send( string datagram) 

if(datagram.length ==0 ) 

return; 


if( !_isconnected ) 

throw (new applicationexception("没有连接服务器,不能发送数据") ); 


//获得报文的编码字节 
byte [] data = _coder.getencodingbytes(datagram); 

_session.clientsocket.beginsend( data, 0, data.length, socketflags.none, 
new asynccallback( senddataend ), _session.clientsocket); 


///  
/// 关闭连接 
///
 
public virtual void close() 

if(!_isconnected) 

return; 


_session.close(); 

_session = null; 

_isconnected = false; 


#endregion 

#region 受保护方法 

///  
/// 数据发送完成处理函数 
///
 
///  
protected virtual void senddataend(iasyncresult iar) 

socket remote = (socket)iar.asyncstate; 
int sent = remote.endsend(iar); 
debug.assert(sent !=0); 



///  
/// 建立tcp连接后处理过程 
///
 
/// 异步socket 
protected virtual void connected(iasyncresult iar) 

socket socket = (socket)iar.asyncstate; 

socket.endconnect(iar); 

//创建新的会话 
_session = new session(socket); 

_isconnected = true; 

//触发连接建立事件 
if(connectedserver != null) 

connectedserver(this, new neteventargs(_session)); 


//建立连接后应该立即接收数据 
_session.clientsocket.beginreceive(_recvdatabuffer, 0, 
defaultbuffersize, socketflags.none, 
new asynccallback(recvdata), socket); 


///  
/// 数据接收处理函数 
///
 
/// 异步socket 
protected virtual void recvdata(iasyncresult iar) 

socket remote = (socket)iar.asyncstate; 

try 

int recv = remote.endreceive(iar); 

//正常的退出 
if(recv ==0 ) 

_session.typeofexit = session.exittype.normalexit; 

if(disconnectedserver!=null) 

disconnectedserver(this, new neteventargs(_session)); 


return; 


string receiveddata = _coder.getencodingstring( _recvdatabuffer,recv ); 

//通过事件发布收到的报文 
if(receiveddatagram != null) 

//通过报文解析器分析出报文 
//如果定义了报文的尾标记,需要处理报文的多种情况 
if(_resolver != null) 

if( _session.datagram !=null && 
_session.datagram.length !=0) 

//加上最后一次通讯剩余的报文片断 
receiveddata= _session.datagram + receiveddata ; 


string [] recvdatagrams = _resolver.resolve(ref receiveddata); 


foreach(string newdatagram in recvdatagrams) 

//need deep copy.因为需要保证多个不同报文独立存在 
icloneable copysession = (icloneable)_session; 

session clientsession = (session)copysession.clone(); 

clientsession.datagram = newdatagram; 

//发布一个报文消息 
receiveddatagram(this,new neteventargs( clientsession )); 


//剩余的代码片断,下次接收的时候使用 
_session.datagram = receiveddata; 

//没有定义报文的尾标记,直接交给消息订阅者使用 
else 

icloneable copysession = (icloneable)_session; 

session clientsession = (session)copysession.clone(); 

clientsession.datagram = receiveddata; 

receiveddatagram( this, new neteventargs( clientsession )); 




}//end of if(receiveddatagram != null) 

//继续接收数据 
_session.clientsocket.beginreceive(_recvdatabuffer, 0, defaultbuffersize, socketflags.none, 
new asynccallback(recvdata), _session.clientsocket); 

catch(socketexception ex) 

//客户端退出 
if( 10054 == ex.errorcode ) 

//服务器强制的关闭连接,强制退出 
_session.typeofexit = session.exittype.exceptionexit; 

if(disconnectedserver!=null) 

disconnectedserver(this, new neteventargs(_session)); 


else 

throw( ex ); 


catch(objectdisposedexception ex) 

//这里的实现不够优雅 
//当调用closesession()时,会结束数据接收,但是数据接收 
//处理中会调用int recv = client.endreceive(iar); 
//就访问了closesession()已经处置的对象 
//我想这样的实现方法也是无伤大雅的. 
if(ex!=null) 

ex =null; 
//donothing; 





#endregion 




///  
/// 通讯编码格式提供者,为通讯服务提供编码和解码服务 
/// 你可以在继承类中定制自己的编码方式如:数据加密传输等 
///
 
public class coder 

///  
/// 编码方式 
///
 
private encodingmothord _encodingmothord; 

protected coder() 




public coder(encodingmothord encodingmothord) 

_encodingmothord = encodingmothord; 


public enum encodingmothord 

default =0, 
unicode, 
utf8, 
ascii, 


///  
/// 通讯数据解码 
///
 
/// 需要解码的数据 
/// 编码后的数据 
public virtual string getencodingstring( byte [] databytes,int size) 

switch( _encodingmothord ) 

case encodingmothord.default: 

return encoding.default.getstring(databytes,0,size); 

case encodingmothord.unicode: 

return encoding.unicode.getstring(databytes,0,size); 

case encodingmothord.utf8: 

return encoding.utf8.getstring(databytes,0,size); 

case encodingmothord.ascii: 

return encoding.ascii.getstring(databytes,0,size); 

default: 

throw( new exception("未定义的编码格式")); 





///  
/// 数据编码 
///
 
/// 需要编码的报文 
/// 编码后的数据 
public virtual byte[] getencodingbytes(string datagram) 

switch( _encodingmothord) 

case encodingmothord.default: 

return encoding.default.getbytes(datagram); 

case encodingmothord.unicode: 

return encoding.unicode.getbytes(datagram); 

case encodingmothord.utf8: 

return encoding.utf8.getbytes(datagram); 

case encodingmothord.ascii: 

return encoding.ascii.getbytes(datagram); 

default: 

throw( new exception("未定义的编码格式")); 







///  
/// 数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文. 
/// 继承该类可以实现自己的报文解析方法. 
/// 通常的报文识别方法包括:固定长度,长度标记,标记符等方法 
/// 本类的现实的是标记符的方法,你可以在继承类中实现其他的方法 
///
 
public class datagramresolver 

///  
/// 报文结束标记 
///
 
private string endtag; 

///  
/// 返回结束标记 
///
 
string endtag 

get 

return endtag; 



///  
/// 受保护的默认构造函数,提供给继承类使用 
///
 
protected datagramresolver() 




///  
/// 构造函数 
///
 
/// 报文结束标记 
public datagramresolver(string endtag) 

if(endtag == null) 

throw (new argumentnullexception("结束标记不能为null")); 


if(endtag == "") 

throw (new argumentexception("结束标记符号不能为空字符串")); 


this.endtag = endtag; 


///  
/// 解析报文 
///
 
/// 原始数据,返回未使用的报文片断, 
/// 该片断会保存在session的datagram对象中 
/// 报文数组,原始数据可能包含多个报文 
public virtual string [] resolve(ref string rawdatagram) 

arraylist datagrams = new arraylist(); 

//末尾标记位置索引 
int tagindex =-1; 

while(true) 

tagindex = rawdatagram.indexof(endtag,tagindex+1); 

if( tagindex == -1 ) 

break; 

else 

//按照末尾标记把字符串分为左右两个部分 
string newdatagram = rawdatagram.substring( 
0, tagindex+endtag.length); 

datagrams.add(newdatagram); 

if(tagindex+endtag.length >= rawdatagram.length) 

rawdatagram=""; 

break; 


rawdatagram = rawdatagram.substring(tagindex+endtag.length, 
rawdatagram.length - newdatagram.length); 

//从开始位置开始查找 
tagindex=0; 



string [] results= new string[datagrams.count]; 

datagrams.copyto(results); 

return results; 





///  
/// 客户端与服务器之间的会话类 
/// 
/// 版本: 1.1 
/// 替换版本: 1.0 
/// 
/// 说明: 
/// 会话类包含远程通讯端的状态,这些状态包括socket,报文内容, 
/// 客户端退出的类型(正常关闭,强制退出两种类型) 
///
 
public class session:icloneable 

#region 字段 

///  
/// 会话id 
///
 
private sessionid _id; 

///  
/// 客户端发送到服务器的报文 
/// 注意:在有些情况下报文可能只是报文的片断而不完整 
///
 
private string _datagram; 

///  
/// 客户端的socket 
///
 
private socket _clisock; 

///  
/// 客户端的退出类型 
///
 
private exittype _exittype; 

///  
/// 退出类型枚举 
///
 
public enum exittype 

normalexit , 
exceptionexit 
}; 

#endregion 

#region 属性 

///  
/// 返回会话的id 
///
 
public sessionid id 

get 

return _id; 



///  
/// 存取会话的报文 
///
 
public string datagram 

get 

return _datagram; 

set 

_datagram = value; 



///  
/// 获得与客户端会话关联的socket对象 
///
 
public socket clientsocket 

get 

return _clisock; 



///  
/// 存取客户端的退出方式 
///
 
public exittype typeofexit 

get 

return _exittype; 


set 

_exittype = value; 



#endregion 

#region 方法 

///  
/// 使用socket对象的handle值作为hashcode,它具有良好的线性特征. 
///
 
///  
public override int gethashcode() 

return (int)_clisock.handle; 


///  
/// 返回两个session是否代表同一个客户端 
///
 
///  
///  
public override bool equals(object obj) 

session rightobj = (session)obj; 

return (int)_clisock.handle == (int)rightobj.clientsocket.handle; 



///  
/// 重载tostring()方法,返回session对象的特征 
///
 
///  
public override string tostring() 

string result = string.format("session:{0},ip:{1}", 
_id,_clisock.remoteendpoint.tostring()); 

//result.c 
return result; 


///  
/// 构造函数 
///
 
/// 会话使用的socket连接 
public session( socket clisock) 

debug.assert( clisock !=null ); 

_clisock = clisock; 

_id = new sessionid( (int)clisock.handle); 


///  
/// 关闭会话 
///
 
public void close() 

debug.assert( _clisock !=null ); 

//关闭数据的接受和发送 
_clisock.shutdown( socketshutdown.both ); 

//清理资源 
_clisock.close(); 


#endregion 

#region icloneable 成员 

object system.icloneable.clone() 

session newsession = new session(_clisock); 
newsession.datagram = _datagram; 
newsession.typeofexit = _exittype; 

return newsession; 


#endregion 



///  
/// 唯一的标志一个session,辅助session对象在hash表中完成特定功能 
///
 
public class sessionid 

///  
/// 与session对象的socket对象的handle值相同,必须用这个值来初始化它 
///
 
private int _id; 

///  
/// 返回id值 
///
 
public int id 

get 

return _id; 



///  
/// 构造函数 
///
 
/// socket的handle值 
public sessionid(int id) 

_id = id; 


///  
/// 重载.为了符合hashtable键值特征 
///
 
///  
///  
public override bool equals(object obj) 

if(obj != null ) 

sessionid right = (sessionid) obj; 

return _id == right._id; 

else if(this == null) 

return true; 

else 

return false; 




///  
/// 重载.为了符合hashtable键值特征 
///
 
///  
public override int gethashcode() 

return _id; 


///  
/// 重载,为了方便显示输出 
///
 
///  
public override string tostring() 

return _id.tostring (); 





///  
/// 服务器程序的事件参数,包含了激发该事件的会话对象 
///
 
public class neteventargs:eventargs 


#region 字段 

///  
/// 客户端与服务器之间的会话 
///
 
private session _client; 

#endregion 

#region 构造函数 
///  
/// 构造函数 
///
 
/// 客户端会话 
public neteventargs(session client) 

if( null == client) 

throw(new argumentnullexception()); 


_client = client; 

#endregion 

#region 属性 

///  
/// 获得激发该事件的会话对象 
///
 
public session client 

get 

return _client; 




#endregion