UN2L9——服务端和客户端异步通信
服务端Socket的TCP异步通信
暂时不考虑区分消息类型,分包、黏包以及心跳消息等功能,实现一个基础的服务端
ServerClient.cs
-
clientDic
:用于管理各个连接客户端的Socket,可以通过ID来获取单个Socket
-
Start
:提供给外部开启并初始化服务端Socket的方法,使用异步方法开启监听客户端连入
-
AcceptCallBack
:监听客户端连入异步方法的回调方法,处理连接客户端的Socket,并开启下一次监听客户端连入
-
BroadCast
:让所有的ClientSocket
发送传入的广播消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| using System.Net; using System.Net.Sockets;
namespace TeachTcpServerAsync { internal class ServerSocket { private Socket? socket; private Dictionary<int, ClientSocket> clientDic = new Dictionary<int, ClientSocket>();
public void Start(string ip, int port, int num) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port); try { socket.Bind(ipPoint); socket.Listen(num); socket.BeginAccept(AcceptCallBack, null); Console.WriteLine($"服务端启动成功"); } catch (Exception e) { Console.WriteLine($"服务端启动失败:{e.Message}"); } }
private void AcceptCallBack(IAsyncResult result) { if (socket == null) return; try { Socket clientSocket = socket.EndAccept(result); ClientSocket client = new ClientSocket(clientSocket); clientDic.Add(client.clientID, client); socket.BeginAccept(AcceptCallBack, null); } catch (Exception e) { Console.WriteLine($"客户端连入失败:{e.Message}"); } }
public void Broadcast(string str) { foreach (var client in clientDic.Values) { client.Send(str); } } } }
|
ClientSocket.cs
-
CLIENT_BEGIN_ID
:连接客户端的Socket起始ID,每多一个ClientSocket
就累加1
-
clientID
:ClientSocket
的ID,便于从字典内获取
-
cacheBytes
:缓存数组
-
cacheNum
:缓存数组内有多少字节数据
-
ReceiveCallBack
:接收消息异步方法的回调方法,消息在这里处理,并开启下一次的接收消息的异步方法
-
Send
:对外开放的发送消息的方法,使用异步方法发送消息
-
SendCallBack
:异步方法发送消息的回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| using System.Net.Sockets; using System.Text;
namespace TeachTcpServerAsync { internal class ClientSocket { public Socket socket; public int clientID; private static int CLIENT_BEGIN_ID = 1;
private byte[] cacheBytes = new byte[1024]; private int cacheNum = 0;
public ClientSocket(Socket socket) { this.socket = socket; this.clientID = CLIENT_BEGIN_ID++;
this.socket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, this.socket); }
private void ReceiveCallBack(IAsyncResult result) { try { cacheNum = this.socket.EndReceive(result); Console.WriteLine(Encoding.UTF8.GetString(cacheBytes, 0, cacheNum)); cacheNum = 0; if (this.socket.Connected) this.socket.BeginReceive(cacheBytes, cacheNum, cacheBytes.Length, SocketFlags.None, ReceiveCallBack, this.socket); else Console.WriteLine($"客户端{clientID}已断开连接"); } catch (SocketException e) { Console.WriteLine($"接收消息错误[{e.ErrorCode}]:{e.Message}"); } }
public void Send(string str) { if (this.socket.Connected) { byte[] bytes = Encoding.UTF8.GetBytes(str); this.socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallBack, null); } }
private void SendCallBack(IAsyncResult result) { try { this.socket.EndSend(result); } catch (SocketException e) { Console.WriteLine($"发送失败[{e.ErrorCode}]:{e.Message}"); } } } }
|
Program.cs
-
Main
:创建并初始化服务器SocketServerSocket
,然后循环监听终端输入,输入B:
就调用服务端广播消息方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| namespace TeachTcpServerAsync { internal class Program { static void Main(string[] args) { ServerSocket serverSocket = new ServerSocket(); serverSocket.Start("127.0.0.1", 8080, 1024);
while (true) { string? input = Console.ReadLine(); if (string.IsNullOrEmpty(input)) return; if (input.Substring(0, 2) == "B:") { serverSocket.Broadcast(input.Substring(2)); } } } } }
|
客户端Socket的TCP异步通信
暂时不考虑区分消息类型,分包、黏包以及心跳消息等功能,实现一个基础的客户端
-
cacheBytes
:缓存数组
-
cacheNum
:缓存数组内有多少字节数据
-
Connent
:传入服务器的IP地址与端口号,使用异步方法连接对应的服务器,连接后就使用异步方法开始接收服务端消息
-
ReceiveCallBack
:异步接收消息的回调函数,处理消息,并开始下一次异步接收服务端消息
-
Close
:关闭并释放客户端Socket
-
Send
:将外部要发送的消息处理后,使用异步方法将字节数组发送到服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine;
public class NetAsyncMgr : MonoBehaviour { private static NetAsyncMgr instance; public static NetAsyncMgr Instance => instance; private Socket socket; private byte[] cacheBytes = new byte[1024 * 1024]; private int cacheNum = 0;
private void Awake() { instance = this; DontDestroyOnLoad(gameObject); }
public void Connent(string ip, int port) { if (socket != null && socket.Connected) return; IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.RemoteEndPoint = ipPoint; args.Completed += (socket, args) => { if (args.SocketError == SocketError.Success) { print("连接成功"); SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs(); receiveArgs.SetBuffer(cacheBytes, 0, cacheBytes.Length); receiveArgs.Completed += ReceiveCallBack; this.socket.ReceiveAsync(receiveArgs); } else { print($"连接失败:{args.SocketError}"); } }; socket.ConnectAsync(args); }
private void ReceiveCallBack(object obj, SocketAsyncEventArgs args) { if (args.SocketError == SocketError.Success) { print(Encoding.UTF8.GetString(args.Buffer, 0, args.BytesTransferred)); args.SetBuffer(0, args.Buffer.Length); if (this.socket != null && this.socket.Connected) socket.ReceiveAsync(args); else Close(); } else { print($"接收消息时出错:{args.SocketError}"); Close(); } }
public void Close() { if (socket != null) { socket.Shutdown(SocketShutdown.Both); socket.Disconnect(false); socket.Close(); socket = null; } }
public void Send(string str) { if (this.socket != null && this.socket.Connected) { byte[] bytes = Encoding.UTF8.GetBytes(str); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.SetBuffer(bytes, 0, bytes.Length); args.Completed += (socket, args) => { if (args.SocketError != SocketError.Success) { print($"发送消息失败:{args.SocketError}"); Close(); } }; this.socket.SendAsync(args); } else { Close(); } } }
|