您的位置:首页 > 其它

使用异步客户端套接字

2008-09-19 20:33 211 查看

使用异步客户端套接字

异步客户端套接字在等待网络操作完成时不挂起应用程序。相反,它使用标准.NET框架异步编程模型在一个线程上处理网络连接,而应用程序继续在原始线程上运行。异步套接字适用于大量使用网络或不能等待网络操作完成才能继续的应用程序。

Socket类遵循异步方法的.NET框架命名模式;例如,同步Receive方法对应异步BeginReceive和EndReceive方法。

异步操作要求回调方法返回操作结果。如果应用程序不需要知道结果,则不需要任何回调方法。本节中的代码示例阐释如何使用某个方法开始与网络设备的连接并使用回调方法结束连接,如何使用某个方法开始发送数据并使用回调方法完成发送,以及如何使用某个方法开始接收数据并使用回调方法结束接收数据。

异步套接字使用多个系统线程池中的线程处理网络连接。一个线程负责初始化数据的发送或接收;其他线程完成与网络设备的连接并发送或接收数据。在下列示例中,System.Threading.ManualResetEvent类的实例用于挂起主线程的执行并在执行可以继续时发出信号。

在下面的示例中,为了将异步套接字连接到网络设备,
Connect
方法初始化Socket实例,然后调用BeginConnect方法,传递表示网络设备的远程终结点、连接回调方法以及状态对象(即客户端Socket实例,用于在异步调用之间传递状态信息)。该示例实现
Connect
方法以将指定的Socket实例连接到指定的终结点。它假定存在一个名为
connectDone
的全局ManualResetEvent
[C#]
publicstaticvoidConnect(EndPointremoteEP,Socketclient){
client.BeginConnect(remoteEP,
newAsyncCallback(ConnectCallback),client);
connectDone.WaitOne();
}

连接回调方法
ConnectCallback
实现AsyncCallback委托。它在远程设备可用时连接到远程设备,然后通过设置ManualResetEvent
connectDone
向应用程序线程发出连接完成的信号。下面的代码实现
ConnectCallback
方法。
[C#]
privatestaticvoidConnectCallback(IAsyncResultar){
try{
//Retrievethesocketfromthestateobject.
Socketclient=(Socket)ar.AsyncState;
//Completetheconnection.
client.EndConnect(ar);
Console.WriteLine("Socketconnectedto{0}",
client.RemoteEndPoint.ToString());
//Signalthattheconnectionhasbeenmade.
connectDone.Set();
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
}

Send
示例方法以ASCII格式对指定的字符串数据进行编码,并将其异步发送到指定的套接字所表示的网络设备。下面的示例实现
Send
方法。
[C#]
privatestaticvoidSend(Socketclient,Stringdata){
//ConvertthestringdatatobytedatausingASCIIencoding.
byte[]byteData=Encoding.ASCII.GetBytes(data);
//Beginsendingthedatatotheremotedevice.
client.BeginSend(byteData,0,byteData.Length,SocketFlags.None,
newAsyncCallback(SendCallback),client);
}

发送回调方法
SendCallback
实现AsyncCallback委托。它在网络设备准备接收时发送数据。下面的示例显示
SendCallback
方法的实现。它假定存在一个名为
sendDone
的全局ManualResetEvent实例。
[C#]
privatestaticvoidSendCallback(IAsyncResultar){
try{
//Retrievethesocketfromthestateobject.
Socketclient=(Socket)ar.AsyncState;
//Completesendingthedatatotheremotedevice.
intbytesSent=client.EndSend(ar);
Console.WriteLine("Sent{0}bytestoserver.",bytesSent);
//Signalthatallbyteshavebeensent.
sendDone.Set();
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
}

从客户端套接字读取数据需要一个在异步调用之间传递值的状态对象。下面的类是用于从客户端套接字接收数据的示例状态对象。它包含以下各项的字段:客户端套接字,用于接收数据的缓冲区,以及用于保存传入数据字符串的StringBuilder。将这些字段放在该状态对象中,使这些字段的值在多个调用之间得以保留,以便从客户端套接字读取数据。
[C#]
publicclassStateObject{
publicSocketworkSocket=null;//Clientsocket.
publicconstintBufferSize=256;//Sizeofreceivebuffer.
publicbyte[]buffer=newbyte[BufferSize];//Receivebuffer.
publicStringBuildersb=newStringBuilder();//Receiveddatastring.
}

Receive
方法示例设置状态对象,然后调用BeginReceive方法从客户端套接字异步读取数据。下面的示例实现
Receive
方法。
[C#]
privatestaticvoidReceive(Socketclient){
try{
//Createthestateobject.
StateObjectstate=newStateObject();
state.workSocket=client;
//Beginreceivingthedatafromtheremotedevice.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
newAsyncCallback(ReceiveCallback),state);
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
}

接收回调方法
ReceiveCallback
实现AsyncCallback委托。它接收来自网络设备的数据并生成消息字符串。它将来自网络的一个或多个数据字节读入数据缓冲区,然后再次调用BeginReceive方法,直到客户端发送的数据完成为止。从客户端读取所有数据后,
ReceiveCallback
通过设置ManualResetEvent
sendDone
向应用程序线程发出数据完成的信号。

下面的示例代码实现
ReceiveCallback
方法。它假定存在一个名为
response
的全局字符串(该字符串保存接收的字符串)和一个名为
receiveDone
的全局ManualResetEvent实例。服务器必须正常关闭客户端套接字以结束网络会话。
[C#]
privatestaticvoidReceiveCallback(IAsyncResultar){
try{
//Retrievethestateobjectandtheclientsocket
//fromtheasyncstateobject.
StateObjectstate=(StateObject)ar.AsyncState;
Socketclient=state.workSocket;
//Readdatafromtheremotedevice.
intbytesRead=client.EndReceive(ar);
if(bytesRead>0){
//Theremightbemoredata,sostorethedatareceivedsofar.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
//Gettherestofthedata.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
newAsyncCallback(ReceiveCallback),state);
}else{
//Allthedatahasarrived;putitinresponse.
if(state.sb.Length>1){
response=state.sb.ToString();
}
//Signalthatallbyteshavebeenreceived.
receiveDone.Set();
}
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
}

用套接字进行侦听

侦听器或服务器套接字在网络上打开一个端口,然后等待客户端连接到该端口。尽管存在其他网络地址族和协议,但本示例说明如何为TCP/IP网络创建远程服务。

TCP/IP服务的唯一地址是这样定义的:将主机的IP地址与服务的端口号组合,以便为服务创建终结点。Dns类提供的方法返回有关本地网络设备支持的网络地址的信息。当本地网络设备有多个网络地址时,或者当本地系统支持多个网络设备时,Dns类返回有关所有网络地址的信息,应用程序必须为服务选择正确的地址。Internet分配号码机构(InternetAssignedNumbersAuthority,IANA)定义公共服务的端口号(有关更多信息,请访问http://www.iana.org/assignments/port-numbers)。其他服务可以具有1,024到65,535范围内的注册端口号。

下面的代码示例通过将Dns为主机返回的第一个IP地址与从注册端口号范围内选择的端口号组合,为服务器创建IPEndPoint。
[C#]
IPHostEntryipHostInfo=Dns.Resolve(Dns.GetHostName());
IPAddressipAddress=ipHostInfo.AddressList[0];
IPEndPointlocalEndPoint=newIPEndPoint(ipAddress,11000);

确定本地终结点后,必须使用Bind方法将Socket与该终结点关联,并使用Listen方法设置Socket在该终结点上侦听。如果特定的地址和端口组合已经被使用,则Bind将引发异常。下面的示例阐释如何将SocketIPEndPoint关联。
[C#]
listener.Bind(localEndPoint);
listener.Listen(100);

Listen方法带单个参数,该参数指定在向连接客户端返回服务器忙错误之前允许的Socket挂起连接数。在本示例中,在向第101个客户端返回服务器忙响应之前,最多可以在连接队列中放置100个客户端。

使用同步服务器套接字

同步服务器套接字挂起应用程序的执行,直到套接字上接收到连接请求。同步服务器套接字不适用于在操作中大量使用网络的应用程序,但它们可能适用于简单的网络应用程序。

使用Bind和Listen方法设置Socket以在终结点上侦听之后,Socket就可以随时使用Accept方法接受传入的连接请求了。应用程序被挂起,直到调用Accept方法时接收到连接请求。

接收到连接请求时,Accept返回一个与连接客户端关联的新Socket实例。下面的示例读取客户端数据,在控制台上显示该数据,然后将该数据回显到客户端。Socket不指定任何消息协议,因此字符串“<EOF>”标记消息数据的结尾。它假定名为
listener
Socket实例已初始化并绑定到终结点。
[C#]
Console.WriteLine("Waitingforaconnection...");
Sockethandler=listener.Accept();
Stringdata=null;
while(true){
bytes=newbyte[1024];
intbytesRec=handler.Receive(bytes);
data+=Encoding.ASCII.GetString(bytes,0,bytesRec);
if(data.IndexOf("<EOF>")>-1){
break;
}
}
Console.WriteLine("Textreceived:{0}",data);
byte[]msg=Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();

使用异步服务器套接字

异步服务器套接字使用.NET框架异步编程模型处理网络服务请求。Socket类遵循标准.NET异步命名模式;例如,同步Accept方法对应异步BeginAccept和EndAccept方法。

异步服务器套接字需要一个开始接受网络连接请求的方法,一个处理连接请求并开始接收网络数据的回调方法以及一个结束接收数据的回调方法。本节将进一步讨论所有这些方法。

在下面的示例中,为开始接受网络连接请求,方法
StartListening
初始化Socket,然后使用BeginAccept方法开始接受新连接。当套接字上接收到新连接请求时调用接受回调方法。它负责获取将处理连接的Socket实例,并将Socket提交给将处理请求的线程。接受回调方法实现AsyncCallback委托;它返回void,并带一个IAsyncResult类型的参数。下面的示例是接受回调方法的外壳程序。
[C#]
voidacceptCallback(IAsyncResultar){
//Addthecallbackcodehere.
}

BeginAccept方法带两个参数:指向接受回调方法的AsyncCallback委托和一个用于将状态信息传递给回调方法的对象。在下面的示例中,侦听Socket通过状态参数传递给回调方法。本示例创建一个AsyncCallback委托并开始接受网络连接。
[C#]
listener.BeginAccept(
newAsyncCallback(SocketListener.acceptCallback),
listener);

异步套接字使用系统线程池中的线程处理传入的连接。一个线程负责接受连接,另一线程用于处理每个传入的连接,还有一个线程负责接收连接数据。这些线程可以是同一个线程,具体取决于线程池所分配的线程。在下面的示例中,System.Threading.ManualResetEvent类挂起主线程的执行并在执行可以继续时发出信号。

下面的示例显示在本地计算机上创建异步TCP/IP套接字并开始接受连接的异步方法。它假定以下内容:存在一个名为
allDone
的全局ManualResetEvent实例,该方法是名为
SocketListener
类的成员,以及定义了一个名为
acceptCallback
的回调方法。
[C#]
publicvoidStartListening(){
IPHostEntryipHostInfo=newDns.Resolve(Dns.GetHostName());
IPEndPointlocalEP=newIPEndPoint(ipHostInfo.AddressList[0],11000);
Console.WriteLine("Localaddressandport:{0}",localEP.ToString());
Socketlistener=newSocket(localEP.Address.AddressFamily,
SocketType.Stream,ProtocolType.Tcp);
try{
listener.Bind(localEP);
s.Listen(10);
while(true){
allDone.Reset();
Console.WriteLine("Waitingforaconnection...");
listener.BeginAccept(
newAsyncCallback(SocketListener.acceptCallback),
listener);
allDone.WaitOne();
}
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
Console.WriteLine("Closingthelistener...");
}

接受回调方法(即前例中的
acceptCallback
)负责向主应用程序发出信号,让它继续执行处理、建立与客户端的连接并开始异步读取客户端数据。下面的示例是
acceptCallback
方法实现的第一部分。该方法的此节向主应用程序线程发出信号,让它继续处理并建立与客户端的连接。它假定存在一个名为
allDone
的全局ManualResetEvent实例。
[C#]
publicvoidacceptCallback(IAsyncResultac){
allDone.Set();
Socketlistener=(Socket)ar.AsyncState;
Sockethandler=listener.EndAccept(ar);
//Additionalcodetoreaddatagoeshere.
}

从客户端套接字读取数据需要一个在异步调用之间传递值的状态对象。下面的示例实现一个用于从远程客户端接收字符串的状态对象。它包含以下各项的字段:客户端套接字,用于接收数据的数据缓冲区,以及用于创建客户端发送的数据字符串的StringBuilder。将这些字段放在该状态对象中,使这些字段的值在多个调用之间得以保留,以便从客户端套接字读取数据。
[C#]
publicclassStateObject{
publicSocketworkSocket=null;
publicconstintBufferSize=1024;
publicbyte[]buffer=newbyte[BufferSize];
publicStringBuildersb=newStringBuilder();
}

开始从客户端套接字接收数据的
acceptCallback
方法的此节首先初始化
StateObject
类的一个实例,然后调用BeginReceive方法以开始从客户端套接字异步读取数据。

下面的示例显示了完整的
acceptCallback
方法。它假定以下内容:存在一个名为
allDone
ManualResetEvent实例,定义了
StateObject
类,以及在名为
SocketListener
的类中定义了
readCallback
方法。
[C#]
publicstaticvoidacceptCallback(IAsyncResultar){
//Getthesocketthathandlestheclientrequest.
Socketlistener=(Socket)ar.AsyncState;
Sockethandler=listener.EndAccept(ar);
//Signalthemainthreadtocontinue.
allDone.Set();
//Createthestateobject.
StateObjectstate=newStateObject();
state.workSocket=handler;
handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
newAsyncCallback(AsynchronousSocketListener.readCallback),state);
}

需要为异步套接字服务器实现的final方法是返回客户端发送的数据的读取回调方法。与接受回调方法一样,读取回调方法也是一个AsyncCallback委托。该方法将来自客户端套接字的一个或多个字节读入数据缓冲区,然后再次调用BeginReceive方法,直到客户端发送的数据完成为止。从客户端读取整个消息后,在控制台上显示字符串,并关闭处理与客户端的连接的服务器套接字。

下面的示例实现
readCallback
方法。它假定定义了
StateObject
类。
[C#]
publicvoidreadCallback(IAsyncResultar){
StateObjectstate=(StateObject)ar.AsyncState;
Sockethandler=state.WorkSocket;
//Readdatafromtheclientsocket.
intread=handler.EndReceive(ar);
//Datawasreadfromtheclientsocket.
if(read>0){
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,read));
handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
newAsyncCallback(readCallback),state);
}else{
if(state.sb.Length>1){
//Allthedatahasbeenreadfromtheclient;
//displayitontheconsole.
stringcontent=state.sb.ToString();
Console.WriteLine("Read{0}bytesfromsocket./nData:{1}",
content.Length,content);
}
handler.Close();
}
}

同步客户端套接字示例

下面的示例程序创建一个连接到服务器的客户端。该客户端是用同步套接字生成的,因此挂起客户端应用程序的执行,直到服务器返回响应为止。该应用程序将字符串发送到服务器,然后在控制台显示该服务器返回的字符串。
[C#]
usingSystem;
usingSystem.Net;
usingSystem.Net.Sockets;
usingSystem.Text;
publicclassSynchronousSocketClient{
publicstaticvoidStartClient(){
//Databufferforincomingdata.
byte[]bytes=newbyte[1024];
//Connecttoaremotedevice.
try{
//Establishtheremoteendpointforthesocket.
//Thenameofthe
//remotedeviceis"host.contoso.com".
IPHostEntryipHostInfo=Dns.Resolve("host.contoso.com");
IPAddressipAddress=ipHostInfo.AddressList[0];
IPEndPointremoteEP=newIPEndPoint(ipAddress,11000);
//CreateaTCP/IPsocket.
Socketsender=newSocket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
//Connectthesockettotheremoteendpoint.Catchanyerrors.
try{
sender.Connect(remoteEP);
Console.WriteLine("Socketconnectedto{0}",
sender.RemoteEndPoint.ToString());
//Encodethedatastringintoabytearray.
byte[]msg=Encoding.ASCII.GetBytes("Thisisatest<EOF>");
//Sendthedatathroughthesocket.
intbytesSent=sender.Send(msg);
//Receivetheresponsefromtheremotedevice.
intbytesRec=sender.Receive(bytes);
Console.WriteLine("Echoedtest={0}",
Encoding.ASCII.GetString(bytes,0,bytesRec));
//Releasethesocket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}catch(ArgumentNullExceptionane){
Console.WriteLine("ArgumentNullException:{0}",ane.ToString());
}catch(SocketExceptionse){
Console.WriteLine("SocketException:{0}",se.ToString());
}catch(Exceptione){
Console.WriteLine("Unexpectedexception:{0}",e.ToString());
}
}catch(Exceptione){
Console.WriteLine(e.ToString());
}
}
publicstaticintMain(String[]args){
StartClient();
return0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