Unity 与 .net core 服务器(C#) Socket通信

前言

本来按着前面是想用nodejs来做游戏服务器的,但是后面查资料,看着看着,好像发现一个新东西.net core。似乎nodejs对帧同步这种要求服务器运算量较大的,不算很好(但是可以nodejs + c++),而且既然.net core是最新的技术,那就与时俱进的研究看看。(目前对服务器不太了解,对.net core也不了解,自己找的资料也不多,有错误请大神指点一二),本文还是从与unity的socket通信入手,效果图如下:

Unity 与 .net core 服务器(C#) Socket通信_第1张图片


资料

.net core 官网:https://dotnet.github.io/

socket连接资料:https://blog.csdn.net/linshuhe1/article/details/51386559


服务器

首先,我们根据官网的下载并安装好.net core(官网说的很详细,这里就不累赘了)。然后我们打开vs新建一个.net core控制台应用,用来做我们的服务器(vs要事先安装好.net core相关库)

Unity 与 .net core 服务器(C#) Socket通信_第2张图片


封装字节流(byte[])操作类

在网络通讯中,数据在网络传输的格式必须以字节流的形式进行,因此需要我们对字节流进行写入和读出的操作,下面将会封装两个类,用来将各种类型的数据写入字节流,和从字节流中读取各种类型的数据。如下

读取类:NetBufferReader

using System;
using System.IO;
using System.Text;

namespace Tool {
    class NetBufferReader {

        MemoryStream m_stream = null;
        BinaryReader m_reader = null;

        ushort m_dataLength;

        public NetBufferReader(byte[] data) {
            if(data != null) {
                m_stream = new MemoryStream(data);
                m_reader = new BinaryReader(m_stream);

                m_dataLength = ReadUShort();
            }
        }

        public byte ReadByte() {
            return m_reader.ReadByte();
        }

        public int ReadInt() {
            return m_reader.ReadInt32();
        }

        public uint ReadUInt() {
            return m_reader.ReadUInt32();
        }

        public short ReadShort() {
            return m_reader.ReadInt16();
        }

        public ushort ReadUShort() {
            return m_reader.ReadUInt16();
        }

        public long ReadLong() {
            return m_reader.ReadInt64();
        }

        public ulong ReadULong() {
            return m_reader.ReadUInt64();
        }

        public float ReadFloat() {
            byte[] temp = BitConverter.GetBytes(m_reader.ReadSingle());
            Array.Reverse(temp);
            return BitConverter.ToSingle(temp, 0);
        }

        public double ReadDouble() {
            byte[] temp = BitConverter.GetBytes(m_reader.ReadDouble());
            Array.Reverse(temp);
            return BitConverter.ToDouble(temp, 0);
        }

        public string ReadString() {
            ushort len = ReadUShort();
            byte[] buffer = new byte[len];
            buffer = m_reader.ReadBytes(len);
            return Encoding.UTF8.GetString(buffer);
        }

        public byte[] ReadBytes() {
            int len = ReadInt();
            return m_reader.ReadBytes(len);
        }

        public void Close() {
            if(m_reader != null) {
                m_reader.Close();
            }
            if(m_stream != null) {
                m_stream.Close();
            }
            m_reader = null;
            m_stream = null;
        }
    }
}

写入类:NetBufferWriter

using System;
using System.IO;
using System.Text;

namespace Tool {
    class NetBufferWriter {
        MemoryStream m_stream = null;
        BinaryWriter m_writer = null;

        int m_finishLength;
        public int finishLength {
            get { return m_finishLength; }
        }

        public NetBufferWriter() {
            m_finishLength = 0;
            m_stream = new MemoryStream();
            m_writer = new BinaryWriter(m_stream);
        }

        public void WriteByte(byte v) {
            m_writer.Write(v);
        }

        public void WriteInt(int v) {
            m_writer.Write(v);
        }

        public void WriteUInt(uint v) {
            m_writer.Write(v);
        }

        public void WriteShort(short v) {
            m_writer.Write(v);
        }

        public void WriteUShort(ushort v) {
            m_writer.Write(v);
        }

        public void WriteLong(long v) {
            m_writer.Write(v);
        }

        public void WriteULong(ulong v) {
            m_writer.Write(v);
        }

        public void WriteFloat(float v) {
            byte[] temp = BitConverter.GetBytes(v);
            Array.Reverse(temp);
            m_writer.Write(BitConverter.ToSingle(temp, 0));
        }

        public void WriteDouble(double v) {
            byte[] temp = BitConverter.GetBytes(v);
            Array.Reverse(temp);
            m_writer.Write(BitConverter.ToDouble(temp, 0));
        }

        public void WriteString(string v) {
            byte[] bytes = Encoding.UTF8.GetBytes(v);
            m_writer.Write((ushort)bytes.Length);
            m_writer.Write(bytes);
        }

        public void WriteBytes(byte[] v) {
            m_writer.Write(v.Length);
            m_writer.Write(v);
        }

        public byte[] ToBytes() {
            m_writer.Flush();
            return m_stream.ToArray();
        }

        public void Close() {
            m_writer.Close();
            m_stream.Close();
            m_writer = null;
            m_stream = null;
        }

        /// 
        /// 将已写入的数据流,封装成一个新的数据流(现有数据长度+现有数据)
        /// 数据转换,网络发送需要两部分数据,一是数据长度,二是主体数据
        /// 
        public byte[] Finish() {
            byte[] message = ToBytes();
            MemoryStream ms = new MemoryStream();
            ms.Position = 0;
            BinaryWriter writer = new BinaryWriter(ms);
            writer.Write((ushort)message.Length);
            writer.Write(message);
            writer.Flush();
            byte[] result = ms.ToArray();
            m_finishLength = result.Length;
            return result;
        }
    }
}

服务器socket逻辑

创建好上面的类后,我们继续在入口类Program中创建Socket服务器的逻辑,大致的逻辑如下,首先启动一个服务器socket,然后创建一个线程,用于监听客户端的连接,每当有一个客户端连接的时候,创建一个socket对象存放在List中,并启动一个新的线程,用来发送和接收数据。代码如下:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Tool;

namespace MyApp {

    class Program {

        static byte[] m_result = new byte[1024];//存放接收的数据
        const int m_port = 8078;//端口号
        static string m_localIp = "127.0.0.1";
        static Socket m_serverSocket;//服务器socket
        static List m_clientSocketList = new List();//存放连接上的的客户端服务器

        static void Main(string[] args) {
            IPAddress ipAddress = IPAddress.Parse(m_localIp);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, m_port);
            //创建服务器Socket对象,并设置相关属性  
            m_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定ip和端口  
            m_serverSocket.Bind(ipEndPoint);
            //设置最长的连接请求队列长度  
            m_serverSocket.Listen(10);
            Console.WriteLine("启动监听{0}成功", m_serverSocket.LocalEndPoint.ToString());
            //在新线程中监听客户端的连接  
            Thread thread = new Thread(ClientConnectListen);
            thread.Start();
            Console.ReadLine();
        }

        ///   
        /// 客户端连接请求监听  
        ///   
        static void ClientConnectListen() {
            while(true) {
                //为新的客户端连接创建一个Socket对象  
                Socket clientSocket = m_serverSocket.Accept();
                m_clientSocketList.Add(clientSocket);
                Console.WriteLine("客户端{0}成功连接", clientSocket.RemoteEndPoint.ToString());

                //向连接的客户端发送连接成功的数据
                NetBufferWriter writer = new NetBufferWriter();
                writer.WriteString("Connected Server Success");
                clientSocket.Send(writer.Finish());

                //每个客户端连接创建一个线程来接受该客户端发送的消息  
                Thread thread = new Thread(RecieveMessage);
                thread.Start(clientSocket);
            }
        }

        ///   
        /// 接收指定客户端Socket的消息  
        ///   
        static void RecieveMessage(object clientSocket) {
            Socket mClientSocket = (Socket)clientSocket;
            while(true) {
                try {
                    int receiveNumber = mClientSocket.Receive(m_result);
                    Console.WriteLine("接收客户端{0}消息, 长度为{1}", mClientSocket.RemoteEndPoint.ToString(), receiveNumber);
                    
                    if(receiveNumber == 0) {
                        //断开连接
                        Console.WriteLine("连接已断开{0}", mClientSocket.RemoteEndPoint.ToString());
                        RemoveClientSocket(mClientSocket);
                        break;
                    }

                    NetBufferReader reader = new NetBufferReader(m_result);
                    string data = reader.ReadString();
                    Console.WriteLine("数据内容:{0}", data);

                    //给所有连接上的客户端返回数据
                    NetBufferWriter writer = new NetBufferWriter();
                    writer.WriteString("Get Message:" + data);
                    byte[] buffer = writer.Finish();
                    foreach(Socket socket in m_clientSocketList) {
                        socket.Send(buffer);
                    }

                } catch(Exception ex) {
                    Console.WriteLine(ex.Message);
                    RemoveClientSocket(mClientSocket);
                    break;
                }
            }
        }

        static void RemoveClientSocket(Socket clientSocket) {
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
            m_clientSocketList.Remove(clientSocket);
        }
    }
}

接着,我们点击vs的运行按钮,就可以启动我们的服务器了

Unity 与 .net core 服务器(C#) Socket通信_第3张图片


客户端

接着,我们开始创建客户端,创建一个新的unity工程,然后将之前服务器用到的字节流的读写类NetBufferWriter和NetBufferReader也导入到工程中。然后创建一个单例类来管理socket,连接我们创建的服务器的ip和端口号,并且发送和接收消息,如下

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

namespace Tool {

    public class ClientSocket : SingleClass {

        public delegate void OnGetReceive(string message);//接收到消息的委托
        public OnGetReceive onGetReceive;

        byte[] m_result = new byte[1024];
        Socket m_clientSocket;

        //是否已连接的标识  
        public bool isConnected {
            get {
                return m_clientSocket.Connected;
            }
        }

        public ClientSocket() {
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        ///   
        /// 连接指定IP和端口的服务器  
        ///   
        ///   
        ///   
        public void ConnectServer(string ip, int port) {
            IPAddress ipAddress = IPAddress.Parse(ip);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

            try {
                m_clientSocket.Connect(ipEndPoint);
                Debug.Log("连接服务器成功");
                Thread thread = new Thread(RecieveMessage);
                thread.Start();
            } catch {
                Debug.Log("连接服务器失败");
                return;
            }
        }

        ///   
        /// 发送数据给服务器  
        ///   
        public void SendMessage(string data) {
            if(isConnected == false) {
                return;
            }
            try {
                NetBufferWriter writer = new NetBufferWriter();
                writer.WriteString(data);
                m_clientSocket.Send(writer.Finish());
            } catch {
                Disconnect();
            }
        }

        ///   
        /// 发送数据给服务器  
        ///   
        public void Disconnect() {
            if(isConnected) {
                m_clientSocket.Shutdown(SocketShutdown.Both);
                m_clientSocket.Close();
            }
        }

        ///   
        /// 接收服务器端Socket的消息  
        ///   
        void RecieveMessage() {
            while(isConnected) {
                try {
                    int receiveLength = m_clientSocket.Receive(m_result);
                    NetBufferReader reader = new NetBufferReader(m_result);
                    string data = reader.ReadString();
                    Debug.Log("服务器返回数据:" + data);
                    if(onGetReceive != null) {
                        onGetReceive(data);
                    }

                } catch(Exception ex) {
                    Console.WriteLine(ex.Message);
                    Disconnect();
                    break;
                }
            }
        }
    }
}

父类为单例工具类

namespace Tool {
    public abstract class SingleClass where T : new() {
        private static T m_instance;
        public static T instance {
            get {
                if (m_instance == null) {
                    m_instance = new T();
                }
                return m_instance;
            }
        }
    }
}

接下来,我们写UI逻辑,先添加UI控件,然后创建一个新类给这些控件添加逻辑

Unity 与 .net core 服务器(C#) Socket通信_第4张图片

using Tool;
using UnityEngine;
using UnityEngine.UI;

namespace Examples {

	public class TestSocket : MonoBehaviour {

        [SerializeField] Button m_connectBtn;
        [SerializeField] InputField m_input;
        [SerializeField] Button m_sendBtn;
        [SerializeField] Button m_disconnectBtn;
        [SerializeField] Text m_receiveText;

        string m_receiveMessage = "wait...";

        void Start () {
            m_connectBtn.onClick.AddListener(SocketConnect);
            m_sendBtn.onClick.AddListener(SocketSendMessage);
            m_disconnectBtn.onClick.AddListener(SocketDisconnect);

            ClientSocket.instance.onGetReceive = ShowReceiveMessage;
        }
		
		void SocketConnect() {
            ClientSocket.instance.ConnectServer("127.0.0.1", 8078);
        }

        void SocketSendMessage() {
            string content = m_input.text;
            if(!string.IsNullOrEmpty(content)) {
                ClientSocket.instance.SendMessage(content);
            }
        }

        void SocketDisconnect() {
            ClientSocket.instance.Disconnect();
            m_receiveMessage = "已断开连接";
        }

        void ShowReceiveMessage(string message) {
            m_receiveMessage = message;
        }

        void Update() {
            m_receiveText.text = m_receiveMessage;
        }
    }
}
绑定好脚本后即可开始运行测试了,效果图见开头。

你可能感兴趣的:(帧同步)