UDP实现一个简易的聊天室(多个主机连接终端) 已完善
程序员文章站
2022-04-08 19:52:59
效果展示 网络编程 --使用UDP协议的简单聊天室 由于TCP协议是一种面向连接的协议,有三次挥手通信确认机制,且数据包没有代销限制,TCP协议确保在通信双方都收到对方确认信息的前提下,才开始为通信的双方传输信息(数据包)! 这样数据包的传输可以得到安全保障!所以简单的理解为安全的!当然一旦,出现网 ......
效果展示
网络编程 --使用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"); //切换到聊天窗口 加载不删除 } //加载场景 } }
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; //反序列化 将内存流中 中的字节反序列化 } //自动释放资源 } } }
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; } } }
using system.collections; using system.collections.generic; using unityengine; namespace ns { ///<summary> /// 消息类型 ///</summary> public enum udpnetwork { online, //上线 offline, //下线 general //普通 } }
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(); } } }
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); //根据不同端口发送消息 } } } }
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; } } }
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); } } }
工程包链接: https://github.com/ansssas/assect
上一篇: 最新最全的Java面试题整理(内附答案)
下一篇: 徒儿们,扶师傅我起来试试