ms-help://MS.MSDNQTR.2003FEB.2052/cpguide/html/cpconusingblockingserversocket.htm
使用同步服務器套接字
同步服務器套接字掛起應用程序的執行,直到套接字上接收到連接請求。同步服務器套接字不適用於在操作中大量使用網絡的應用程序,但它們可能適用於簡單的網絡應用程序。
使用 Bind 和 Listen 方法設置 Socket 以在終結點上偵聽之後,Socket 就可以隨時使用 Accept 方法接受傳入的連接請求了。應用程序被掛起,直到調用 Accept 方法時接收到連接請求。
接收到連接請求時,Accept 返回一個與連接客戶端關聯的新 Socket 實例。下面的示例讀取客戶端數據,在控制檯上顯示該數據,然後將該數據回顯到客戶端。Socket 不指定任何消息協議,因此字符串“<EOF>”標記消息數據的結尾。它假定一個名爲 listener 的 Socket 已初始化,並綁定到一個終結點。
[Visual Basic]
Console.WriteLine("Waiting for a connection...")
Dim handler As Socket = listener.Accept()
Dim data As String = Nothing
While True
bytes = New Byte(1024) {}
Dim bytesRec As Integer = handler.Receive(bytes)
data += Encoding.ASCII.GetString(bytes, 0, bytesRec)
If data.IndexOf("<EOF>") > - 1 Then
Exit While
End If
End While
Console.WriteLine("Text received : {0}", data)
Dim msg As Byte() = Encoding.ASCII.GetBytes(data)
handler.Send(msg)
handler.Shutdown(SocketShutdown.Both)
handler.Close()
[C#]
Console.WriteLine("Waiting for a connection...");
Socket handler = listener.Accept();
String data = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}
Console.WriteLine( "Text received : {0}", data);
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
使用異步服務器套接字
異步服務器套接字使用 .NET Framework 異步編程模型處理網絡服務請求。Socket 類遵循標準 .NET Framework 異步命名模式;例如,同步 Accept 方法對應異步 BeginAccept 和 EndAccept 方法。
異步服務器套接字需要一個開始接受網絡連接請求的方法,一個處理連接請求並開始接收網絡數據的回調方法以及一個結束接收數據的回調方法。本節將進一步討論所有這些方法。
在下面的示例中,爲開始接受網絡連接請求,方法 StartListening 初始化 Socket,然後使用 BeginAccept 方法開始接受新連接。當套接字上接收到新連接請求時,將調用接受回調方法。它負責獲取將處理連接的 Socket 實例,並將 Socket 提交給將處理請求的線程。接受回調方法實現 AsyncCallback 委託;它返回 void,並帶一個 IAsyncResult 類型的參數。下面的示例是接受回調方法的外殼程序。
[Visual Basic]
Sub acceptCallback(ar As IAsyncResult)
' Add the callback code here.
End Sub 'acceptCallback
[C#]
void acceptCallback( IAsyncResult ar) {
// Add the callback code here.
}
BeginAccept 方法帶兩個參數:指向接受回調方法的 AsyncCallback 委託和一個用於將狀態信息傳遞給回調方法的對象。在下面的示例中,偵聽 Socket 通過狀態參數傳遞給回調方法。本示例創建一個 AsyncCallback 委託並開始接受網絡連接。
[Visual Basic]
listener.BeginAccept( _
New AsyncCallback(SocketListener.acceptCallback),_
listener)
[C#]
listener.BeginAccept(
new AsyncCallback(SocketListener.acceptCallback),
listener);
異步套接字使用系統線程池中的線程處理傳入的連接。一個線程負責接受連接,另一線程用於處理每個傳入的連接,還有一個線程負責接收連接數據。這些線程可以是同一個線程,具體取決於線程池所分配的線程。在下面的示例中,System.Threading.ManualResetEvent 類掛起主線程的執行並在執行可以繼續時發出信號。
下面的示例顯示在本地計算機上創建異步 TCP/IP 套接字並開始接受連接的異步方法。它假定以下內容:存在一個名爲 allDone 的全局 ManualResetEvent,該方法是一個名爲 SocketListener 的類的成員,以及定義了一個名爲 acceptCallback 的回調方法。
[Visual Basic]
Public Sub StartListening()
Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())
Dim localEP = New IPEndPoint(ipHostInfo.AddressList(0), 11000)
Console.WriteLine("Local address and port : {0}", localEP.ToString())
Dim listener As New Socket(localEP.Address.AddressFamily, _
SocketType.Stream, ProtocolType.Tcp)
Try
listener.Bind(localEP)
s.Listen(10)
While True
allDone.Reset()
Console.WriteLine("Waiting for a connection...")
listener.BeginAccept(New _
AsyncCallback(SocketListener.acceptCallback), _
listener)
allDone.WaitOne()
End While
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
Console.WriteLine("Closing the listener...")
End Sub 'StartListening
[C#]
public void StartListening() {
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPEndPoint localEP = new IPEndPoint(ipHostInfo.AddressList[0],11000);
Console.WriteLine("Local address and port : {0}",localEP.ToString());
Socket listener = new Socket( localEP.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp );
try {
listener.Bind(localEP);
s.Listen(10);
while (true) {
allDone.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(SocketListener.acceptCallback),
listener );
allDone.WaitOne();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine( "Closing the listener...");
}
接受回調方法(即前例中的 acceptCallback)負責向主應用程序發出信號,讓它繼續執行處理、建立與客戶端的連接並開始異步讀取客戶端數據。下面的示例是 acceptCallback 方法實現的第一部分。該方法的此節向主應用程序線程發出信號,讓它繼續處理並建立與客戶端的連接。它採用一個名爲 allDone 的全局 ManualResetEvent。
[Visual Basic]
Public Sub acceptCallback(ar As IAsyncResult)
allDone.Set()
Dim listener As Socket = CType(ar.AsyncState, Socket)
Dim handler As Socket = listener.EndAccept(ar)
' Additional code to read data goes here.
End Sub 'acceptCallback
[C#]
public void acceptCallback(IAsyncResult ar) {
allDone.Set();
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Additional code to read data goes here.
}
從客戶端套接字讀取數據需要一個在異步調用之間傳遞值的狀態對象。下面的示例實現一個用於從遠程客戶端接收字符串的狀態對象。它包含以下各項的字段:客戶端套接字,用於接收數據的數據緩衝區,以及用於創建客戶端發送的數據字符串的 StringBuilder。將這些字段放在該狀態對象中,使這些字段的值在多個調用之間得以保留,以便從客戶端套接字讀取數據。
[Visual Basic]
Public Class StateObject
Public workSocket As Socket = Nothing
Public BufferSize As Integer = 1024
Public buffer(BufferSize) As Byte
Public sb As New StringBuilder()
End Class 'StateObject
[C#]
public class StateObject {
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
開始從客戶端套接字接收數據的 acceptCallback 方法的此節首先初始化 StateObject 類的一個實例,然後調用 BeginReceive 方法以開始從客戶端套接字異步讀取數據。
下面的示例顯示了完整的 acceptCallback 方法。它假定以下內容:存在一個名爲 allDone 的 ManualResetEvent,定義了 StateObject 類,以及在名爲 SocketListener 的類中定義了 readCallback 方法。
[Visual Basic]
Public Shared Sub acceptCallback(ar As IAsyncResult)
' Get the socket that handles the client request.
Dim listener As Socket = CType(ar.AsyncState, Socket)
Dim handler As Socket = listener.EndAccept(ar)
' Signal the main thread to continue.
allDone.Set()
' Create the state object.
Dim state As New StateObject()
state.workSocket = handler
handler.BeginReceive(state.buffer, 0, state.BufferSize, 0, _
AddressOf AsynchronousSocketListener.readCallback, state)
End Sub 'acceptCallback
[C#]
public static void acceptCallback(IAsyncResult ar) {
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Signal the main thread to continue.
allDone.Set();
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(AsynchronousSocketListener.readCallback), state);
}
需要爲異步套接字服務器實現的 final 方法是返回客戶端發送的數據的讀取回調方法。與接受回調方法一樣,讀取回調方法也是一個 AsyncCallback 委託。該方法將來自客戶端套接字的一個或多個字節讀入數據緩衝區,然後再次調用 BeginReceive 方法,直到客戶端發送的數據完成爲止。從客戶端讀取整個消息後,在控制檯上顯示字符串,並關閉處理與客戶端的連接的服務器套接字。
下面的示例實現 readCallback 方法。它假定定義了 StateObject 類。
[Visual Basic]
Public Shared Sub readCallback(ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim handler As Socket = state.workSocket
' Read data from the client socket.
Dim read As Integer = handler.EndReceive(ar)
' Data was read from the client socket.
If read > 0 Then
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read))
handler.BeginReceive(state.buffer, 0, state.BufferSize, 0, _
AddressOf readCallback, state)
Else
If state.sb.Length > 1 Then
' All the data has been read from the client;
' display it on the console.
Dim content As String = state.sb.ToString()
Console.WriteLine("Read {0} bytes from socket." + _
ControlChars.Cr + " Data : {1}", content.Length, content)
End If
End If
End Sub 'readCallback
[C#]
public void readCallback(IAsyncResult ar) {
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.WorkSocket;
// Read data from the client socket.
int read = handler.EndReceive(ar);
// Data was read from the client socket.
if (read > 0) {
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,read));
handler.BeginReceive(state.buffer,0,StateObject.BufferSize, 0,
new AsyncCallback(readCallback), state);
} else {
if (state.sb.Length > 1) {
// All the data has been read from the client;
// display it on the console.
string content = state.sb.ToString();
Console.WriteLine("Read {0} bytes from socket./n Data : {1}",
content.Length, content);
}
handler.Close();
}
}
使用異步客戶端套接字
異步客戶端套接字在等待網絡操作完成時不掛起應用程序。相反,它使用標準 .NET Framework 異步編程模型在一個線程上處理網絡連接,而應用程序繼續在原始線程上運行。異步套接字適用於大量使用網絡或不能等待網絡操作完成才能繼續的應用程序。
Socket 類遵循異步方法的 .NET Framework 命名模式;例如,同步 Receive 方法對應異步 BeginReceive 和 EndReceive 方法。
異步操作要求回調方法返回操作結果。如果應用程序不需要知道結果,則不需要任何回調方法。本節中的代碼示例闡釋如何使用某個方法開始與網絡設備的連接並使用回調方法結束連接,如何使用某個方法開始發送數據並使用回調方法完成發送,以及如何使用某個方法開始接收數據並使用回調方法結束接收數據。
異步套接字使用多個系統線程池中的線程處理網絡連接。一個線程負責初始化數據的發送或接收;其他線程完成與網絡設備的連接併發送或接收數據。在下列示例中,System.Threading.ManualResetEvent 類的實例用於掛起主線程的執行並在執行可以繼續時發出信號。
在下面的示例中,爲了將異步套接字連接到網絡設備,Connect 方法初始化一個 Socket,然後調用 BeginConnect 方法,傳遞表示網絡設備的遠程終結點、連接回調方法以及狀態對象(即客戶端 Socket,用於在異步調用之間傳遞狀態信息)。該示例實現 Connect 方法以將指定的 Socket 連接到指定的終結點。它採用一個名爲 connectDone 的全局 ManualResetEvent。
[Visual Basic]
Public Shared Sub Connect(remoteEP As EndPoint, client As Socket)
client.BeginConnect(remoteEP, _
AddressOf ConnectCallback, client)
connectDone.WaitOne()
End Sub 'Connect
[C#]
public static void Connect(EndPoint remoteEP, Socket client) {
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client );
connectDone.WaitOne();
}
連接回調方法 ConnectCallback 實現 AsyncCallback 委託。它在遠程設備可用時連接到遠程設備,然後通過設置 ManualResetEvent connectDone 嚮應用程序線程發出連接完成的信號。下面的代碼實現 ConnectCallback 方法。
[Visual Basic]
Private Shared Sub ConnectCallback(ar As IAsyncResult)
Try
' Retrieve the socket from the state object.
Dim client As Socket = CType(ar.AsyncState, Socket)
' Complete the connection.
client.EndConnect(ar)
Console.WriteLine("Socket connected to {0}", _
client.RemoteEndPoint.ToString())
' Signal that the connection has been made.
connectDone.Set()
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub 'ConnectCallback
[C#]
private static void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Send 示例方法以 ASCII 格式對指定的字符串數據進行編碼,並將其異步發送到指定的套接字所表示的網絡設備。下面的示例實現 Send 方法。
[Visual Basic]
Private Shared Sub Send(client As Socket, data As [String])
' Convert the string data to byte data using ASCII encoding.
Dim byteData As Byte() = Encoding.ASCII.GetBytes(data)
' Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, _
AddressOf SendCallback, client)
End Sub 'Send
[C#]
private static void Send(Socket client, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None,
new AsyncCallback(SendCallback), client);
}
發送回調方法 SendCallback 實現 AsyncCallback 委託。它在網絡設備準備接收時發送數據。下面的示例顯示 SendCallback 方法的實現。它採用一個名爲 sendDone 的全局 ManualResetEvent。
[Visual Basic]
Private Shared Sub SendCallback(ar As IAsyncResult)
Try
' Retrieve the socket from the state object.
Dim client As Socket = CType(ar.AsyncState, Socket)
' Complete sending the data to the remote device.
Dim bytesSent As Integer = client.EndSend(ar)
Console.WriteLine("Sent {0} bytes to server.", bytesSent)
' Signal that all bytes have been sent.
sendDone.Set()
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub 'SendCallback
[C#]
private static void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket) ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
從客戶端套接字讀取數據需要一個在異步調用之間傳遞值的狀態對象。下面的類是用於從客戶端套接字接收數據的示例狀態對象。它包含以下各項的字段:客戶端套接字,用於接收數據的緩衝區,以及用於保存傳入數據字符串的 StringBuilder。將這些字段放在該狀態對象中,使這些字段的值在多個調用之間得以保留,以便從客戶端套接字讀取數據。
[Visual Basic]
Public Class StateObject
' Client socket.
Public workSocket As Socket = Nothing
' Size of receive buffer.
Public BufferSize As Integer = 256
' Receive buffer.
Public buffer(256) As Byte
' Received data string.
Public sb As New StringBuilder()
End Class 'StateObject
[C#]
public class StateObject {
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
Receive 方法示例設置狀態對象,然後調用 BeginReceive 方法從客戶端套接字異步讀取數據。下面的示例實現 Receive 方法。
[Visual Basic]
Private Shared Sub Receive(client As Socket)
Try
' Create the state object.
Dim state As New StateObject()
state.workSocket = client
' Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, state.BufferSize, 0, _
AddressOf ReceiveCallback, state)
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub 'Receive
[C#]
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
接收回調方法 ReceiveCallback 實現 AsyncCallback 委託。它接收來自網絡設備的數據並生成消息字符串。它將來自網絡的一個或多個數據字節讀入數據緩衝區,然後再次調用 BeginReceive 方法,直到客戶端發送的數據完成爲止。從客戶端讀取所有數據後,ReceiveCallback 通過設置 ManualResetEvent sendDone 嚮應用程序線程發出數據完成的信號。
下面的示例代碼實現 ReceiveCallback 方法。它採用一個名爲 response 的全局字符串,其中包含接收到的字符串和一個名爲 receiveDone 的全局 ManualResetEvent。服務器必須完全關閉客戶端套接字才能結束該網絡會話。
[Visual Basic]
Private Shared Sub ReceiveCallback(ar As IAsyncResult)
Try
' Retrieve the state object and the client socket
' from the asynchronous state object.
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
' Read data from the remote device.
Dim bytesRead As Integer = client.EndReceive(ar)
If bytesRead > 0 Then
' There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, _
bytesRead))
' Get the rest of the data.
client.BeginReceive(state.buffer, 0, state.BufferSize, 0, _
AddressOf ReceiveCallback, state)
Else
' All the data has arrived; put it in response.
If state.sb.Length > 1 Then
response = state.sb.ToString()
End If
' Signal that all bytes have been received.
receiveDone.Set()
End If
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub 'ReceiveCallback
[C#]
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}