Week 08-day02-Unity网络通讯之聊天室

一、搭建Unity聊天室UI界面

简单UI拖拽一下:

4f2c85b6eae54d809b5dadef6933757c.png

 聊天室实现步骤:
1.向服务器发送消息
2.刷新Content聊天界面
3.清空输入框

将InputField中输入的消息发给服务器

 代码:

chatPanel:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class ChatPanel : MonoBehaviour
{
    StringBuilder stringBuilder=new StringBuilder("");
    TMP_InputField TMP_input;
    Button button;
    InputField inputfield;
    Text content;

    //获取相关的组件
    private void Awake()
    {
        TMP_input = transform.Find("InputField").GetComponent<TMP_InputField>();
        content = GameObject.Find("Content").GetComponent<Text>();
        button = transform.Find("Btn_Send").GetComponent<Button>();
        button.onClick.AddListener(OnSend);
    }

    // Start is called before the first frame update
    void Start()
    {
        
        
    }


    private void OnSend()
    {
        if (string.IsNullOrEmpty(TMP_input.text))
        {
            return;
        }
        Debug.Log(TMP_input.text);
        //将内容发送给服务器
        NetManager.Instance.SendMessageToServer(TMP_input.text);
        //刷新界面
        UpdateContent(TMP_input.text);
        //将输入框清空
        TMP_input.text = "";
    }

    void UpdateContent(string message)
    {
        stringBuilder.AppendLine(message);
        content.text = stringBuilder.ToString();

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

问题:为什么要使用到StringBuilder,有什么好处?StringBuilder常见的API有哪些?

NetManager:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class NetManager : MonoBehaviour
{
    //单例
    private static NetManager _instance = null;

    public static NetManager Instance
    {
        get
        {
            return _instance;
        }
    }
    // Start is called before the first frame update
    //客户端连接服务器
    //1.连接到服务器
    //2.接受服务器的消息,并处理
    //3.向服务器发送消息
    byte[] msgArr = new byte[1024];
    Socket clientSocket = null;
    
    //消息队列,用于处理消息
    Queue<string> messageQueue = new Queue<string>();

    private void Awake()
    {
        _instance = this; 
    }
    
    void Start()
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        clientSocket.Connect(new IPEndPoint(IPAddress.Parse(Protocol.ProtocolConfig.ip), Protocol.ProtocolConfig.port));
        Debug.Log("客户端开始连接服务器!");
        try
        {
            clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);
        }
        catch (System.Exception e)
        {
            Debug.Log(e.Message);
            
        }

    }

    //接受服务器的消息
    private void AsyncReceive(IAsyncResult ar)
    {
        Socket clientSocket = ar.AsyncState as Socket;
        if (clientSocket == null)
        {
            return;
        }
        int length = clientSocket.EndReceive(ar);
        string message = Encoding.UTF8.GetString(msgArr, 0, length);//接受的消息,思考为啥不在这个函数里面处理消息
                                                                    //异步的本质是一条多线程,很多Unity的API没有办法在支线程中使用
        messageQueue.Enqueue(message);

        clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);

        
    }

    public void SendMessageToServer(string message)
    {
        clientSocket.Send(Encoding.UTF8.GetBytes(message));
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            SendMessageToServer("helloWorld");
        }

        if (messageQueue.Count>0)
        {
            string message = messageQueue.Dequeue();

            //处理这个消息
            Debug.Log(message);
        }
    }
}

加上服务器运行截图:

09f1b178b7f84230831391c7f56d629c.png

二、消息号改写聊天室

4338a99501d64c8d9309fbed734c0b40.png

代码:

服务端:

动态链接库:ProtocolConfig:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Protocol
{
    //class,struct,interface默认的访问权限修饰符为internal 
    public class ProtocolConfig
    {
        public const string ip = "192.168.1.";
        public const int port = 8899;
    }
}

动态链接库:ProtocolEnum:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Protocol
{
    public enum ProtocolEnum
    {
        loginReq=1,
        loginRep,
        registerReq,
        registerRep,
        updateChat,

    }

}

动态链接库:SocketMessage:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Protocol
{
    public class SocketMessage
    {
        public ProtocolEnum messageId;
        public string message;

        public SocketMessage(ProtocolEnum messageId, string message)
        {
            this.messageId = messageId;
            this.message = message;
        }

        public byte[] PackMessage()
        {
            byte[] idArr = BitConverter.GetBytes((int)messageId);
            byte[] msgArr = Encoding.UTF8.GetBytes(message);

            byte[] result = idArr.Concat(msgArr).ToArray();
            return result;
        }

        public static SocketMessage Parse(byte[] msgArr)
        {
            ProtocolEnum _messageId = (ProtocolEnum)BitConverter.ToInt32(msgArr, 0);
            string _message = Encoding.UTF8.GetString(msgArr, 4, msgArr.Length - 4);

            return new SocketMessage(_messageId,_message);
        }

    }
}

Main函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Protocol;

namespace AsyncChatServer
{
    class Program
    {
       
