如果在.NET下寫過網絡通訊的同學應該感覺不陌生了,有很多剛入門的同學很多都認為這東西可以大大提高處理效能還有就是使用上很不適應.其實使用之前最好通過MSDN了解一下,該對象緊緊是Begin End模式的一個增強版本,它的主要作用主要是解決之前異步過程時創建不可復用的異步對象而產生的.主要是在高並發下節省大量對象重分配和同步相關問題,從而實現在高並發吞吐下更少的資源損耗(如果你的應用緊緊是密集通訊那性能提供相對來說是可觀一點,但應用還存在其他處理那就不要想着它能有質的改變了).
SocketAsyncEventArgs(SAEA)在.net 2.0 sp1所提供開發人員主要使用它的場景分別是Accept,Send和Receive.在傳統的Begin End模式中一般都通過調用Begin方法然后在回調方法中調用End來處理,其實SAEA原理差不多,只是由原來的指定的回調過程變成了完成事件,更重要的一個改變就是SAEA是可以復用的.下面詳解一下SAEA的以上幾種用法.
主要屬性和事件
在使用SocketAsyncEventArgs進行TCP或UDP通訊的時候最常用的幾個成員分別是:Buffer,BufferList,BytesTransferred,SocketError,UserToken,BytesTransferred屬性,SetBuffer方法和Completed事件。
SocketAsyncEventArgs接收和發送數據都需要設置buffer,一般用SetBuffer方法或設置BufferList。通過Completed來查看完成情況,而在Completed通過SocketError和BytesTransferred結合來判斷當前操作是否成功能,如在receive狀態當BytesTransferred為零的時候,說明對方已經把連接斷開了。
由於SocketAsyncEventArgs是異步操作,在很多情況需要的一些其他信息傳遞到Completed事件中,這個時候可以通過UserToken屬性來解決異步信息傳遞的問題。
使用注意:SocketAsyncEventArgs同一時間只能進行一個操作,通過Completed來確認當前操作是否完成,如果同步完成是不會觸該事件需要自己手動調用處理。
Accept
此方法是管連接接入,一般在寫TCP應用的的時候需要用到(UDP下則不需要).
if(!Socket.AcceptAsync(saea))
{
Accept_Process(Socket,saea);
}
在使用的時候一般是采用以上方法,如果是同步完成就手動調用完成處理過程,否則會觸發完成事件.所以在構建saea的時候必須綁定Competed事件,也可以通過承繼saea類派生OnCompleted方法處理也行.
private void Accept_Process(object sender,SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
//成功
}
else
{
ReleaseSocket(e.AcceptSocket);
}
Accept();
}
可以通過判斷SocketError來確定連接接入是否成功,如果方法還要處理一些有可能存在異常的邏輯,那這里最好寫上try catch.防止在線程回調過程出現未知異常導致服務關閉的嚴重問題發生. 在使用SAEA進行Accept的時候其實也可以設置buffer,這個的主要作用是當連接接入后必須收到對方請求的數據才會觸發完成事件.
Send
在Socket針對SAEA的Send操作用兩個分別是SendAsync和SendToAsync,前者是基於有連接狀態常用於TCP,而后者則用於無連接狀態的一般發送UDP的時候使用.
if (!mSocket.SendAsync(mSendSAEA))
{
OnSend(this, mSendSAEA);
}
發送方法返回一個布爾值,如果為false則為同步完成需要手動調用完整事件對應的方法;數據的發送情況同樣通過SAEA的完成事件來處理.
private void OnSend(object sender, System.Net.Sockets.SocketAsyncEventArgs e)
{
if (e.SocketError == System.Net.Sockets.SocketError.Success )
{
//成功
}
else
{
// Dispose();
}
}
在完成事件通過SockeError來判斷發送是否成功,如果不成功那基本上就說明當前連接出現異常,可以對當前TCP連接斷開.實際還有幾種狀態需要考慮如 IOPending,不過在發關數據的時候是單一投遞則不會存在這情況 .在傳統的begin,end模式中需要判斷發送字節數量的情況,而在SAEA中則不需要.
Receive
接收和發送的原理基本一致.
if (!mSocket.ReceiveAsync(mReceiveSAEA))
{
OnReceive(this, mReceiveSAEA);
}
private void OnReceive(object sender, System.Net.Sockets.SocketAsyncEventArgs e)
{
if (e.SocketError == System.Net.Sockets.SocketError.Success && e.BytesTransferred > 0)
{
//數據接收成功
BeginReceive();
}
else
{
Dispose();
}
}
注意
在Win下SocketAsyncEventArgs綁定Buffer或BufferList都是需要注意,盡可能綁定同一份byte[]的引用,如果不是會導致內部緩存區重新綁定影響性能,Mono下從源代碼來看是不會有這情況發生.在一些交流中經常碰到如何用SAEA實現雙功,其實網絡通訊中只有輸入和輸出,不存在只是單向的概念,對於SAEA同一時間只能允許一個操作,所以在做應用的時候需要同時接收和發送則可以針對發和收都單獨定義個SAEA即可.如果為了更好地利用做個SAEA Pool則相對來說比較好.
