Unity C# 网络学习(二)

27 篇文章 0 订阅

Unity C# 网络学习(二)

一.Socket的重要API

public class Lesson05 : MonoBehaviour
{
    private void Start()
    {
        var tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        var udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //是否连接
        Debug.Log(tcpSocket.Connected);
        //获取套接字的一些初始化的属性
        Debug.Log(tcpSocket.SocketType);
        Debug.Log(tcpSocket.ProtocolType);
        Debug.Log(tcpSocket.AddressFamily);
        //从网络中获取准备读取的数据数据量
        Debug.Log(tcpSocket.Available);
        //获取本机的EndPoint
        Debug.Log(tcpSocket.LocalEndPoint);
        //获取远程的EndPoint
        Debug.Log(tcpSocket.RemoteEndPoint);

        //用于服务器
        tcpSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
        tcpSocket.Listen(10);
        tcpSocket.Accept();
        //客户端
        tcpSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
        //通用
        //主要TCP发送
        tcpSocket.Send(new byte[10]);
        //主要UDP发送
        //tcpSocket.SendTo(new byte[10]);
        //接收
        //tcpSocket.Receive();
        tcpSocket.Shutdown(SocketShutdown.Both);
        tcpSocket.Close();
    }
}

二.实现一个最简单的服务端

namespace TcpServer
{
    class Program
    {
        static void Main(string[] args)
        {
            var serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
            }
            catch (Exception e)
            {
                Console.WriteLine("绑定失败!" + e.Message);
                return;
            }
            serverSocket.Listen(10);
            Console.WriteLine("等待玩家接入!");
            var clientSocket = serverSocket.Accept();
            Console.WriteLine("有客户端接入了!!!");
            clientSocket.Send(Encoding.UTF8.GetBytes("欢迎接入服务器!"));
            var buffer = new byte[1024];
            var num = clientSocket.Receive(buffer);
            Console.WriteLine($"接收到了{0}的消息:{1}", clientSocket.RemoteEndPoint, Encoding.UTF8.GetString(buffer, 0, num));
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
            Console.WriteLine("按任意键退出");
            Console.ReadKey();
        }
    }
}

三.实现一个最简单的客户端

public class Lesson06 : MonoBehaviour
{
    private void Start()
    {
        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            socket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
        }
        catch (Exception e)
        {
            Debug.Log(e);
            return;
        }
        var buffer = new byte[1024];
        var num = socket.Receive(buffer);
        Debug.Log("收到服务器的消息:" + Encoding.UTF8.GetString(buffer, 0, num));
        socket.Send(Encoding.UTF8.GetBytes("你好,我是客户端"));
        
        socket.Shutdown(SocketShutdown.Both);
        socket.Close();
    }
}

四.优化服务端

  • 由于之前的服务端只能够接收一次客户端发来的消息,并且只能发送一次消息,这样并不能在实际开发中使用,所以要为服务端进行优化
  • 用多线程处理客户端连接,接收消息,处理消息
namespace TcpServerPlus
{
    class Program
    {
        private static Socket _serverSocket;
        private static List<Socket> _allClientSocketList = new List<Socket>();
        static void Main(string[] args)
        {
            _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                _serverSocket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080));
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return;
            }
            _serverSocket.Listen(10);

            Thread acceptThread = new Thread(AcceptClientConnect);
            acceptThread.Start();

            Thread reciveThread = new Thread(ReciveClientMsg);
            reciveThread.Start();

            while (true)
            {
                var inputStr = Console.ReadLine();
                if (inputStr == "Quit")
                {
                    for (int i = 0; i < _allClientSocketList.Count; i++)
                    {
                        _allClientSocketList[i].Shutdown(SocketShutdown.Both);
                        _allClientSocketList[i].Close();
                    }
                    break;
                }
                else if (inputStr.StartsWith("S:", StringComparison.OrdinalIgnoreCase))
                {
                    for (int i = 0; i < _allClientSocketList.Count; i++)
                    {
                        _allClientSocketList[i].Send(Encoding.UTF8.GetBytes(inputStr.Substring(2)));
                    }
                }
            }
        }
        static void AcceptClientConnect()
        {
            while (true)
            {
                var clientSocket = _serverSocket.Accept();
                _allClientSocketList.Add(clientSocket);
                clientSocket.Send(Encoding.UTF8.GetBytes("欢迎连接服务器!"));
            }
        }
        static void ReciveClientMsg()
        {
            Socket clientSocket;
            byte[] buffer = new byte[1024 * 1024];
            int num = 0;
            while (true)
            {
                for (int i = 0; i < _allClientSocketList.Count; i++)
                {
                    clientSocket = _allClientSocketList[i];
                    if (clientSocket.Available > 0)
                    {
                        //用线程池处理逻辑,防止数据量太大,卡顿主线程
                        num = clientSocket.Receive(buffer);
                        ThreadPool.QueueUserWorkItem(HandleMsg, (clientSocket, Encoding.UTF8.GetString(buffer, 0, num)));
                    }
                }
            }
        }
        static void HandleMsg(object state)
        {
            (Socket s, string str) info = ((Socket s, string str))state;
            Console.WriteLine($"收到客户端{info.s.RemoteEndPoint}发来的消息:{info.str}");
        }
    }
}

五.继续优化服务器

  • 虽然上面的代码以及可以实现功能了,但是不够面向对象,可以进一步优化
