ainatipen 发表于 2022-7-10 12:54

Unity实现服务器:1、Socket通信

最近研究了一下Unity的网络框架,踩了很多的坑,所以想在这里写篇文章作为总结,文章只针对实现,不讲过多的原理,后续会陆续更新完整的服务器框架。
要想实现服务器框架,首先就是要使用Socket进行通信,Socket通信大致逻辑:服务端暴露端口号和IP地址,客户端通过访问这个IP地址,进行数据传输,传输的数据为二进制数据。接下来为大家讲讲怎么实现。文章最后放出源码。
需要工具:unity、vs2019
1、首先在服务器端创建服务端脚本Server,我这直接用本地:
大致内容:暴露服务器ip地址、端口号、最大连接数,等待客户端连接后发送给客户端信息,接收客户端发来的信息。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Server
{
    class Program
    {
      private static string ipAdress = "127.0.0.1";//ip地址
      private static int port = 5000;//端口号
      private static int maxConnect = 20;//最大连接数
      private static Socket _serverSocket;
      private static Socket _clientSocket;
      static void Main(string[] args)
      {
            //1、创建一个服务端socket对象
            _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2、绑定一个ip和端口
            IPAddress ipaddress = IPAddress.Parse(ipAdress);//将一个字符串类型的ip地址,转换成一个IPAdress对象
            EndPoint endPoint = new IPEndPoint(ipaddress, port);
            _serverSocket.Bind(endPoint);//向操作系统申请一个可用的ip和端口号来通讯
            //3、开始监听客户端的连接请求
            _serverSocket.Listen(maxConnect);//设置最大连接数
            Console.WriteLine(string.Format("开启服务端{0}...", _serverSocket.LocalEndPoint));
            Socket clientSocket = _serverSocket.Accept();//阻塞当前线程,直到有一个客户端连接服务器
            Console.WriteLine("接受到客户端的连接请求!");

            //4、发送、接收消息
            string message = "hello client";
            byte[] data = Encoding.UTF8.GetBytes(message);//将字符串转成字节数组
            _clientSocket.Send(data);
            Console.WriteLine("服务器向客户端发送了一条消息:" + message);
            //5、接收客户端发来的信息
            byte[] data2 = new byte;//存放消息的字节数容器 1M大小
            int length = _clientSocket.Receive(data2);
            string messgae2 = Encoding.UTF8.GetString(data2, 0, length);//bytes转换成string
            Console.WriteLine("服务器接受到客户端发来的信息" + messgae2);
      }
    }
}
2、创建Unity项目,并添加Client脚本。
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class Client : MonoBehaviour
{
    private Socket tcpClient;
    private string serverIP = "127.0.0.1";//服务器ip地址
    private int serverPort = 5000;//端口号
    void Start()
    {
      //1、创建socket
      tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

      //2、建立一个连接请求
      IPAddress iPAddress = IPAddress.Parse(serverIP);
      EndPoint endPoint = new IPEndPoint(iPAddress, serverPort);
      tcpClient.Connect(endPoint);
      Debug.Log("请求服务器连接");

      //3、接受、发送消息
      byte[] data = new byte;
      int length = tcpClient.Receive(data);
      var message = Encoding.UTF8.GetString(data, 0, length);
      Debug.Log("客户端收到来自服务器发来的信息" + message);

      //发送消息
      string message2 = "Client Say To Hello";
      tcpClient.Send(Encoding.UTF8.GetBytes(message2));
      Debug.Log("客户端向服务器发送消息" + message2);
    }
}
3、开启服务器




4、开启客户端
将写好的脚本挂载在场景中


5、效果



服务端效果



客户端效果

