国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

在C#中使用異步Socket編程實現TCP網絡服務的C/S的通訊構架(一)----基礎類庫部分

2024-07-21 02:18:08
字體:
來源:轉載
供稿:網友

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

標題:在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: [email protected]
/// 創建日期: 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類的
/// 繼承類中重載編碼和解碼函數,自定義加密格式進行通訊.總之確??蛻舳伺c服務
/// 器端使用相同的通訊格式
/// 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

}
}



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 株洲市| 基隆市| 将乐县| 林口县| 甘德县| 青州市| 昔阳县| 合作市| 义马市| 盐城市| 丰原市| 房产| 姜堰市| 和林格尔县| 全南县| 翁源县| 开原市| 乐清市| 精河县| 吴旗县| 毕节市| 五大连池市| 芦溪县| 贵阳市| 上高县| 泸定县| 深水埗区| 临邑县| 田东县| 延安市| 墨脱县| 湘西| 阆中市| 新闻| 太白县| 康定县| 固阳县| 广州市| 岳阳市| 扶风县| 西充县|