        static void Main(string[] args)
        {
            try
            {
                Socket Serversocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                
                Serversocket.Bind(new IPEndPoint(IPAddress.Parse(ProtocolConfig.ip),ProtocolConfig.port));
                Serversocket.Listen(10);

                Console.WriteLine("服务器启动了");
                
                
                Serversocket.BeginAccept(AsyncAccept, Serversocket);
            }
            catch (Exception e)
            {
                Console.WriteLine("1"+e.Message);
            }

            Console.ReadKey();

        }

        static byte[] msgarr = new byte[1024];

        private static void AsyncAccept(IAsyncResult ar)
        {
            Socket serversocket = ar.AsyncState as Socket;
            if (serversocket == null)
            {
                return;
            }
            Socket clientsocket = serversocket.EndAccept(ar);

            ClientPeer clientPeer = new ClientPeer(clientsocket);
            NetManager.Instance.AddClient(clientPeer);
            Console.WriteLine(NetManager.Instance); 
            Console.WriteLine(clientsocket.RemoteEndPoint.ToString());
            //clientsocket.beginreceive(msgarr, 0, msgarr.length, socketflags.none, asyncreveive, clientsocket);

            serversocket.BeginAccept(AsyncAccept, serversocket);


        }
    }
}

NetManager:

using Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncChatServer
{

    class NetManager
    {
        private static NetManager _instance = null;
        private NetManager()
        {
            //开启一条线程处理消息队列中的消息
            Thread thread = new Thread(DealWithMessage);
            thread.Start();
        }

        public static NetManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new NetManager();
                }
                return _instance;
            }
        }

        //消息的广播:需要使用List容器存储与客户端建立的连接
        private static List<ClientPeer> clientList = new List<ClientPeer>();

        //消息队列
        Queue<ClientMessage> messageQueue = new Queue<ClientMessage>();

        public void AddClient(ClientPeer client)
        {
            clientList.Add(client);
        }

        public void RemoveClient(ClientPeer client)
        {
            clientList.Remove(client);
        }

        //广播消息
        public void BoardCastMessage(ProtocolEnum messageId, string message,ClientPeer client = null)
        {
            for (int i = 0; i < clientList.Count; i++)
            {
                if (client!=null&& clientList[i] == client)
                {
                    continue;
                }
                clientList[i].SendMessage(messageId,message);
            }
        }
        public void BoardCastMessage(byte[] message, ClientPeer client = null)
        {
            for (int i = 0; i < clientList.Count; i++)
            {
                if (client != null && clientList[i] == client)
                {
                    continue;
                }
                clientList[i].SendMessage(message);
            }
        }

        public void SendMessage(byte[] message,ClientPeer client)
        {
            client.SendMessage(message);
        }

        public void SendMessage(ProtocolEnum messageId,string message, ClientPeer client)
        {

            client.SendMessage(messageId,message);
        }

        public void AddMessage(byte[] msgArr,ClientPeer clientPeer)
        {
            SocketMessage socketMessage = SocketMessage.Parse(msgArr);
            messageQueue.Enqueue(new ClientMessage(clientPeer, socketMessage));
        }

        private void DealWithMessage()
        {
            while (true)
            {
                if (messageQueue.Count > 0)
                {
                    ClientMessage clientMessage = messageQueue.Dequeue();
                    switch (clientMessage.message.messageId)
                    {
                        case ProtocolEnum.loginReq:
                            break;
                        case ProtocolEnum.loginRep:
                            break;
                        case ProtocolEnum.registerReq:
                            break;
                        case ProtocolEnum.registerRep:
                            break;
                        case ProtocolEnum.updateChat:
                            BoardCastMessage(ProtocolEnum.updateChat, clientMessage.message.message,clientMessage.client);//不给自己发消息
                            Console.WriteLine(clientMessage.message.message);
                            break;
                        default:
                            break;
                    }
                }
            }

        }

    }
}

ClientPeer类:

using Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace AsyncChatServer
{
    class ClientPeer
    {
        Socket clientSocket = null;

        static byte[] msgArr = new byte[1024];

        internal ClientPeer(Socket clientSocket)
        {
            this.clientSocket = clientSocket;

            clientSocket.BeginReceive(msgArr,0,msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);
        }

        internal void AsyncReceive(IAsyncResult ar)
        {
            try
            {
                Socket clientSocket = ar.AsyncState as Socket;
                int length = clientSocket.EndReceive(ar);
                if (clientSocket == null)
                {
                    clientSocket.Close();
                    NetManager.Instance.RemoveClient(this);
                    return;
                }


                //string message = Encoding.UTF8.GetString(msgArr,0,length);
                //Console.WriteLine(message);
                //广播
                //NetManager.Instance.BoardCastMessage(message, this);
                byte[] arr = new byte[length];
                Array.Copy(msgArr, 0, arr, 0, length);
                NetManager.Instance.AddMessage(arr,this);

                clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);
               
                
            }
            catch (Exception e)
            {
                clientSocket.Close();
                NetManager.Instance.RemoveClient(this);
                Console.WriteLine(e.Message);
                return;
            }
            
        }

        public void SendMessage(byte[] message)
        {
            clientSocket.Send(message);
        }

        public void SendMessage(ProtocolEnum messageId , string message)
        {
            SocketMessage msg = new SocketMessage(messageId, message);
            clientSocket.Send(msg.PackMessage());
        }
    }
}