namespace TcpServerPlusPlus
{
    class ClientSocket
    {
        public int id;
        public static int BEGIN_ID = 1;
        public Socket socket;
        public ClientSocket(Socket socket)
        {
            this.socket = socket;
            id = BEGIN_ID++;
        }
        public void SendMsg(string str)
        {
            try
            {
                socket.Send(Encoding.UTF8.GetBytes(str));
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return;
            }
        }
        public void ReceiveMsg()
        {
            if (socket == null)
                return;
            try
            {
                if (socket.Available > 0)
                {
                    var buffer = new byte[1024 * 5];
                    var num = socket.Receive(buffer);
                    ThreadPool.QueueUserWorkItem(MsgHandle,Encoding.UTF8.GetString(buffer,0,num));
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
        public void Close()
        {
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
            socket = null;
        }
        private void MsgHandle(object state)
        {
            var info = state as string;
            Console.WriteLine($"收到客户端{socket.RemoteEndPoint}发来的消息:{info}");
        }
    }
}
namespace TcpServerPlusPlus
{
    class ServerSocket
    {
        public Socket socket;
        private Dictionary<int, ClientSocket> _allID2ClientDic = new Dictionary<int, ClientSocket>();
        public void Connent(string ip,int host,int maxNum)
        {
            if (socket != null)
                return;
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                socket.Bind(new IPEndPoint(IPAddress.Parse(ip), host));
                socket.Listen(maxNum);
                ThreadPool.QueueUserWorkItem(Accept);
                ThreadPool.QueueUserWorkItem(Receive);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

        }
        public void Broadcast(string info)
        {
            foreach (var clientSocket in _allID2ClientDic.Values)
                clientSocket.SendMsg(info);
        }
        public void Close()
        {
            foreach (var clientSocket in _allID2ClientDic.Values)
                clientSocket.Close();
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
            socket = null;
        }
        private void Receive(object state)
        {
            while (true)
            {
                try
                {
                    if (_allID2ClientDic.Count > 0)
                    {
                        foreach (var clientSocket in _allID2ClientDic.Values)
                            clientSocket.ReceiveMsg();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    return;
                }
            }
        }
        private void Accept(object state)
        {
            while (true)
            {
                try
                {
                    var clientSocket = socket.Accept();
                    var newClientSocket = new ClientSocket(clientSocket);
                    _allID2ClientDic.Add(newClientSocket.id,newClientSocket);
                    newClientSocket.SendMsg("连接成功!");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
        }
    }
}
namespace TcpServerPlusPlus
{
    class Program
    {
        static void Main(string[] args)
        {
            var serverSocket = new ServerSocket();
            serverSocket.Connent("127.0.0.1", 8080, 10);
            Console.WriteLine("服务器开启成功!!!");
            while (true)
            {
                var inputStr = Console.ReadLine();
                if (inputStr == "Quit")
                {
                    serverSocket.Close();
                    break;
                }
                else if (inputStr.StartsWith("S:", StringComparison.OrdinalIgnoreCase))
                {
                    serverSocket.Broadcast(inputStr.Substring(2));
                }
            }
        }
    }
}

六.编写客户端

public class NetMgr : MonoBehaviour
{
    private static NetMgr _instance;
    public static NetMgr Instance => _instance;
    private Socket _socket;
    private readonly Queue<string> _sendMsgQueue = new Queue<string>();
    private static readonly object SendMsgQueueLock = new object();
    private readonly Queue<string> _receiveMsgQueue = new Queue<string>();
    private static readonly object ReceiveMsgQueueLock = new object();
    private bool _isClose = false;
    private readonly byte[] _receiveMsgBuffer = new byte[1024 * 1024];
    private int _receiveMsgLength = 0;
    private const int UpdateCheckCount = 5;
    private int _currUpdateCheckCount = 0;

    private void Awake()
    {
        _instance = this;
    }
    private void Update()
    {
        while (_currUpdateCheckCount<UpdateCheckCount)
        {
            _currUpdateCheckCount++;
            lock (ReceiveMsgQueueLock)
            {
                if (_receiveMsgQueue.Count<=0)
                {
                    _currUpdateCheckCount = 0;
                    break;
                }
                string msg = _receiveMsgQueue.Dequeue();
                Debug.Log(msg);
            }
        }
    }
    public void Connect(string ip, int port)
    {
        if (_socket is {Connected: true})
            return;
        _isClose = false;
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            _socket.Connect(new IPEndPoint(IPAddress.Parse(ip),port));
            ThreadPool.QueueUserWorkItem(CheckSendMsgQueue);
            ThreadPool.QueueUserWorkItem(CheckReceiveMsgQueue);
        }
        catch (Exception e)
        {
            Debug.Log(e);
        }
    }
    public void SendMsg(string msg)
    {
        lock (SendMsgQueueLock)
        {
            _sendMsgQueue.Enqueue(msg);
        }
    }
    private void CheckSendMsgQueue(object obj)
    {
        while (!_isClose)
        {
            try
            {
                lock (SendMsgQueueLock)
                {
                    if (_sendMsgQueue.Count>0)
                    {
                        _socket.Send(Encoding.UTF8.GetBytes(_sendMsgQueue.Dequeue()));
                    }
                }
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return;
            }
        }
    }
    private void CheckReceiveMsgQueue(object obj)
    {
        while (!_isClose)
        {
            try
            {
                if (_socket.Available > 0)
                {
                    _receiveMsgLength = _socket.Receive(_receiveMsgBuffer);
                    lock (ReceiveMsgQueueLock)
                    {
                        _receiveMsgQueue.Enqueue(Encoding.UTF8.GetString(_receiveMsgBuffer,0,_receiveMsgLength));
                    }
                }
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return;
            }
        }
    }
    public void Close()
    {
        _isClose = true;
        if (_socket!=null)
        {
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
        }
        _socket = null;
    }
    private void OnDestroy()
    {
        Close();
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值