UDP实现一个简易的聊天室(多个主机连接终端) 已完善

 效果展示 

 

 

网络编程 --使用UDP协议的简单聊天室

由于TCP协议是一种面向连接的协议,有三次挥手通信确认机制,且数据包没有代销限制,TCP协议确保在通信双方都收到对方确认信息的前提下,才开始为通信的双方传输信息(数据包)!

这样数据包的传输可以得到安全保障!所以简单的理解为安全的!当然一旦,出现网络问题,或者一些其他意外现象造成通信双方的连接出现异常或断开连接,使用TCP是不能传输信息的!

UDP不是一种面向连接的协议,就是说,在使用UDP协议进行数据交互的时候,彼此可以不连接,通信的一端突然地向另外一端发送数据包,但是对方没有启动接收程序,那么对方当时是收不到数据包的,或者当你发送数据的过程中,突然出现意外断网,停电等等意外现象,都有可能造成对方收不到信息!也就说,你发送的信息对方没收到,同时你也不知道对方有没有收到!这不好!

 

加载场景

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Text;
using Common;
using UnityEngine.UI;
using System;
using System.Net;
using UIWidgetsSamples;
using UnityEngine.SceneManagement;

namespace  NS
{
    ///<summary>
    /// 程序入口
    ///</summary>
    public class GameMain : MonoSingleton<GameMain>
    {

        private InputField IPInputField;

        private InputField IPInputField1;
        //点击按钮 ButtonClientEnter初始化客户端服务类
        private void Start()
        {
            transform.FindChildByName("ButtonClientEnter").
                GetComponent<Button>().onClick.AddListener(OnEnterChatScene);
            //获取进入按钮
            transform.FindChildByName("ButtonServerEnter").
                GetComponent<Button>().onClick.AddListener(OnEnterServerScene);
            // InputServerIPForClient
            IPInputField1 = transform.FindChildByName("" +
                "InputServerIPForClient").GetComponent<InputField>();
            IPInputField = transform.FindChildByName("" +
                "InputServerIPForServer").GetComponent<InputField>();
            //获取文本框  拿到IP
        }

        private void OnEnterServerScene()
        {
            UDPServerNetWorkService.Instance.Initialized(IPInputField.text);
            SceneManager.LoadScene("Server");
        }

        //进去聊天室
        private void OnEnterChatScene()
        {
            UDPClientNetWorkService.Instance.Initialized 
                (IPInputField1.text);
            //获取IP  初始化客户都安
            SceneManager.LoadScene("Chat");
            //切换到聊天窗口 加载不删除 
        }

        //加载场景
    }
}
GameMain

 

Date数据类

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System.Net.Sockets;
using System.Text;
using Common;
using UnityEngine.UI;
using System;
using System.Net;
using UIWidgetsSamples;
using System.Runtime.Serialization.Formatters.Binary;

namespace  NS
{
    ///<summary>
    ///聊天消息
    ///</summary>
    [System.Serializable]//表示类可以被序列化
    public class ChatMessage 
    {
        //消息类型 0 1 2 枚举
        public UDPNetWork Type { get; set; }
        //发送者名称
        public string SenderName { get; set; }
        //消息内容
        public string Content { get; set; }


        //如何把一个对象转字节数组???
        //序列化:将对象状态存储到某种介质的过程(场景还原,网络交互)
        //存储介质:磁盘  内存   网络
        //反序列化:将某种介质中的对象转换成C#对象
        public  byte []  ObjectToBytes()
        {
            //1.二进制写入器  往内存流去写  支持跨平台
            //MemoryStream ms = new MemoryStream();
            //BinaryWriter writer = new BinaryWriter(ms);

            //byte[] bys = Encoding.UTF8.GetBytes(SenderName);
            ////值类型  因为大小固定  所以直接Write
            ////引用类型  大小不固定  所以需要写入长度
            //writer.Write(bys.Length);
            ////写入长度
            //writer.Write(bys);
            //写入内容
            //2.序列化
            //使用Using  可以在退出代码块的时候 自动关闭非托管资源
            using (MemoryStream ms = new MemoryStream())
            {
                //内存流
                BinaryFormatter fomatter = new BinaryFormatter();
                //二进制格式化器
                fomatter.Serialize(ms, this);
                //序列化  将当前对象序列化到内存流中                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

                //byte[] result = ms.ToArray();
                //从内存流中获取字节数组
                return ms.ToArray();
            }
        }
        public static  ChatMessage   BytesToObject(byte [] bytes)
        {
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                //内存流
                BinaryFormatter fomatter = new BinaryFormatter();
                //二进制格式化器
                return fomatter.Deserialize(ms) as ChatMessage;
                //反序列化  将内存流中  中的字节反序列化                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               

            }
            //自动释放资源
        }
    }
}
ChatMessage
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Net;

