首页 > 代码库 > 服务总结 - 异步

服务总结 - 异步

项目使用Socket与设备通信,可能同时会有成百上千个连接传送数据,当然不可能针对每个连接都要单独建立一个线程在后台接收数据,很明显的方式就是异步。

 

1. 怎么理解异步


 

这里,我举个不太恰当的例子,打电话和写信。打电话的时候,理想情况下,我们希望我问个问题,对方立马就能答复的,如果对方憋了半天,啥话也不说,我也只能一直等着,因为我接下来要说的话,是要根据他的回复来说的,这样在这期间什么事也干不了,对于程序来说,就叫同步;而写信不是这样,写信是即使我只问一个问题,我也可以在发完信以后,做其他事情,比如吃个饭啊,睡个觉什么的,不用一直守在信箱旁边,等着那边的回信,这个就可以称为异步

所以引申到编程中来,可以知道异步实际上就是程序发送一个指令以后,并不是阻塞等待回复,而是继续运行,这样减少了资源浪费,也让程序运行更加流畅。

 

2. Socket异步通信原理


 

Socket异步通信实际上是利用了Windows系统的消息机制,当发送异步请求的时候,Socket向操作系统注册了一个回调函数,告诉操作系统,这个回调是用来接收Socket那边发来的数据;当有回复返回的时候,操作系统通过调用注册的回调函数,通知应用程序有数据返回,从而达到处理回复的目的。

 

3. 了解.net异步编程的规则


 

在实现异步通信前,我们来看看.net是如何实现异步的,以下摘自网络(自己写打字太多。。)

.NET Framework允许异步调用任何方法,定义与需要调用的方法具有相同签名的委托,CLR将自动为该委托定义添加适当签名的BeginInvoke虚方法和EndInvoke虚方法和Invoke方法。 

我们先来了解这2个方法和一个委托和一个接口:

(1)
BeginInvoke方法用于启动异步调用
它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数,将 AsyncCallback 和 AsyncState(可通过 IAsyncResult 接口的AsyncState 属性获得)。作为最后两个参数,如没有可以为空。
BeginInvoke立即返回,不等待异步调用完成。
BeginInvoke返回IasyncResult,可用于监视调用进度。

结果对象IAsyncResult是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。
结果对象被传递到结束操作,该操作返回调用的最终返回值。
在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。

(2)
EndInvoke方法用于检索异步调用结果。
在调用BeginInvoke后可随时调用EndInvoke方法,注意:始终在异步调用完成后调用EndInvoke。
如果异步调用未完成,EndInvoke将一直阻塞到异步调用完成。
EndInvoke的参数包括需要异步执行的方法的out和ref参数以及由BeginInvoke返回的IAsyncResult。
要注意的是,始终在异步调用完成后调用EndInvoke

(3)
AsyncCallback委托用于指定在开始操作完成后应被调用的方法。
AsyncCallback委托被作为开始操作上的第二个到最后一个参数传递。
代码原型如下:
[Serializable]
public delegate void AsyncCallback(IAsyncResult ar);

(4)

IAsyncResult接口
它表示异步操作的状态,该接口定义了4个公用属性。

 

4. Socket异步通信的实现


 

.net已经为我们建好了Socket的异步通信模型,并提供了相应的函数接口,我们只要直接拿来用就行了。接口主要一下三种:BeginAccept, EndAccept; BeginReceive, EndReceive; BeginSend, EndSend。使用这三种接口就能实现我们想要的异步通信方式。

新建一个控制台程序,在服务端,代码如下: (注:为了描述方便,所有代码只写关键步骤,没有考虑异常和其他条件处理)

建立服务端Socket,并设置好参数,绑定ip和端口后,使用BeginAccept异步监听连接,其中定义了新连接回调函数Accept,并设置参数“new socket connect”

static Socket socket;static void Main(string[] args){     //新建Socket,并开始监听连接     socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);     socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));     socket.Listen(100);     socket.BeginAccept(new AsyncCallback(Accept), "new socket connect");     Console.ReadKey();}

 

当有新的连接请求时,触发回调,建立完成后,启动异步接收

static void Accept(IAsyncResult ar){    Console.WriteLine(ar.AsyncState.ToString());    //结束监听    Socket _socket = socket.EndAccept(ar);    //投递接收请求    Mark mark = new Mark(_socket);    _socket.BeginReceive(mark.buffer, 0, mark.buffer.Length, SocketFlags.None, new AsyncCallback(Receive), mark)}

其中,定义类Mark,用于保存新连接和要接收的数据。

class Mark{    public Socket socket;    public byte[] buffer;    public Mark(Socket socket)    {        this.socket = socket;        buffer = new byte[1024];    }}

 

接收到数据,并打印,同时异步发送一条信息

static void Receive(IAsyncResult ar){    Mark mark = (Mark)ar.AsyncState;    //结束接收    int length = mark.socket.EndReceive(ar);    if (length > 0)    {        Console.WriteLine("Receive: " + Encoding.Default.GetString(mark.buffer, 0, length));        byte[] senddata = http://www.mamicode.com/Encoding.Default.GetBytes("hello, this is server.");        Console.WriteLine("Send: hello, this is server.");        //投递发送请求        mark.socket.BeginSend(senddata, 0, senddata.Length, SocketFlags.None, new AsyncCallback(Send), mark);    }}

 

最后,结束发送

static void Send(IAsyncResult ar){    Mark mark = (Mark)ar.AsyncState;    //结束发送    mark.socket.EndSend(ar);}

这样,服务端的简单异步处理完成,客户端类似,不做赘述。

class Program{    static Socket socket;    static void Main(string[] args)    {        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);        socket.Connect(IPAddress.Parse("127.0.0.1"), 1234);        byte[] senddata = http://www.mamicode.com/Encoding.Default.GetBytes("Hello, this is client.");        Console.WriteLine("Send: Hello, this is client.");        socket.BeginSend(senddata, 0, senddata.Length, SocketFlags.None, new AsyncCallback(Send), null);        Console.ReadKey();    }    static void Send(IAsyncResult ar)    {        socket.EndSend(ar);        byte[] buffer = new byte[1024];        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Receive), buffer);    }    static void Receive(IAsyncResult ar)    {        int length = socket.EndReceive(ar);        if (length > 0)        {            byte[] buffer = (byte[])ar.AsyncState;            Console.WriteLine("Receive: " + Encoding.Default.GetString(buffer, 0, length));        }    }}

 

启动服务端,再启动客户端,结果如下:

client:技术分享

 

server: 技术分享

服务总结 - 异步