本文将在C#中Socket同步通信的基础上,分析和研究Socket异步编程的实现方法,目的是深入了解Socket编程的基本原理,增强对网络游戏开发相关内容的认识。
什么是Socket编程的异步是实现
所谓Socket编程的异步实现是指按照异步过程来实现Socket编程,那么什么是异步过程呢,我们把在完成了一次调用后通过状态、通知和回调来告知调用者的方式成为异步过程,换句话说,在异步过程中当调用一个方法时,调用者并不能够立刻得到结果,只有当这个方法调用完毕后调用者才能获得调用结果。这样做的好处是什么呢?答案是高效。相信大家还记得我们在《C#中Socket通信编程的同步实现》这篇文章中使用多线程来实现简单聊天的案例吧,在这个案例中我们需要开启两个线程来不断监听客户端的连接和客户端的消息,这样的效率肯定是很低的。那么现在好了,我们可以通过异步过程来解决这个问题,下面我们就来看看如何实现Socket的异步通信。
如何实现Socket异步通信
服务端
基本流程
- 创建套接字
- 绑定套接字的IP和端口号——Bind()
- 使套接字处于监听状态等待客户端的连接请求——Listen()
- 当请求到来后,使用BeginAccept()和EndAccept()方法接受请求,返回新的套接字
- 使用BeginSend()/EndSend和BeginReceive()/EndReceive()两组方法与客户端进行收发通信
- 返回,再次等待新的连接请求
- 关闭套接字
代码示例
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Net;
- using System.Net.Sockets;
-
- namespace AsyncServer
- {
- public class AsyncTCPServer
- {
- public void Start()
- {
-
- IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
- Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-
- socket.Bind(ipe);
-
- socket.Listen(10);
-
- AsyncAccept(socket);
- }
-
-
-
-
-
- private void AsyncAccept(Socket socket)
- {
- socket.BeginAccept(asyncResult =>
- {
-
- Socket client = socket.EndAccept(asyncResult);
- Console.WriteLine(string.Format("客户端{0}请求连接...", client.RemoteEndPoint));
- AsyncSend(client, "服务器收到连接请求");
- AsyncSend(client, string.Format("欢迎你{0}",client.RemoteEndPoint));
- AsyncReveive(client);
- }, null);
- }
-
-
-
-
-
- private void AsyncReveive(Socket socket)
- {
- byte[] data = new byte[1024];
- try
- {
-
- socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
- asyncResult =>
- {
- int length = socket.EndReceive(asyncResult);
- Console.WriteLine(string.Format("客户端发送消息:{0}", Encoding.UTF8.GetString(data)));
- }, null);
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
-
-
-
-
-
-
- private void AsyncSend(Socket client, string p)
- {
- if (client == null || p == string.Empty) return;
-
- byte[] data = new byte[1024];
- data = Encoding.UTF8.GetBytes(p);
- try
- {
-
- client.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
- {
-
- int length = client.EndSend(asyncResult);
-
- Console.WriteLine(string.Format("服务器发出消息:{0}", p));
- }, null);
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
- }
- }
- }
客户端
基本流程
- 创建套接字并保证与服务器的端口一致
- 使用BeginConnect()和EndConnect()这组方法向服务端发送连接请求
- 使用BeginSend()/EndSend和BeginReceive()/EndReceive()两组方法与服务端进行收发通信
- 关闭套接字
代码示例
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Net;
- using System.Net.Sockets;
-
- namespace AsyncClient
- {
- public class AsyncTCPClient
- {
-
-
-
- public void AsynConnect()
- {
-
- IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
-
- Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-
- client.BeginConnect(ipe, asyncResult =>
- {
- client.EndConnect(asyncResult);
-
- AsynSend(client,"你好我是客户端");
- AsynSend(client, "第一条消息");
- AsynSend(client, "第二条消息");
-
- AsynRecive(client);
- }, null);
- }
-
-
-
-
-
-
- public void AsynSend(Socket socket, string message)
- {
- if (socket == null || message == string.Empty) return;
-
- byte[] data = Encoding.UTF8.GetBytes(message);
- try
- {
- socket.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
- {
-
- int length = socket.EndSend(asyncResult);
- Console.WriteLine(string.Format("客户端发送消息:{0}", message));
- }, null);
- }
- catch (Exception ex)
- {
- Console.WriteLine("异常信息:{0}", ex.Message);
- }
- }
-
-
-
-
-
- public void AsynRecive(Socket socket)
- {
- byte[] data = new byte[1024];
- try
- {
-
- socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
- asyncResult =>
- {
- int length = socket.EndReceive(asyncResult);
- Console.WriteLine(string.Format("收到服务器消息:{0}", Encoding.UTF8.GetString(data)));
- AsynRecive(socket);
- }, null);
- }
- catch (Exception ex)
- {
- Console.WriteLine("异常信息:", ex.Message);
- }
- }
- }
- }
从总体上来讲Socket异步编程的逻辑性更加明确了,因为我们只需要为每一个过程写好回调函数就好了。那么这个示例的效果如何呢?我们来看看它的演示效果:Socket异步编程效果演示:
Server服务器端:
Client客户端:

总结
和Socket同步编程的案例相比,今天的这个案例可能只是对Socket异步编程内容的一个简单应用,因为博主到现在为止都还没有写出一个可以进行交互聊天的程序来。在Socket的异步编程中,服务端不需要为一个客户端单独创建一个线程来维护其连接。我们知道Socket异步编程中的方法是成对出现的,每一个方法都有一个回调函数,对于回调函数,这里有两种写法,以BeginConnect方法为例:
- m_Socket.BeginConnect(this.m_ipEndPoint,
- new AsyncCallback(this.ConnectCallBack),
- this.m_Socket);
或者
- m_Socket.BeginConnect(this.m_ipEndPoint,asyncResult=>
- {
-
- },null)
博主为什么要在这里说这两种写法呢,有两个原因:
* 第二种写法更为简洁,无需去构造容器传递Socket和消息,因为它们都是局部变量。如果我们使用第一种方法,因为主函数和回调函数是两个不同的函数,因此如果想要共享变量就需要通过IAsyncResult接口来访问容器中的值,这样显然增加了我们的工作量。
* 第二种写法更为优雅,这是C#语言中某种高级语法,asyncResult=> 是使用了委托的lambda表达式。
综合以上两个观点,还是建议大家使用第二种写法。
看完了C#中Socket通信编程的异步实现,如果对《C#中Socket通信编程的同步实现》感兴趣的,也可以看看。