在之前的文章中,我們講了如何在.NET 2.0下面開發Socket項目。其中的異步Socket讓我們得以很輕 松的在.NET中開發高性能服務端應用。
但是,在實際應用中我們還是發現了一些問題的存在,如:我們在每一次操作的過程中都要創建一個 IAsyncResult上下文對象,如果數據通訊很頻繁的話,會導致大量的IAsyncResult對象被創建,大大的增 加了垃圾回收器的工作量,從而降低了整個應用的效率。
在.NET 3.5中,這個麻煩已經被解決了,在3.5 版本中,Socket定義了一些新的方法。這些方法不要 求每一次操作都創建一個新的上下文對象。
如,在2.0中我們采用下面的方式在Socket上啟動一次接收操作。
void ReceiveCallBack(IAsyncResult ar){}
IAsyncResult result=socket.BeginReceive(info.Buffer, 0, info.Buffer.Length,
SocketFlags.None, ReceiveCallBack, info);//在這裡有一個IAsyncResult對象被創建。
在3.5中我們可以用新的方法完成一次接收操作。
void OnReceiveCompleted(object sender,SocketAsyncEventArgs e){}
SocketAsyncEventArgs receive=new SocketAnyncEventArgs();
receive.Completed+= OnReceiveCompleted;
receive.SetBuffer(buffer,0,buffer.Length);
socket.ReceiveAsync(receive);
在這裡我們可以看出3.5 和 2.0 的一個明顯區別,那就是不是使用IAsyncResult而是用 SocketAsyncEventArgs作為上下文對象。應用程序創建並管理(並且可以重復使用) SocketAsyncEventArgs 對象。套接字操作的所有參數都由 SocketAsyncEventArgs 對象的屬性和方法指 定。完成狀態也由 SocketAsyncEventArgs 對象的屬性提供。最後,需要使用事件處理程序回調完成方法 。
讓我們來看看代碼:
首先我們創建一個用戶類,用來存儲和客戶端有關的數據:
public class UserObject
{
/**//// <summary>
/// 接收數據的緩沖區
/// </summary>
public byte[] ReceiveBuffer { get; private set; }
/**//// <summary>
/// 發送數據的緩沖區
/// </summary>
public byte[] SendBuffer { get; private set; }
/**//// <summary>
/// 客戶端Socket對象
/// </summary>
public Socket Socket { get; private set; }
/**//// <summary>
/// 發送數據上下文對象
/// </summary>
public SocketAsyncEventArgs SendEventArgs { get; private set; }
/**//// <summary>
/// 接收數據上下文對象
/// </summary>
public SocketAsyncEventArgs ReceiveEventArgs { get; private set; }
public UserObject(Socket socket)
{
ReceiveBuffer = new byte[1024];//定義接收緩沖區
SendBuffer = new byte[1024];//定義發送緩沖區
this.Socket = socket;
SendEventArgs = new SocketAsyncEventArgs();
SendEventArgs.UserToken = this;
ReceiveEventArgs = new SocketAsyncEventArgs();
ReceiveEventArgs.UserToken = this;
ReceiveEventArgs.SetBuffer(ReceiveBuffer, 0, ReceiveBuffer.Length);//
設置接收緩沖區
SendEventArgs.SetBuffer(SendBuffer, 0, SendBuffer.Length);//設置發送
緩沖區
}
}
接下來我們開始接入客戶端連接:
1socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
2socket.Bind(localEP);
3socket.Listen(100);
4Console.WriteLine("Server is bind on {0}",socket.LocalEndPoint);
5acceptEventArgs = new SocketAsyncEventArgs();//創建接入Socket上下文對象
6acceptEventArgs.Completed += acceptCompleted;//注冊接入完成事件處理程序
7socket.AcceptAsync(acceptEventArgs);//投遞接入操作
8Console.WriteLine("Server is started");
9
10//接入事件處理程序
11void acceptCompleted(object sender, SocketAsyncEventArgs e)
12{
13 var client = new UserObject( e.AcceptSocket);//創建用戶對象實例
14 client.ReceiveEventArgs.Completed += Receives_Completed;//注冊接收數據完成事件處
理程序
15 client.SendEventArgs.Completed += Send_Completed;//注冊發送數據完成事件處理程序
16 client.Socket.ReceiveAsync(client.ReceiveEventArgs);//投遞接收數據操作
17}
好了,我們開始接收數據:
1//接收數據完成事件處理程序
2void Receives_Completed(object sender, SocketAsyncEventArgs e)
3{
4 var client = e.UserToken as UserObject;
5 if (e.BytesTransferred == 0)//如果傳輸的數據量為0,則表示鏈接已經斷開
6 {
7 Console.WriteLine("Socket:{0} is closed",client.Socket.Handle);
8 client.Socket.Close();
9 }
10 else
11 {
12 string message = Encoding.Unicode.GetString(e.Buffer, 0,
e.BytesTransferred);//獲取接收到的數據
13 Console.WriteLine("Socket:{0} send message:
{1}",client.Socket.Handle,message);
14 string sent=string.Format("{0} bytes has been
received",e.BytesTransferred);
15 int length = Encoding.Unicode.GetBytes
(sent,0,sent.Length,client.SendBuffer,0);//將數據寫入發送緩沖區
16 client.SendEventArgs.SetBuffer(0, length);//設置緩沖區中有效數據的偏移量
和長度
17 client.Socket.SendAsync(client.SendEventArgs);//投遞發送數據操作
18 client.Socket.ReceiveAsync(client.ReceiveEventArgs);//投遞接收數據操作
19 }
20}
21
22//發送數據完成事件處理程序
23void Send_Completed(object sender, SocketAsyncEventArgs e)
24{
25 var client = e.UserToken as UserObject;
26 if (e.BytesTransferred==0)//如果傳輸的數據量為0,則表示鏈接已經斷開
27 {
28 Console.WriteLine("Socket:{0} is closed", client.Socket.Handle);
29 client.Socket.Close();
30 }
31 else
32 {
33 Console.WriteLine("Sent {0} bytes data to socket:{1}",e.BytesTransferred,
client.Socket.Handle);
34 }
35}
本文配套源碼