namespace  NS
{
    ///<summary>
    /// 事件参数类 
    ///</summary>
    public class MessageArrivedEventArgs
    {
        public  ChatMessage Message { get; set; }
        public DateTime DateTime { get; set; }

        public IPEndPoint senderEP { get; set; }

    }
}
MessageArrivedEventArgs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace  NS
{
    ///<summary>
    /// 消息类型
    ///</summary>
    public enum UDPNetWork
    {
        Online,
        //上线
        Offline,
        //下线
        General
        //普通
    }
}
UDPNetWork

 

NetWork Service(网络服务)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Common;
using System.Net.Sockets;
using System.Text;
using UnityEngine.UI;
using System.Net;
using UIWidgetsSamples;
using System.Threading;
namespace  NS
{
    ///<summary>
    ///基于UDP协议  客户端网络服务类
    ///</summary>
    public class UDPClientNetWorkService : MonoSingleton<UDPClientNetWorkService>
    {
        //事件参数类  传递信息
        public event Action<MessageArrivedEventArgs> MessageArrivedEventHandler;
        //初始化socket对象  ,由开始界面的Game Main类调用
        private Thread receiverThread;
        //线程
        private UdpClient udpServer;
        public void Initialized(string serverIP)
        {
            
            //加载不删除对象
            udpServer = new UdpClient();
            //客户端发送消息再给终结点
            serverEP = new IPEndPoint(IPAddress.Parse(serverIP), 8989);
            Debug.Log(serverEP.Address);

            //分配IP和端口  没有保存
            udpServer.Connect(serverEP);
            //Connect方法建立默认远程主机使用中指定的值endPoint参数。 
            //一旦建立,无需在每次调用中指定远程主机Send方法。

            receiverThread = new Thread(ReceiveMessage);
            receiverThread.Start();
            //提示上线
            SendChatMessage(new ChatMessage() { Type = UDPNetWork.Online });
        }
        private IPEndPoint serverEP;
        //发送消息  send按钮
        public void SendChatMessage(ChatMessage msg)
        {
            byte[] dgram = msg.ObjectToBytes();
            //获取报文
            udpServer.Send(dgram, dgram.Length);
            //发送需要端口和IP
            
            Debug.Log(msg.Type);
        }
        //接收消息  接收阻塞  线程
        public void ReceiveMessage()
        {
            while (true)//不停接收数据
            {
                //接收
                IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                byte[] date = udpServer.Receive(ref remote);
                ChatMessage msg = ChatMessage.BytesToObject(date);
                Debug.Log(msg.Type);
                //引发事件
                if (MessageArrivedEventHandler != null)
                {
                    //线程交叉助手类调用 因为访问UnityAPI
                    ThreadCrossHelper.Instance.ExecuteOnMainThread(() =>
                    {
                        var args = new MessageArrivedEventArgs()
                        {
                            Message = msg,
                            DateTime = DateTime.Now
                        };
                    //创建事件参数类  参数没有赋值???
                    MessageArrivedEventHandler(args);
                    //调用方法不需要传参数吗???
                });
                }

            }
        }
        //关闭
        public void OnApplicationQuit()
        {
            SendChatMessage(new ChatMessage() { Type = UDPNetWork.Offline });
            receiverThread.Abort();
            udpServer.Close();
        }
    }
}
UDPClientNetWorkService
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Common;
using System.Net.Sockets;
using System.Text;
using UnityEngine.UI;
using System.Net;
using UIWidgetsSamples;
using System.Threading;
using System;
namespace  NS
{
    ///<summary>
    /// 基于UDP的服务端网络服务类
    ///</summary>
    public class UDPServerNetWorkService : MonoSingleton<UDPServerNetWorkService>
    {
        //客户端列表
        //初始化Socket
        //发送消息
        //接收消息 
        //事件参数类  传递信息
        public event Action<MessageArrivedEventArgs> MessageArrivedEventHandler;
        //初始化socket对象  ,由开始界面的Game Main类调用
        private Thread receiverThread;
        //线程
        private UdpClient udpServer;
        public void Initialized(string serverIP)
        {
            //加载不删除对象
            //客户端发送消息再给终结点
            serverEP = new IPEndPoint(IPAddress.Parse(serverIP), 8989);
            udpServer = new UdpClient(serverEP);

            receiverThread = new Thread(ReceiveMessage);
            receiverThread.Start();


            //注册事件  收到消息的时候执行
            MessageArrivedEventHandler += OnMessageArrived;
        }
        //收到消息的时候分发
        private void OnMessageArrived(MessageArrivedEventArgs obj)
        {
            switch (obj.Message.Type)
            {
                case UDPNetWork.Online:
                    //存列表
                    AddClient(obj.senderEP);
                    break;
                case UDPNetWork.Offline:
                    //移除列表
                    RemoveClient(obj.senderEP);
                    break;
                case UDPNetWork.General:
                    //转发
                    TransmitMessage(obj.Message);
                    break;
            }
        }

