欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-08 19:52:59
效果展示 网络编程 --使用UDP协议的简单聊天室 由于TCP协议是一种面向连接的协议,有三次挥手通信确认机制,且数据包没有代销限制,TCP协议确保在通信双方都收到对方确认信息的前提下,才开始为通信的双方传输信息(数据包)! 这样数据包的传输可以得到安全保障!所以简单的理解为安全的!当然一旦,出现网 ......

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

 

 

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

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

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

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

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数据类

UDP实现一个简易的聊天室(多个主机连接终端) 已完善
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
UDP实现一个简易的聊天室(多个主机连接终端) 已完善
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
UDP实现一个简易的聊天室(多个主机连接终端) 已完善
using system.collections;
using system.collections.generic;
using unityengine;
namespace  ns
{
    ///<summary>
    /// 消息类型
    ///</summary>
    public enum udpnetwork
    {
        online,
        //上线
        offline,
        //下线
        general
        //普通
    }
}
udpnetwork

 

network service(网络服务)

UDP实现一个简易的聊天室(多个主机连接终端) 已完善
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
UDP实现一个简易的聊天室(多个主机连接终端) 已完善
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
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;
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
UDP实现一个简易的聊天室(多个主机连接终端) 已完善
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