ClientMessage类:

using Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AsyncChatServer
{
    /// <summary>
    /// 
    /// </summary>
    class ClientMessage
    {
        public ClientPeer client;
        public SocketMessage message;

        public ClientMessage(ClientPeer client, SocketMessage socketMessage)
        {
            this.client = client;
            this.message = socketMessage;
        }


    }
}

客户端:

ChatPanel脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class ChatPanel : MonoBehaviour
{
    StringBuilder stringBuilder=new StringBuilder("");
    TMP_InputField TMP_input;
    Button button;
    InputField inputfield;
    Text content;

    //获取相关的组件
    private void Awake()
    {
        TMP_input = transform.Find("InputField").GetComponent<TMP_InputField>();
        content = GameObject.Find("Content").GetComponent<Text>();
        button = transform.Find("Btn_Send").GetComponent<Button>();
        button.onClick.AddListener(OnSend);
        NetManager.Instance.updateChat+= UpdateContent;
        Debug.LogError(NetManager.Instance);
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }


    private void OnSend()
    {
        if (string.IsNullOrEmpty(TMP_input.text))
        {
            return;
        }
        Debug.Log(TMP_input.text);
        //将内容发送给服务器
        NetManager.Instance.SendMessageToServer(Protocol.ProtocolEnum.updateChat,TMP_input.text);

        //刷新界面
        UpdateContent(TMP_input.text);
        //将输入框清空
        TMP_input.text = "";
    }

    public void UpdateContent(string message)
    {
        stringBuilder.AppendLine(message);
        content.text = stringBuilder.ToString();

    }
    // Update is called once per frame
    void Update()
    {
        
    }

    
}

NetManager脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
using Protocol;


public class NetManager : MonoBehaviour
{
    //单例
    private static NetManager _instance = null;

    public static NetManager Instance
    {
        get
        {
            return _instance;
        }
    }
    // Start is called before the first frame update
    //客户端连接服务器
    //1.连接到服务器
    //2.接受服务器的消息,并处理
    //3.向服务器发送消息
    byte[] msgArr = new byte[1024];
    Socket clientSocket = null;

    //消息队列,用于处理消息
    Queue<SocketMessage> messageQueue = new Queue<SocketMessage>();

    //public delegate void updateClientMsgDel(string message);
    //public updateClientMsgDel del;
    public event  Action<string> updateChat;

    private void Awake()
    {
        _instance = this; 
    }
    
    void Start()
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        clientSocket.Connect(new IPEndPoint(IPAddress.Parse(Protocol.ProtocolConfig.ip), Protocol.ProtocolConfig.port));
        Debug.Log("客户端开始连接服务器!");
        try
        {
            clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);
        }
        catch (System.Exception e)
        {
            Debug.Log(e.Message);
            
        }

    }

    //接受服务器的消息
    private void AsyncReceive(IAsyncResult ar)
    {
        Socket clientSocket = ar.AsyncState as Socket;
        if (clientSocket == null)
        {
            return;
        }
        int length = clientSocket.EndReceive(ar);
        byte[] Arr = new byte[length];
        Array.Copy(msgArr, 0, Arr, 0, length);
        SocketMessage message = SocketMessage.Parse(Arr);
        //string message = Encoding.UTF8.GetString(msgArr, 0, length);//接受的消息,思考为啥不在这个函数里面处理消息
        //异步的本质是一条多线程,很多Unity的API没有办法在支线程中使用
        //updateChat(message.message);
        messageQueue.Enqueue(message);

        clientSocket.BeginReceive(msgArr, 0, msgArr.Length, SocketFlags.None, AsyncReceive, clientSocket);

        
    }

    public void SendMessageToServer(ProtocolEnum messageId, string message)
    {
        SocketMessage msg = new SocketMessage(messageId, message);
        clientSocket.Send(msg.PackMessage());

    }

    // Update is called once per frame
    void Update()
    {

        if (messageQueue.Count>0)
        {
            SocketMessage message = messageQueue.Dequeue();
            switch (message.messageId)
            {            
                case ProtocolEnum.loginRep:
                    break;
                case ProtocolEnum.registerRep:
                    break;
                case ProtocolEnum.updateChat:
                    updateChat(message.message);
                    break;
                default:
                    break;
            }
        }
    }

  
}

最后运行:

网络通讯day02

 完整项目学习视频源文件:

链接:https://张pan.baidu.com/s三/1ql3j4JXNboVKYzGn6_80tA    (把张三去掉)
提取码:wwww 
--来自百度网盘超级会员V6的分享

猜你喜欢

转载自blog.csdn.net/qq_53663718/article/details/127515988