        private IPEndPoint serverEP;
        //发送消息  send按钮
        public void SendChatMessage(ChatMessage msg,IPEndPoint remote)
        {
            byte[] dgram = msg.ObjectToBytes();
            //获取报文
            udpServer.Send(dgram, dgram.Length, remote);
            //发送需要端口和IP
        }
        //接收消息  接收阻塞  线程
        public void ReceiveMessage()
        {
            while (true) //不停接收数据
            {

                //接收
                IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
                byte[] date = udpServer.Receive(ref remote);
                ChatMessage msg = ChatMessage.BytesToObject(date);
                Debug.Log(msg.Type + "adsadad");
                //引发事件
                if (MessageArrivedEventHandler != null)
                {
                    //线程交叉助手类调用 因为访问UnityAPI
                    ThreadCrossHelper.Instance.ExecuteOnMainThread(() =>
                    {
                        var args = new MessageArrivedEventArgs()
                        {
                            Message = msg,
                            DateTime = DateTime.Now,
                            senderEP = remote
                            
                        };
                        Debug.Log(msg + "1");
                    //创建事件参数类  参数没有赋值???
                    MessageArrivedEventHandler(args);
                        //调用方法不需要传参数吗???
                        Debug.Log(msg + "2");
                    });
                }
            }
        }
        //关闭
        public void OnApplicationQuit()
        {
            receiverThread.Abort();
            udpServer.Close();
        }

        private List<IPEndPoint> allListEP = new List<IPEndPoint>();
        //添加客户端
        public  void AddClient(IPEndPoint clientEP)
        {
            allListEP.Add(clientEP);
        }
        //移除客户端
        public void RemoveClient(IPEndPoint clientEP)
        {
            allListEP.Remove(clientEP);
        }
        //转发消息
        private void  TransmitMessage(ChatMessage msg)
        {
            foreach (var item in allListEP)
            {
                SendChatMessage(msg, item);
                //根据不同端口发送消息
            }
        }
    }
}
UDPServerNetWorkService
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.Text;
using Common;
using UnityEngine.UI;
using System;
using System.Net;
using UIWidgetsSamples;
namespace  NS
{
    ///<summary>
    ///客户端显示信息界面类
    ///</summary>
    public class ChatClient : MonoBehaviour
    {
        private InputField messageInput;

        private InputField nameInput;
        private void Start()
        {
            messageInput = transform.FindChildByName("MessageInput").GetComponent<InputField>();
            nameInput = transform.FindChildByName("NameInput").GetComponent<InputField>();
            transform.FindChildByName("Send").GetComponent<Button>().
                onClick.AddListener(OnSendButtonClick);
            //获取各种组建
            chatview = transform.FindChildByName("ChatView").GetComponent<ChatView>();
            UDPClientNetWorkService.Instance.MessageArrivedEventHandler 
                += OnMessageArrived;
            //注册事件
        }
        //点击调用发送消息的方法
        private void OnSendButtonClick()
        {
            ChatMessage msg = new ChatMessage()
            {
                Type = UDPNetWork.General,
                Content = messageInput.text,
                SenderName = nameInput.text
            };
            UDPClientNetWorkService.Instance.
                SendChatMessage(msg);
        }
        private ChatView chatview;
        private  void  OnMessageArrived(MessageArrivedEventArgs args)
        {
            //注册方法
            //显示消息
            ChatLine line = new ChatLine()
            {
                //事件参数对象赋值????
                Time = args.DateTime,
                Message = args.Message.Content,
                Type = ChatLineType.User,
                UserName=args.Message.SenderName
            };
            chatview.DataSource.Add(line);
            //滚动
            chatview.ScrollRect.verticalNormalizedPosition = 0;
        }
    }
}
ChatClient
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Common;
namespace  NS
{
    ///<summary>
    ///服务端显示面板
    ///</summary>
    public class ChatServer : MonoBehaviour
    {
        private void Start()
        {
            text = transform.FindChildByName("Text").GetComponent<Text>();
            UDPServerNetWorkService.Instance.MessageArrivedEventHandler
                += OnMessageArrived;
        }
        private Text text;  
        //当服务端收到消息 显示在文本中
        private void OnMessageArrived(MessageArrivedEventArgs obj)
        {
            Debug.Log(obj.Message.Type);
            text.text = string.Format("{0}---{1}", 
                obj.Message.Type, obj.Message.Content);
        }
    }
}
ChatServer

 工程包链接:  https://github.com/AnsssAS/Assect

posted @ 2019-03-01 16:29  NuLiAs  阅读(729)  评论(0编辑  收藏  举报