欢迎光临
我们一直在努力

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

建站超值云服务器,限时71元/月

//////////////////////////////////////////////////////////////////////////////////////////

/*

标题:在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

}

}

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)—-基础类库部分-.NET教程,C#语言
分享到: 更多 (0)