首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 操作系统-> Linux教程
在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分-.NET教程,C#语言
作者:网友供稿 点击:120
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
//////////////////////////////////////////////////////////////////////////////////////////
/*

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

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

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

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

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

客户端使用的事件包括:

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

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

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

/// <summary>
/// (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类型
///
/// </summary>

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

namespace ibms.net.tcpcsframework
{

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

/// <summary>
/// 提供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生成,可能与编码规范不符
///
/// 原理:
///
///
/// 使用用法:
///
/// 例子:
///
/// </summary>
public class tcpsvr
{
#region 定义字段

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

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

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

/// <summary>
/// 报文解析器
/// </summary>
private datagramresolver _resolver;

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

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

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

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

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

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

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

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

#endregion

#region 事件定义

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

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

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

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

#endregion

#region 构造函数

/// <summary>
/// 构造函数
/// </summary>
/// <param name="port">服务器端监听的端口号</param>
/// <param name="maxclient">服务器能容纳客户端的最大能力</param>
/// <param name="encodingmothord">通讯的编码方式</param>
public tcpsvr( ushort port,ushort maxclient, coder coder)
{
_port = port;
_maxclient = maxclient;
_coder = coder;
}


/// <summary>
/// 构造函数(默认使用default编码方式)
/// </summary>
/// <param name="port">服务器端监听的端口号</param>
/// <param name="maxclient">服务器能容纳客户端的最大能力</param>
public tcpsvr( ushort port,ushort maxclient)
{
_port = port;
_maxclient = maxclient;
_coder = new coder(coder.encodingmothord.default);
}


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

#endregion

#region 属性

/// <summary>
/// 服务器的socket对象
/// </summary>
public socket serversocket
{
get
{
return _svrsock;
}
}

/// <summary>
/// 数据报文分析器
/// </summary>
public datagramresolver resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

/// <summary>
/// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改
/// </summary>
public hashtable sessiontable
{
get
{
return _sessiontable;
}
}

/// <summary>
/// 服务器可以容纳客户端的最大能力
/// </summary>
public int capacity
{
get
{
return _maxclient;
}
}

/// <summary>
/// 当前的客户端连接数
/// </summary>
public int sessioncount
{
get
{
return _clientcount;
}
}

/// <summary>
/// 服务器运行状态
/// </summary>
public bool isrun
{
get
{
return _isrun;
}

}

#endregion

#region 公有方法

/// <summary>
/// 启动服务器程序,开始监听客户端请求
/// </summary>
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;

}

/// <summary>
/// 停止服务器程序,所有与客户端的连接将关闭
/// </summary>
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;

}


/// <summary>
/// 关闭所有的客户端会话,与所有的客户端连接会断开
/// </summary>
public virtual void closeallclient()
{
foreach(session client in _sessiontable.values)
{
client.close();
}

_sessiontable.clear();
}


/// <summary>
/// 关闭一个与客户端之间的会话
/// </summary>
/// <param name="closeclient">需要关闭的客户端会话对象</param>
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();
}
}


/// <summary>
/// 发送数据
/// </summary>
/// <param name="recvdataclient">接收数据的客户端会话</param>
/// <param name="datagram">数据报文</param>
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 受保护方法
/// <summary>
/// 关闭一个客户端socket,首先需要关闭session
/// </summary>
/// <param name="client">目标socket对象</param>
/// <param name="exittype">客户端退出的类型</param>
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对象不存在"));
}
}


/// <summary>
/// 客户端连接处理函数
/// </summary>
/// <param name="iar">欲建立服务器连接的socket对象</param>
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);
}


/// <summary>
/// 通过socket对象查找session对象
/// </summary>
/// <param name="client"></param>
/// <returns>找到的session对象,如果为null,说明并不存在该回话</returns>
private session findsession( socket client )
{
sessionid id = new sessionid((int)client.handle);

return (session)_sessiontable[id];
}


/// <summary>
/// 接受数据完成处理函数,异步的特性就体现在这个函数中,
/// 收到数据后,会自动解析为字符串报文
/// </summary>
/// <param name="iar">目标客户端socket</param>
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;
}
}

}


/// <summary>
/// 发送数据完成处理函数
/// </summary>
/// <param name="iar">目标客户端socket</param>
protected virtual void senddataend(iasyncresult iar)
{
socket client = (socket)iar.asyncstate;

int sent = client.endsend(iar);
}

#endregion

}


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

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

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

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

/// <summary>
/// 报文解析器
/// </summary>
private datagramresolver _resolver;

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

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

#endregion

#region 事件定义

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

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

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

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

#region 属性

/// <summary>
/// 返回客户端与服务器之间的会话对象
/// </summary>
public session clientsession
{
get
{
return _session;
}
}

/// <summary>
/// 返回客户端与服务器之间的连接状态
/// </summary>
public bool isconnected
{
get
{
return _isconnected;
}
}

/// <summary>
/// 数据报文分析器
/// </summary>
public datagramresolver resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

/// <summary>
/// 编码解码器
/// </summary>
public coder servercoder
{
get
{
return _coder;
}
}

#endregion

#region 公有方法

/// <summary>
/// 默认构造函数,使用默认的编码格式
/// </summary>
public tcpcli()
{
_coder = new coder( coder.encodingmothord.default );
}

/// <summary>
/// 构造函数,使用一个特定的编码器来初始化
/// </summary>
/// <param name="_coder">报文编码器</param>
public tcpcli( coder coder )
{
_coder = coder;
}

/// <summary>
/// 连接服务器
/// </summary>
/// <param name="ip">服务器ip地址</param>
/// <param name="port">服务器端口</param>
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);

}

/// <summary>
/// 发送数据报文
/// </summary>
/// <param name="datagram"></param>
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);
}

/// <summary>
/// 关闭连接
/// </summary>
public virtual void close()
{
if(!_isconnected)
{
return;
}

_session.close();

_session = null;

_isconnected = false;
}

#endregion

#region 受保护方法

/// <summary>
/// 数据发送完成处理函数
/// </summary>
/// <param name="iar"></param>
protected virtual void senddataend(iasyncresult iar)
{
socket remote = (socket)iar.asyncstate;
int sent = remote.endsend(iar);
debug.assert(sent !=0);

}

/// <summary>
/// 建立tcp连接后处理过程
/// </summary>
/// <param name="iar">异步socket</param>
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);
}

/// <summary>
/// 数据接收处理函数
/// </summary>
/// <param name="iar">异步socket</param>
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


}

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

protected coder()
{

}

public coder(encodingmothord encodingmothord)
{
_encodingmothord = encodingmothord;
}

public enum encodingmothord
{
default =0,
unicode,
utf8,
ascii,
}

/// <summary>
/// 通讯数据解码
/// </summary>
/// <param name="databytes">需要解码的数据</param>
/// <returns>编码后的数据</returns>
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("未定义的编码格式"));
}
}

}

/// <summary>
/// 数据编码
/// </summary>
/// <param name="datagram">需要编码的报文</param>
/// <returns>编码后的数据</returns>
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("未定义的编码格式"));
}
}
}

}


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

/// <summary>
/// 返回结束标记
/// </summary>
string endtag
{
get
{
return endtag;
}
}

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

}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="endtag">报文结束标记</param>
public datagramresolver(string endtag)
{
if(endtag == null)
{
throw (new argumentnullexception("结束标记不能为null"));
}

if(endtag == "")
{
throw (new argumentexception("结束标记符号不能为空字符串"));
}

this.endtag = endtag;
}

/// <summary>
/// 解析报文
/// </summary>
/// <param name="rawdatagram">原始数据,返回未使用的报文片断,
/// 该片断会保存在session的datagram对象中</param>
/// <returns>报文数组,原始数据可能包含多个报文</returns>
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;
}

}


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

/// <summary>
/// 会话id
/// </summary>
private sessionid _id;

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

/// <summary>
/// 客户端的socket
/// </summary>
private socket _clisock;

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

/// <summary>
/// 退出类型枚举
/// </summary>
public enum exittype
{
normalexit ,
exceptionexit
};

#endregion

#region 属性

/// <summary>
/// 返回会话的id
/// </summary>
public sessionid id
{
get
{
return _id;
}
}

/// <summary>
/// 存取会话的报文
/// </summary>
public string datagram
{
get
{
return _datagram;
}
set
{
_datagram = value;
}
}

/// <summary>
/// 获得与客户端会话关联的socket对象
/// </summary>
public socket clientsocket
{
get
{
return _clisock;
}
}

/// <summary>
/// 存取客户端的退出方式
/// </summary>
public exittype typeofexit
{
get
{
return _exittype;
}

set
{
_exittype = value;
}
}

#endregion

#region 方法

/// <summary>
/// 使用socket对象的handle值作为hashcode,它具有良好的线性特征.
/// </summary>
/// <returns></returns>
public override int gethashcode()
{
return (int)_clisock.handle;
}

/// <summary>
/// 返回两个session是否代表同一个客户端
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool equals(object obj)
{
session rightobj = (session)obj;

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

}

/// <summary>
/// 重载tostring()方法,返回session对象的特征
/// </summary>
/// <returns></returns>
public override string tostring()
{
string result = string.format("session:{0},ip:{1}",
_id,_clisock.remoteendpoint.tostring());

//result.c
return result;
}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="clisock">会话使用的socket连接</param>
public session( socket clisock)
{
debug.assert( clisock !=null );

_clisock = clisock;

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

/// <summary>
/// 关闭会话
/// </summary>
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
}


/// <summary>
/// 唯一的标志一个session,辅助session对象在hash表中完成特定功能
/// </summary>
public class sessionid
{
/// <summary>
/// 与session对象的socket对象的handle值相同,必须用这个值来初始化它
/// </summary>
private int _id;

/// <summary>
/// 返回id值
/// </summary>
public int id
{
get
{
return _id;
}
}

/// <summary>
/// 构造函数
/// </summary>
/// <param name="id">socket的handle值</param>
public sessionid(int id)
{
_id = id;
}

/// <summary>
/// 重载.为了符合hashtable键值特征
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
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;
}

}

/// <summary>
/// 重载.为了符合hashtable键值特征
/// </summary>
/// <returns></returns>
public override int gethashcode()
{
return _id;
}

/// <summary>
/// 重载,为了方便显示输出
/// </summary>
/// <returns></returns>
public override string tostring()
{
return _id.tostring ();
}

}


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

#region 字段

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

#endregion

#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="client">客户端会话</param>
public neteventargs(session client)
{
if( null == client)
{
throw(new argumentnullexception());
}

_client = client;
}
#endregion

#region 属性

/// <summary>
/// 获得激发该事件的会话对象
/// </summary>
public session client
{
get
{
return _client;
}

}

#endregion

}
}




文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·最简单的Struts程序-JSP教程,资料/其它
·利用激光供电的光电电流互感器
·java到structs-JSP教程,Java技巧及代码
·鼠标样式,整体背景图,滚动条的效果(网页效果)-ASP教程,ASP应用
·ASP中正则表达式的应用-ASP教程,正则表达式
·XML应用-利用XML 与XSL-.NET教程,XML应用
·.net 下用javascript调用webservice-.NET教程,Web Service开发
·Asp无组件上传进度条解决方案-ASP教程,组件开发
·破译动网验证码的简单方法-.NET教程,评论及其它
·linux系统文件命令精通指南(上)
最新文章
·mashups+ajax打造全新web开发_ajax教程
·windows vista系统中写字板应用全攻略_windows vista
·adsense单价持续低迷的解读_网赚技巧
·google反作弊小组成员专访_站长访谈
·端午议话:谈网站的特色_站长心得
·浅谈我的十点论坛管理经验_站长心得
·如何针对msn的onpage进行网页优化_站长心得
·用google 搜索框黏住易变的访问者_google推广
·google关键词广告每次点击付费(ppc)术语表_google推广
·确保万无一失的google 关键词广告公式_google推广
相关主题
  • 在c#中使用热键隐含窗口_c#应用
  • 在c#中使用可空类型_c#应用
  • 在C#中使用ADO.Net部件来访问Access数据库-.NET教程,数据库应用
  • 在C#中使用XML指南之读取XML-.NET教程,C#语言
  • 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法-.NET教程,C#语言
  • 西部数码虚拟主机

    友情链接
    CNNIC 西部数码
    万网 自助建站
    虚拟主机 asp空间
    域名注册 域名
    域名申请 主页空间
    论坛空间 网站空间
    国际域名 虚拟空间
    空间租用 DDOS防火墙
    成都主机托管 四川主机托管
    主机租用 服务器租用
    网站目录 自助建站
    虚拟主机 网址大全
    软件下载
    自助链接
    虚拟主机资讯 特价虚拟主机
    版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
    关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
    特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
    发表评论 打印  刷新     关闭