「前言」
之前在一直在写lua联网等一些知识,虽然lua重要,但C#联网也必不可少是吧。所以呢,本篇博客就主要介绍如何使用Unity和C#在实现多人在线聊天室。

服务器 客户端工作原理:(通过消息类型控制)

●登陆消息:Login
客户端向服务器发起请求加入消息,服务器收到后回给客户端一个允许加入的消息。同时,在发给房间的所有人一条某某人加入房间的消息,客户端收到后,把收到的信息显示出来。
●聊天消息:Chat
客户端把聊天的信息发给服务器,服务器收到后转发给所有人。
●登出消息:LogOut
客户端向服务器发送一条请求退出的消息,服务器收到后回复一条允许退出的消息,同时在告诉所有人这个人退出了房间。

工程流程:
● 搭建UI界面
● 制定消息协议
● 编写服务器
● 编写客户端

注意事项:Unity虽然支持线程的使用,但是我们要尽量避免在Unity中去使用线程。因为这对于手机配置低的机器来说运行起来会很卡。当然,如果你希望手机变成暖手宝,可以多开几个线程试一下。

效果如下:

● 搭建UI界面
参考上图,随意搭建,快乐就好。

● 制定消息协议

每一条消息都是通过创建消息对象,设置消息类型,和消息内容组成。服务器和客户端都必须拥有这个消息协议。

参考如下:

/// <summary>
/// 简单的协议类型
/// </summary>
public enum MessageType
{Chat = 0,//聊天Login = 1,//登陆LogOut = 2,//登出
}
/// <summary>
/// 消息体
/// </summary>
public class MessageData
{/// <summary>/// 消息类型/// </summary>public MessageType msgType;/// <summary>/// 消息内容/// </summary>public string msg;
}

● 编写服务器
在有用户连接成功时,服务器会自动创建该用户的客户端管理器,并记录用户的客户端管理器。

参考如下:

//N0.0-----------------------
//    用户连接控制器
//N0.0-----------------------
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;namespace Server
{class Program{/// <summary>/// 客户端管理列表/// </summary>public static List<ClientController> clientControllerList = new List<ClientController>();static void Main(string[] args){//定义socketSocket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint ipendPoint = new IPEndPoint(IPAddress.Parse("192.168.213.50"), 10004);Console.WriteLine("开始绑定端口号....");//将ip地址和端口号绑定serverSocket.Bind(ipendPoint);Console.WriteLine("绑定端口号成功,开启服务器....");//开启服务器serverSocket.Listen(100);Console.WriteLine("启动服成功!");while(true){Console.WriteLine("等待链接.....");Socket clinetSocket = serverSocket.Accept();ClientController controller = new ClientController(clinetSocket);//添加到列表中clientControllerList.Add(controller);Console.WriteLine("当前有" + clientControllerList.Count + "个用户");Console.WriteLine("有一个用户链接....");}}}
}

服务器在接收到信息时会根据消息类型而定做出回应,或转发他人,或回给客户端。

//N0.1-----------------------
//    客户端控制器
//N0.1-----------------------
using System;
using System.Net.Sockets;
using System.Threading;namespace Server
{class ClientController{/// <summary>/// 用户链接的通道/// </summary>private Socket clientSocket;/// <summary>/// 昵称/// </summary>public string nickName;//接收的线程Thread receiveThread;public ClientController(Socket socket){clientSocket = socket;//启动接收的方法//开始收的线程receiveThread = new Thread(ReceiveFromClient);//启动收的线程receiveThread.Start();}/// <summary>/// /// </summary>void ReceiveFromClient(){while (true){byte[] buffer = new byte[512];int lenght = clientSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None);Console.WriteLine("接收长度=" + lenght);string json = System.Text.Encoding.UTF8.GetString(buffer,0, lenght);json.TrimEnd();if (json.Length>0){Console.WriteLine("json=" + json);MessageData data = LitJson.JsonMapper.ToObject<MessageData>(json);switch (data.msgType){case MessageType.Login://如果是登陆消息nickName = data.msg;//1、告诉这个客户端,你登陆成功了!MessageData backData = new MessageData();backData.msgType = MessageType.Login;backData.msg = "";SendToClient(backData);//2、需要告诉所有客户端,***加入了房间MessageData chatData = new MessageData();chatData.msgType = MessageType.Chat;chatData.msg = nickName + " 进入了房间";SendMessageDataToAllClientWithOutSelf(chatData);break;case MessageType.Chat://如果是聊天消息//转发聊天信息MessageData chatMessageData = new MessageData();chatMessageData.msgType = MessageType.Chat;chatMessageData.msg = nickName + ":" + data.msg;SendMessageDataToAllClientWithOutSelf(chatMessageData);break;case MessageType.LogOut://客户端请求退出//1、回给这个用户一个消息,告诉用你你可以退出了MessageData logOutData = new MessageData();logOutData.msgType = MessageType.LogOut;SendToClient(logOutData);//2、告诉所有的其他用户,**退出了房间MessageData logOutChatData = new MessageData();logOutChatData.msgType = MessageType.Chat;logOutChatData.msg = nickName + " 退出了房间";SendMessageDataToAllClientWithOutSelf(logOutChatData);break;}}}}/// <summary>/// 广播信息,告诉所有用户有什么信息过来了!/// </summary>/// <param name="data"></param>void SendMessageDataToAllClient(MessageData data){for(int i = 0;i<Program.clientControllerList.Count;i++){try{Program.clientControllerList[i].SendToClient(data);}catch(Exception e){i--;}}}/// <summary>/// 广播消息,排除掉自己/// </summary>/// <param name="data"></param>void SendMessageDataToAllClientWithOutSelf(MessageData data){//5   i=3for (int i = 0; i < Program.clientControllerList.Count; i++){if(Program.clientControllerList[i] != this){Program.clientControllerList[i].SendToClient(data);}}}void SendToClient(MessageData data){//把对象转换为json字符串string msg = LitJson.JsonMapper.ToJson(data);//把json字符串转换byte数组byte[] msgBytes = System.Text.Encoding.UTF8.GetBytes(msg);//调用发送字节的方法SendToClient(msgBytes);}/// <summary>/// 发消息给客户端/// </summary>/// <param name="msgByte">需要发送的内容</param>void SendToClient(byte[] msgBytes){//确保发送的是拼接后的信息int sendLength = clientSocket.Send(msgBytes);Console.WriteLine("服务器发送信息结束,成功发送:" + sendLength);Thread.Sleep(50);}/// <summary>/// 销毁自己,也就是释放资源/// </summary>public void DestroySelf(){try{receiveThread.Abort();}catch(Exception e){Console.WriteLine(e.ToString());}}}
}

● 编写客户端
客户端在进行发送消息时会生成消息对象,发送之前会把消息对象换成Json字符串,在把Json转换为Byte字节流进行最终的发送。当然服务器也是同理。服务器接收到字节流时会将字节流转为字符串(Json),并且对字符串进行解析,最终得到的将是一个对象。该对象里包含客户端发来的消息内容,和消息类型。同时,这也属于一个简单的深度克隆。

using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System;
using LitJson;
/// <summary>
/// 声明一个委托对象
/// </summary>
/// <param name="data">接收到的数据</param>
public delegate void ReceiveMessageData(byte[] buffer,int offset,int size);
/// <summary>
/// 当连接改变
/// </summary>
public delegate void OnConnectChange();
public class ClientSocket : MonoBehaviour {/// <summary>/// 客户端Socket/// </summary>Socket clientSocket;/// <summary>/// 数据缓冲池/// </summary>private byte[] buffer = new byte[10000];/// <summary>/// 委托变量/// </summary>public ReceiveMessageData receiveMessageData;/// <summary>/// 链接成功/// </summary>public OnConnectChange onConnectSuccess;/// <summary>/// 链接异常/链接断开/// </summary>public OnConnectChange onConnectExcept;void Start () {//创建socket对象clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);ConnectToServer();}public void ConnectToServer(){IPEndPoint ipendPoint = new IPEndPoint(IPAddress.Parse("172.0.0.1"), 10004);Debug.Log("开始链接服务器!!!");//请求链接clientSocket.BeginConnect(ipendPoint, ConnectCallback, "");}/// <summary>/// 链接的回调/// </summary>/// <param name="ar"></param>public void ConnectCallback(IAsyncResult ar){Debug.Log("ar.AsyncState=" + ar.AsyncState + ",clientSocket.Connected=" + clientSocket.Connected);try{clientSocket.EndConnect(ar);}catch(Exception e){Debug.Log(e.ToString());}//判断到底链接成功了还是没有Debug.Log("链接回调!!!!!!!!!!");if(clientSocket.Connected == true){//调用链接成功的回调onConnectSuccess();//链接成功Debug.Log("链接成功");//开启接收消息ReceiveMessageFromServer();}else{//链接失败Debug.Log("链接失败");}}/// <summary>/// 发送消息的方法/// </summary>/// <param name="sendMsgContent">消息内容</param>/// <param name="offset">从消息内容第几个开始发送</param>/// <param name="size">发送的长度</param>public void SendBytesMessageToServer(byte[] sendMsgContent, int offset, int size){Debug.Log("开始发送!!准备发送长度=" + size);clientSocket.BeginSend(sendMsgContent, offset, size, SocketFlags.None, SendMessageCallback, "");}/// <summary>/// 发送一g给服务器/// </summary>/// <param name="msg"></param>public void PutMessageToQueue(MessageData data){//将对象序列化发过去byte[] msgBytes = System.Text.Encoding.UTF8.GetBytes(JsonMapper.ToJson(data));SendBytesMessageToServer(msgBytes, 0, msgBytes.Length);Debug.Log("开始发送的字节为:"+ msgBytes);}/// <summary>/// 发送一条文字信息给服务器/// </summary>/// <param name="msg"></param>public void PutMessageToQueue(string msg){MessageData msgdata = new MessageData();msgdata.msgType = MessageType.Chat;msgdata.msg = msg;//将对象序列化发过去byte[] msgBytes = System.Text.Encoding.UTF8.GetBytes(JsonMapper.ToJson(msgdata));SendBytesMessageToServer(msgBytes, 0, msgBytes.Length);}/// <summary>/// 发送消息的回调/// </summary>/// <param name="ar"></param>public void SendMessageCallback(IAsyncResult ar){Debug.Log("发送结束!!!");//停止发送int length = clientSocket.EndSend(ar);Debug.Log("发送的长度:" + length);}/// <summary>/// 从服务器开始接收信息/// </summary>public void ReceiveMessageFromServer(){Debug.Log("开始接收数据");clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveMessageCallback, "");}/// <summary>/// 接收回调/// </summary>/// <param name="ar"></param>public void ReceiveMessageCallback(IAsyncResult ar){Debug.Log("接收结束!!");//结束接收int length = clientSocket.EndReceive(ar);Debug.Log("接收的长度是:" + length);string msg = System.Text.Encoding.UTF8.GetString(buffer, 0, length);Debug.Log("服务器发过来的消息是:" + msg);if(receiveMessageData != null){receiveMessageData(buffer, 0, length);}//开启下一次消息的接收ReceiveMessageFromServer();}public void OnDestroy(){//该释放的内容释放掉Debug.Log("释放链接资源");//clientSocket.Disconnect(true);//clientSocket.Dispose();}
}

在上面的客户端代码中我使用了委托让ClientSocket脱离了出来,方式就是设计模式中的单一原则。降低了耦合度。这样增加了代码的可移植性。下面是UI聊天控制源码。

参考如下:

//N0.1-----------------------
//    聊天控制器
//N0.1-----------------------
using UnityEngine;
using UnityEngine.UI;public class ChatUIController : MonoBehaviour {/// <summary>/// 要发送的内容/// </summary>public InputField sendMsgInputField;/// <summary>/// 昵称/// </summary>public InputField nickNameInputField;/// <summary>/// socket控制对象/// </summary>public ClientSocket clientSocket;//显示消息的文本public Text text;//接收的消息public string receiveMsg;/// <summary>/// 界面 0==loading 1==登陆  2==聊天/// </summary>public GameObject[] panels;/// <summary>/// 界面状态 0==loading 1==登陆  2==聊天/// </summary>public int panelState = 0;void Start () {//委托和具体方法关联clientSocket.receiveMessageData += ReceiveMsgData;clientSocket.onConnectSuccess += OnSocketConnectSuccess;}void Update () {//聊天信息的显示text.text = receiveMsg;//首先把所有的界面关闭掉panels[0].SetActive(panelState == 0);panels[1].SetActive(panelState == 1);panels[2].SetActive(panelState == 2);}/// <summary>/// 发送按钮的点击/// </summary>public void SendBtnClick(){if(sendMsgInputField != null && sendMsgInputField.text != ""){//发送clientSocket.PutMessageToQueue(sendMsgInputField.text);receiveMsg += "我:" + sendMsgInputField.text + "\n";//清理一下输入框内容sendMsgInputField.text = "";}}/// <summary>/// 接收消息的方法/// </summary>/// <param name="byteArray"></param>/// <param name="offset"></param>/// <param name="length"></param>public void ReceiveMsgData(byte[] byteArray, int offset, int length){Debug.Log("!!!!!!!!!!!!!!!!!!!!!!");string msg = ToolsUtil.ByteArrayToString(byteArray, offset, length);Debug.Log("收到信息:" + msg);//xml//receiveMsg += "服务器发来信息:" + msg + "\n";//对信息进行处理MessageData data = LitJson.JsonMapper.ToObject<MessageData>(msg);switch (data.msgType){case MessageType.Login://如果是登陆,代表界面可以切换了receiveMsg = "";panelState = 2;break;case MessageType.Chat://如果是聊天,代表进行聊天的显示receiveMsg += data.msg + "\n";break;case MessageType.LogOut://退出消息panelState = 1;break;}}/// <summary>/// 链接成功的回调/// </summary>public void OnSocketConnectSuccess(){//进入登陆界面panelState = 1;}/// <summary>/// 加入房间按钮点击/// </summary>public void JoinInBtnClick(){if(nickNameInputField != null && nickNameInputField.text != ""){//创建数据对象MessageData data = new MessageData();data.msgType = MessageType.Login;data.msg = nickNameInputField.text;//发送数据对象clientSocket.PutMessageToQueue(data);}else{//提示Debug.Log("昵称不能为空!");}}/// <summary>/// 退出房间点击事件/// </summary>public void LogOutBtnClick(){//消息数据MessageData data = new MessageData();data.msgType = MessageType.LogOut;//把消息传进去clientSocket.PutMessageToQueue(data);}
}

回顾一下:我们从搭建UI界面,制定消息协议,编写服务器,编写客户端,至此,实现了简单的多人联网聊天。其中不掺杂粘包,拆包,和队列收发。当然这些会在后面的博客中陆续更新出来。

Demo:聊天demo

视屏地址:UnitySocket异步聊天室

努力积才能,壹叶便成名,喜欢我关注我!

原创出品,转载请注明出处。

Unity联网之使用Socket简单实现多人在线聊天室(一)相关推荐

  1. 如何简单的创建一个多人在线聊天室

    学习目标: 在本教程中,我们将要使用PHP和jQuery创建一个简单的在线聊天工具. 这种实用性的模块对于你想要有实时在线客户支持系统的网站可以说是完美. 废话不多说直接开始. 步骤1:HTML的代码 ...

  2. JAVA利用多线程和Socket制作GUI界面的在线聊天室

    目录 前言 功能设计 GUI画面展示 服务器端 客户端 私聊窗口 主要代码 服务器端 客户端 其它代码 打包成jar 打包成exe文件 如何让其它电脑访问聊天室? 最后 前言 最近刚好是期末,碰上Ja ...

  3. node + socket + NetAPP 实现多人在线聊天

    紧急因疫情隔离在家,闲来无事,突发奇想,开发一通讯平台,实时群聊. 思路:客户端发送消息,服务器响应,并将其反馈给在线用户,最后使用内网穿透链接至公网. 技术栈: 前端:原生js + socket 后 ...

  4. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-完整应用例子-在线聊天室系统-代码解析...

    一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍一文之中我们对AgileEAS.NET S ...

  5. 基于Server-Sent Event的简单在线聊天室

    一.Web即时通信 所谓Web即时通信,就是说我们可以通过一种机制在网页上立即通知用户一件事情的发生,是不需要用户刷新网页的.Web即时通信的用途有很多,比如实时聊天,即时推送等.如当我们在登陆浏览 ...

  6. Nodejs+socket.io 搭建个人的网页聊天室

    Nodejs+socket.io 搭建个人的网页聊天室 最近看到别人搭建了自己的实时聊天室便产生了兴趣,于是乎自己也着手搭建了一个.在socket这里我选用了socket.io这个模块,在网上看了很多 ...

  7. Vue全家桶+Socket.io+Koa2打造一个智能聊天室 接口已开放

    Vue.js+Socket.io+Koa2打造一个智能聊天室 Vue.js全家桶+Socket.io+Express/Koa2 打造的一个智能聊天室. 已经开源啦!为了方便大家学习,智能机器人.IP定 ...

  8. 第三章、C#简单界面在线聊天室C#一对多聊天(使用TCP转发实现的在线聊天室,文章末尾附免费项目资源)

    C#网络通信系列学习笔记 第一章.C#最简单的控制台网络通信&C#最简单的控制台socket通信 第二章.C#控制台实现一对一聊天&C#socket类的简单封装 第三章.C#简单在线聊 ...

  9. 在线白板,基于socket.io的多人在线协作工具

    为什么80%的码农都做不了架构师?>>>    首发:个人博客,更新&纠错&回复 是昨天这篇博文留的尾巴,socket.io库的使用练习,成品地址在这里. 代码已经上 ...

最新文章

  1. 那时刚毕业的我,曾参与惊心动魄 3Q 大战
  2. zabbix之微信告警(python版):微信个人报警,微信企业号告警脚本
  3. 弹指之间 -- Waltz
  4. DCMTK:工作清单数据库测试程序
  5. JSTL (标准标签库)
  6. 各种java生成word解决方案的优缺点对比
  7. html引用c 变量,在jsp页面中定义全局变量,供其他页面引用
  8. [css] 实现单行文本居中和多行文本左对齐并超出显示“...“
  9. 解决 -- java 调用webservice 服务端收到参数为null
  10. 小汤学编程之JAVA基础day03——运算符
  11. (39)时钟抖动约束
  12. Graphviz样例之有向图
  13. scala写入mysql_spark rdd转dataframe 写入mysql的实例讲解
  14. 自动化测试selenium(四)check,选中复选框,操作一组元素
  15. UVaOJ 10328 Coin Toss
  16. Hbuilder Webview调试+逍遥安卓模拟器
  17. springboot+nodejs+vue+Elementui网上商城购物系统
  18. Snort IPS入侵防御系统模式
  19. 多重共线性的解决方法
  20. 泛函分析中的数学空间概念图谱

热门文章

  1. php二维数组追加字段给所有数组追加
  2. 为什么加密货币进入了历史上最长的熊市?
  3. CVPR 2022 | 美团技术团队精选6篇优秀论文解读
  4. 通过低代码平台搭建客服管理系统,解决管理难题
  5. Koala编译sass出错及中文编译解决
  6. [转]2008年最牛语录
  7. jquery数组求和
  8. C语言的文件的写入------C语言
  9. 【GDOI2008】彩球游戏
  10. Unity3D DLL加密