由于平时使用Unity开发,所以相对其他的编程语言,对C#比较了解,所以写了一个C/S的Socket异步通信程序。目前只是在C#中测试,未来会接入Unity3D,这个只是我个人用来进行学习用的,所以大家不必在意使用哪个语言做后端服务器开发更好
一些说明:
- 使用最简单的TCP协议来进行通信,通信数据未做任何包装和加工(后面会接入Protobuf-net)
- 采用异步通信结构
- 为了提高服务器的性能,使用了连接池复用的方法
1.服务器连接池
class Conn { //缓存区大小 public const int BUFFER_SIZE = 1024; //socket public Socket socket; //是否被使用 public bool isUse = false; //Buff public byte[] readBuff; public int buffCount = 0; //构造函数 public Conn() { readBuff = new byte[BUFFER_SIZE]; } //初始化 public void Init(Socket socket) { this.socket = socket; isUse = true; buffCount = 0; } //缓存区剩余的字节 public int BuffRemain() { return BUFFER_SIZE - buffCount; } //获取客户端的地址 public string GetAddress() { if (!isUse) return "获取地址失败"; return socket.RemoteEndPoint.ToString(); } //关闭 public void Close() { if (isUse) return; Console.WriteLine(GetAddress() + " 断开连接"); socket.Close(); isUse = false; } }
2.服务器Socket
class UnityServer { //监听套接字 public Socket listenSocket; //客户端连接(异步) public Conn[] conns; //最大连接数 public int maxCount = 100; //获取连接池索引 public int NewIndex() { if (conns == null) return -1; for(int i=0;i<conns.Length;i++) { if (conns[i] == null) { conns[i] = new Conn(); return i; } else if(conns[i].isUse == false) { return i; } } return -1; } //开启服务器 public void Start(IPEndPoint iPEndPoint) { //连接对象池 conns = new Conn[maxCount]; for(int i=0;i<maxCount;i++) { conns[i] = new Conn(); } //socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //bind listenSocket.Bind(iPEndPoint); //listen listenSocket.Listen(maxCount); //Accept listenSocket.BeginAccept(AcceptCb, null); } //Accept回调函数 private void AcceptCb(IAsyncResult asyncResult) { try { Socket socket = listenSocket.EndAccept(asyncResult); int index = NewIndex(); if(index == -1) { Console.WriteLine("Conn连接池已满!"); } else { Conn conn = conns[index]; conn.Init(socket); string addr = conn.GetAddress(); Console.WriteLine("客户端 [" + addr + "] 连接"); Console.WriteLine("已连接 " + index + " 个客户端"); conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn); listenSocket.BeginAccept(AcceptCb, null); } } catch(Exception ex) { Console.WriteLine("AcceptCb 失败:" + ex.Message); } } //Receive回调函数 private void ReceiveCb(IAsyncResult asyncResult) { //获取接收对象 Conn conn = (Conn) asyncResult.AsyncState; try { int count = conn.socket.EndReceive(asyncResult); //关闭信号 if(count <= 0) { Console.WriteLine("收到 [" + conn.GetAddress() + "] 断开连接"); conn.Close(); return; } //数据处理 string str = conn.socket.RemoteEndPoint.ToString() +":" + Encoding.UTF8.GetString(conn.readBuff, 0, count); Console.WriteLine("[" + conn.GetAddress() + "] : " + str); byte[] sendMsg = Encoding.UTF8.GetBytes(str); //将所有的消息广播 for(int i=0;i<maxCount;i++) { if (conns[i] == null || !conns[i].isUse) continue; conns[i].socket.Send(sendMsg); } //继续接收 conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn); } catch (Exception ex) { Console.WriteLine("[" + conn.GetAddress() + "] 断开连接"); conn.Close(); } } }
3.服务器Main函数
class Program { static void Main(string[] args) { int port = 1234; IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Any, port); Console.WriteLine("服务器开启..."); UnityServer unityServer = new UnityServer(); unityServer.Start(iPEndPoint); while(true) { string input = Console.ReadLine(); if (input == "Q") return; } } }
4.客户端Socket
class Program { //客户端套接字 static Socket clientSocket; static void Main(string[] args) { string IP = "127.0.01"; IPAddress ip = IPAddress.Parse(IP); int port = 1234; clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint endPoint = new IPEndPoint(ip, port); //连接服务器 try { clientSocket.Connect(endPoint); //发送字节 string str = Console.ReadLine(); byte[] message = Encoding.UTF8.GetBytes(str); clientSocket.Send(message); //接收服务器信息 byte[] receive = new byte[1024]; clientSocket.Receive(receive); Console.WriteLine("接收消息为:" + Encoding.UTF8.GetString(receive)); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { //关闭连接 clientSocket.Close(); } Console.ReadKey(); } }
5.测试结果
启动服务器,注意端口号和IP地址
启动客户端,服务器显示有客户端连接上来(0个客户端是因为是0开始计数的)
客户端发送消息,服务器将这个消息返回到所有在线的客户端
总结:
这只是一个简单的Socket测试Demo,如果要用于真正的通信,就需要考虑封装协议、加密数据、网络异常处理、服务器负载、高并发能力等,后续会继续学习如何完成一个小型的Unity游戏服务器