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());
}
}