最基本的通信已经实现,现在来开始重构一下代码。
服务器端:
将Client封装成一个类,要有初始化、开始接收、接收回调等方法
将Server封装成一个类,要有启动服务器,关闭服务器、接收信息等方法,并且还要有管理Client的方法。
客户端:
将Client封装成一个类,包括初始化、开始接收、接收回调等方法。
接下来实现:
服务器端:Server类
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Server
{
    class Server
    {
      private Socket _serverSocket;
      private int _prot;
      private int _maxClient;
      private string ipAdress;

      public List<Client> ClientList = new List<Client>();//客户端列表

      public Server(string ipAdress, int prot, int maxClient)
      {
            this.ipAdress = ipAdress;
            this._prot = prot;
            this._maxClient = maxClient;
            Start();//启动
      }

      //启动服务器
      public void Start()
      {
            try
            {
                //1、创建一个socket对象
                _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //2、绑定一个ip和端口
                IPAddress ipaddress = IPAddress.Parse(ipAdress);//将一个字符串类型的ip地址,转换成一个IPAdress对象
                EndPoint endPoint = new IPEndPoint(ipaddress, _prot);
                _serverSocket.Bind(endPoint);//向操作系统申请一个可用的ip和端口号来通讯
                _serverSocket.Listen(_maxClient);//设置最大连接数
                Console.WriteLine(string.Format("开启服务端{0}...", _serverSocket.LocalEndPoint));
                StartAccept();
            }
            catch (Exception)//出现异常则关闭服务器
            {
                //Close();
                throw;
            }
      }
      //开始接收信息
      private void StartAccept()
      {
            OnAccept();
      }

      //接收信息中
      private void OnAccept()
      {
            while (true)
            {
                Socket clientSocket = _serverSocket.Accept();
                Console.WriteLine(string.Format("客户端 {0} 连接", clientSocket.RemoteEndPoint));
                Client client = new Client(clientSocket);
                client.OnSend(Encoding.Default.GetBytes("登录成功"));
            }
      }

      //关闭所有服务器
      public void Close()
      {
            foreach (var client in ClientList)
            {
                client.Close();
            }
            _serverSocket.Close();
      }
    }
}
Client类:
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Server
{
    class Client
    {
      private Thread _recvThread;
      public Socket ClientSocket;

      public Client(Socket socket)
      {
            ClientSocket = socket;
            StartRecv();
      }

      //开始接收
      private void StartRecv()
      {
            _recvThread = new Thread(OnRecv);
            _recvThread.IsBackground = true;
            _recvThread.Start();
      }

      //接收中
      private void OnRecv()
      {
            try
            {
                while (true)
                {
                  byte[] buff = new byte;
                  ClientSocket.Receive(buff);
                  Console.WriteLine(Encoding.Default.GetString(buff));
                }
            }
            catch (Exception)
            {
                Close();
                throw;
            }
      }

      //发送信息
      public void OnSend(byte[] data)
      {
            ClientSocket.Send(data);
      }
      //关闭
      public void Close()
      {
            _recvThread.Abort();
            ClientSocket.Shutdown(SocketShutdown.Both);
            ClientSocket.Close();
      }
    }
}
Program:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Server
{
    class Program
    {
      private static Server server;
      private static void Start()
      {
            server = new Server("127.0.0.1", 5000, 20);
      }

      static void Main(string[] args)
      {
            Start();//开启服务器
      }
    }
}
客户端 SocketClient类:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

public class ClientSocket
{
    private Socket _clientSocket;
    private Thread _recvThread;
    private string _rIP;//ip地址
    private int _rProt;//端口

    public ClientSocket(string _rIP, int _rProt)
    {
      this._rIP = _rIP;
      this._rProt = _rProt;
      Connect();
    }

    //连接
    private void Connect()
    {
      try
      {
            _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _clientSocket.Connect(new IPEndPoint(IPAddress.Parse(_rIP), _rProt));
            OnSend(Encoding.Default.GetBytes("hello,server"));
            StartRecv();
      }
      catch
      {
            throw;
      }
    }
    //开始接收消息
    private void StartRecv()
    {
      _recvThread = new Thread(OnRecv);
      _recvThread.IsBackground = true;
      Debug.Log("开始接收消息");
      _recvThread.Start();
    }

    //接收中
    private void OnRecv()
    {
      while (true)
      {
            byte[] buff = new byte;
            _clientSocket.Receive(buff);
            Debug.Log(Encoding.Default.GetString(buff));
      }
    }

    //发送信息
    public void OnSend(byte[] data)
    {
      try
      {
            _clientSocket.Send(data);
      }
      catch (Exception)
      {
            Close();
            throw;
      }
    }

    //关闭
    public void Close()
    {
      _recvThread.Abort();
      _clientSocket.Shutdown(SocketShutdown.Both);
      _clientSocket.Close();
    }
}

调用:
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class Client : MonoBehaviour
{
    private Socket tcpClient;
    private string serverIP = "127.0.0.1";//服务器ip地址
    private int serverPort = 5000;//端口号
    private ClientSocket c1;
    void Start()
    {
      c1 = new ClientSocket(serverIP, serverPort);
    }
}
效果:



开启服务端



开启客户端



服务端响应

注:目前客户端若是强制退出,则会导致服务端异常。
看到这里已经能实现基本的通信了,不过存在一个问题,服务器端和客户端是通过二进制数据进行通信的,而不能够传递具体的方法,要想实现这个问题,就要使用到通信协议,也就是两边设置一样的字段,接收到的二进制信息转换成字段,就调用相应字段的方法,这种方法我会在下篇文章给出。

源码链接:https://pan.baidu.com/s/1lU3f_5aYHnt1h5AjuBitlQ
提取码:e1cm

KaaPexei 发表于 2022-7-10 12:56

大佬,爱了爱了[赞同]
页: [1]
查看完整版本: Unity实现服务器:1、Socket通信