基于Socket的聊天系统

先看看效果图



以上分别是两个客户端相互通讯的情况的。


源码:https://github.com/zymix/Unity_ChatSystem


C#本身对Socket拥有着高度的封装,所以搭建这样的一个多人聊天系统是非常容易的事情。这里先提醒几点:

1.关于Unity内部编码问题,Unity在debug阶段时其编码模式是跟操作系统一致的,但是当其发布以后,由于Unity的跨平台性使其编码改变成UTF8的形式,所以无论哪个阶段,都应该注意客户端与服务器段的编码模式一致。

2.关于Scroll View的问题,想滚动条动态的改变滚动长度需要,动态修改Scroll Rect组件的Content对象的RectTransform的deltaSize


剩下的,看注释,算是比较清晰了

客户端代码

[csharp]   view plain  copy
  1. using UnityEngine;  
  2. using System.Net.Sockets;  
  3. using System;  
  4.   
  5. using System.Collections.Generic;  
  6. using UnityEngine.UI;  
  7. using System.Collections;  
  8.   
  9. public class ChatClient : MonoBehaviour  
  10. {  
  11.     public GameObject content;  
  12.     public InputField input;  //用于发送输入框的信息  
  13.     public InputField usernameInput;//客户端用户名  
  14.     public GameObject messageItem;//预制件的宽高  
  15.     public Button connectButton; //连接按钮  
  16.   
  17.     TcpClient client;//客户端  
  18.     NetworkStream nstream;//网络数据流  
  19.     string ip = "192.168.10.5";//客户端IP  
  20.     int port = 10086;//客户端端口  
  21.     byte[] data;//客户端收发数据  
  22.     bool isConnect = false;  
  23.   
  24.     List<GameObject> messageList;//存储接受的消息列表  
  25.     public int messagesMaxSize = 20;//存储接受的消息列表最大长度  
  26.     bool isNewMessage = false;//消息更新的标志位  
  27.     string newMessage;//接受到的最新信息  
  28.     //预制件的宽高  
  29.     float height = 0;  
  30.     float width = 0;  
  31.     void Start()  
  32.     {  
  33.         messageList = new List<GameObject>();  
  34.         messageList.Capacity = messagesMaxSize;  
  35.   
  36.         //获取预设件的宽高  
  37.         width = messageItem.GetComponent<RectTransform>().rect.width;  
  38.         height = messageItem.GetComponent<RectTransform>().rect.height;  
  39.   
  40.         //初始化组件  
  41.         if (content == null)  
  42.             content = transform.FindChild("Chat View/Viewport/Content").gameObject;  
  43.         if (input == null)  
  44.             input = transform.FindChild("InputField").GetComponent<InputField>();  
  45.         if (usernameInput == null)  
  46.             usernameInput = transform.FindChild("UsernameInput").GetComponent<InputField>();  
  47.   
  48.         if (connectButton == null)  
  49.             connectButton = transform.FindChild("ConnectButton").GetComponent<Button>();  
  50.         //给连接按钮添加OnConnect事件,控制链接开启和关闭  
  51.         connectButton.onClick.AddListener(delegate { OnConnect(); });  
  52.   
  53.     }  
  54.     void Update()  
  55.     {  
  56.         if (isNewMessage)  
  57.         {//若存在新的消息,则添加消息更新界面  
  58.             AddMessage(newMessage);  
  59.             isNewMessage = false;  
  60.         }  
  61.     }  
  62.     public void OnDestroy()  
  63.     {  
  64.         Disconnect();  
  65.     }  
  66.   
  67.   
  68.     //连接按钮的点击事件调用Onconnect连接函数  
  69.     public void OnConnect()  
  70.     {  
  71.         isConnect = !isConnect;  
  72.         if (isConnect)  
  73.         {  
  74.             connectButton.transform.FindChild("Text").GetComponent<Text>().text = "断开连接";  
  75.             Connect();  
  76.         }  
  77.         else  
  78.         {  
  79.             connectButton.transform.FindChild("Text").GetComponent<Text>().text = "连接服务器";  
  80.             Disconnect();  
  81.         }  
  82.     }  
  83.   
  84.   
  85.   
  86.   
  87.     //中断网络连接  
  88.     void Disconnect()  
  89.     {  
  90.         //关闭流和客户端  
  91.         if(nstream !=null&&(nstream.CanRead|| nstream.CanWrite))  
  92.             nstream.Close();  
  93.         if (nstream != null && client.Connected)  
  94.             client.Close();  
  95.     }  
  96.   
  97.     //网络连接  
  98.     void Connect()  
  99.     {  
  100.         Info.debugStr = "";  
  101.         if ("" == usernameInput.text)  
  102.         {  
  103.             Info.debugStr = DateTime.Now + "->用户名不能为空";  
  104.             return;  
  105.         }  
  106.         try  
  107.         {  
  108.             //创建TCP连接  
  109.             client = new TcpClient(ip, port);  
  110.             nstream = client.GetStream();  
  111.             //先发送用户名  
  112.             SendMessage(usernameInput.text);  
  113.             //初始化收发数据  
  114.             data = new byte[client.ReceiveBufferSize];  
  115.   
  116.             //开启异步从服务器获取消息,获取到的数据流存入data,回调ReceiveMessage方法  
  117.             nstream.BeginRead(data, 0, data.Length, AsyncReceive, null);  
  118.         }  
  119.         catch (SocketException socketEx)  
  120.         {//输出错误码  
  121.             Info.debugStr = DateTime.Now + "->" + socketEx.ErrorCode + ": " + socketEx.Message;  
  122.             //关闭流和客户端  
  123.             client.Close();  
  124.         }  
  125.         catch (Exception e)  
  126.         {   //显示错误信息  
  127.             Info.debugStr = DateTime.Now + "->" + e.Message;  
  128.             //断开连接  
  129.             Disconnect();  
  130.         }  
  131.     }  
  132.   
  133.     
  134.   
  135.     //发送消息到服务器  
  136.     public void Send()  
  137.     {  
  138.         Info.debugStr = "";  
  139.   
  140.         if ("" == input.text)  
  141.         {  
  142.             Info.debugStr = DateTime.Now + "->消息不能为空";  
  143.             return;  
  144.         }  
  145.         //发送消息到服务器  
  146.         SendMessage(input.text);  
  147.         //清空输入框  
  148.         input.text = "";  
  149.     }  
  150.     new public void SendMessage(string message)  
  151.     {  
  152.         try  
  153.         {   //把输入框的消息写入数据流,发送服务器  
  154.             //NetworkStream stream = client.GetStream();  
  155.             byte[] bytes = System.Text.Encoding.UTF8.GetBytes(message);  
  156.             nstream.Write(bytes, 0, bytes.Length);  
  157.         }  
  158.         catch (Exception e)  
  159.         {  
  160.             //显示错误信息  
  161.             Info.debugStr = DateTime.Now + "->" + e.Message;  
  162.             //断开连接  
  163.             Disconnect();  
  164.         }  
  165.     }  
  166.   
  167.   
  168.      
  169.     //异步处理从服务器获得的消息  
  170.     void AsyncReceive(IAsyncResult ar)  
  171.     {  
  172.         Info.debugStr = "";  
  173.         try  
  174.         {  
  175.             int bytesRead = client.GetStream().EndRead(ar);  
  176.             if (bytesRead < 1)  
  177.             {   //接收不到任何消息  
  178.                 return;  
  179.             }  
  180.             else  
  181.             {  
  182.                 //把接收到的消息编码为UTF8,跟Unity发布后编码方式一致  
  183.                 newMessage = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead);  
  184.                 //设置标志位,在UI上添加一条消息  
  185.                 isNewMessage = true;  
  186.             }  
  187.             //再次开启异步从服务器获取消息,形成循环等待服务器消息  
  188.             nstream.BeginRead(data, 0, client.ReceiveBufferSize, AsyncReceive, null);  
  189.         }  
  190.         catch (Exception e)  
  191.         {  
  192.             Info.debugStr = DateTime.Now + "->" + e.Message;  
  193.             //断开连接  
  194.             Disconnect();  
  195.         }  
  196.     }  
  197.       
  198.       
  199.       
  200.     //在UI上添加一条消息  
  201.     private void AddMessage(string uiMessage)  
  202.     {  
  203.         //把接收到的消息存入消息列表中  
  204.         GameObject item = Instantiate<GameObject>(messageItem);  
  205.         item.GetComponent<Text>().text = uiMessage;  
  206.         //超出列表上限时,再删除第一条  
  207.         if (messageList.Count > messageList.Capacity)  
  208.             messageList.RemoveAt(0);  
  209.         messageList.Add(item);  
  210.   
  211.         //刷新界面  
  212.         ShowMessages();  
  213.     }  
  214.      
  215.     private void ShowMessages()  
  216.     {  
  217.         int i = 0;  
  218.         foreach (GameObject m in messageList)  
  219.         {  
  220.             //设置为显示内容面板的子对象,便于位置的设置位置  
  221.             m.transform.SetParent(content.transform, false);  
  222.             //按序设置位置  
  223.             RectTransform rf = m.GetComponent<RectTransform>();  
  224.             rf.sizeDelta = new Vector2(width, height);  
  225.             rf.localPosition = new Vector2(0, -i * height);  
  226.             ++i;  
  227.         }  
  228.         content.GetComponent<RectTransform>().sizeDelta = new Vector2(width, i * height);  
  229.     }  
  230. }  

简单的负责debug的提示信息类

[csharp]   view plain  copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class Info : MonoBehaviour {  
  5.   
  6.     public static string debugStr = "";  
  7.   
  8.     void OnGUI() {  
  9.         GUILayout.Label(debugStr);  
  10.   
  11.     }  
  12. }  


服务器端的代码

主程序

[csharp]   view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net;  
  5. using System.Net.Sockets;  
  6. using System.Text;  
  7. using System.Threading.Tasks;  
  8.   
  9. namespace ChatSystem  
  10. {  
  11.     class Program  
  12.     {  
  13.         static void Main(string[] args)  
  14.         {  
  15.             //设置监听的端口号  
  16.             Console.WriteLine("请输入服务器需要监听的端口: ");  
  17.             string input = Console.ReadLine();  
  18.             int port = int.Parse(input);  
  19.             //调用方法启动服务器  
  20.             Server(port);  
  21.         }  
  22.   
  23.         static void Server(int port)  
  24.         {  
  25.             //初始化服务器ip  
  26.             //IPAddress localAddress = IPAddress.Parse("127.0.0.1");  
  27.             //设置监听  
  28.             TcpListener listener = new TcpListener(IPAddress.Any, port);  
  29.             listener.Start();  
  30.   
  31.             //提示信息  
  32.             Console.WriteLine("{0:HH:mm:ss}->监听端口{1}....", DateTime.Now, port);  
  33.   
  34.             //循环等待客户端的连接请求  
  35.             while(true)  
  36.             {  
  37.                 ChatServerHandle usr = new ChatServerHandle(listener.AcceptTcpClient());  
  38.                 Console.WriteLine(usr.ip + "加入聊天室");  
  39.             }  
  40.         }  
  41.   
  42.     }  
  43. }  


服务器辅助类
[csharp]   view plain  copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net.Sockets;  
  5. using System.Net;  
  6. using System.Text;  
  7. using System.Threading.Tasks;  
  8. using System.Collections;  
  9.   
  10. namespace ChatSystem  
  11. {  
  12.     class ChatServerHandle  
  13.     {  
  14.         public static Hashtable Clients = new Hashtable();//客户端连接记录表  
  15.         public TcpClient client;//客户端  
  16.         public string username;//客户端用户名  
  17.   
  18.         public string ip;//客户端IP  
  19.         public int port;//客户端端口  
  20.   
  21.         public byte[] data;//客户端收发数据  
  22.   
  23.         bool firstConnet = true;  
  24.   
  25.         public ChatServerHandle(TcpClient client)  
  26.         {  
  27.             this.client = client;  
  28.               
  29.             //保存客户端IP和端口  
  30.             IPEndPoint ipEndPoint = client.Client.RemoteEndPoint as IPEndPoint;  
  31.             this.ip = ipEndPoint.Address.ToString();  
  32.             this.port = ipEndPoint.Port;  
  33.   
  34.             //初始化收发数据  
  35.             this.data = new byte[client.ReceiveBufferSize];  
  36.   
  37.             //开启异步从客户端获取消息,获取到的数据流存入data,回调ReceiveMessage方法  
  38.             client.GetStream().BeginRead(data, 0, data.Length, ReceiveMessage, null);  
  39.   
  40.         }  
  41.         //向客户端发送消息  
  42.         public void SendMessage(string message)  
  43.         {  
  44.             try  
  45.             {  
  46.                 NetworkStream stream;  
  47.                 lock(client.GetStream())  
  48.                 {  
  49.                     stream = client.GetStream();  
  50.                 }  
  51.                 byte[] bytes = Encoding.UTF8.GetBytes(message); //注意Unity发布后程序已UTF8编码  
  52.                 stream.Write(bytes, 0, bytes.Length);  
  53.                 //stream.Flush();  
  54.             }  
  55.             catch (Exception e)  
  56.             {  
  57.                 Console.WriteLine("{0:HH:mm:ss}->[SendMessage]Error: {1}", DateTime.Now, e.Message);  
  58.             }  
  59.         }  
  60.         //从客户端接受到的数据,再广播给所有客户端  
  61.         public void ReceiveMessage(IAsyncResult ar)  
  62.         {  
  63.            try  
  64.             {  
  65.                 int bytesRead;  
  66.                 lock(client.GetStream())  
  67.                 {  
  68.                     bytesRead = client.GetStream().EndRead(ar);  
  69.                 }  
  70.                 if (bytesRead < 1)  
  71.                 {//接收不到任何数据,则删除在客户端连接记录表  
  72.                     Clients.Remove(this.ip);  
  73.                     //向所有客户端广播该用户的下线信息  
  74.                     Broadcast("<color=#00ffffff>"+this.username + "</color> 于" + DateTime.Now + " 已下线....");  
  75.                     return;  
  76.                 }  
  77.                 else  
  78.                 {    
  79.                     string recMessage = Encoding.UTF8.GetString(data, 0, bytesRead); //注意Unity发布后程序已UTF8编码  
  80.                     if (firstConnet)  
  81.                     {   //检查是不是同一台电脑重复连接  
  82.                         if(Clients.ContainsKey(this.ip))  
  83.                             return;  
  84.                         //第一次连接,将客户端信息记录入客户端连接记录表中  
  85.                         Clients.Add(this.ip,this);  
  86.                         //获取发送的用户名信息  
  87.                         this.username = recMessage;  
  88.                         //向所有客户端广播该用户的上线信息  
  89.                         Broadcast("<color=#00ffffff>" + this.username + "</color> 于" + DateTime.Now+ " 已上线....");  
  90.                         //不在是第一次连接  
  91.                         firstConnet = false;  
  92.                     }  
  93.                     else  
  94.                     {  
  95.                         //向所有客户端广播该用户发送的  
  96.                         Broadcast(DateTime.Now + " ->【"this.username+"】" + recMessage);  
  97.                     }  
  98.                     //Console.WriteLine(recMessage);  
  99.                 }  
  100.                 lock(client.GetStream())  
  101.                 {  
  102.                     //再次开启异步从服务器获取消息,形成循环  
  103.                     client.GetStream().BeginRead(data, 0, client.ReceiveBufferSize, ReceiveMessage, null);  
  104.                 }  
  105.             }  
  106.             catch(Exception e)  
  107.             {  
  108.                 //删除连接记录  
  109.                 Clients.Remove(this.ip);  
  110.                 //向所有客户端广播该用户的下线信息  
  111.                 Broadcast("<color=#00ffffff>" + this.username + "</color> 于" + DateTime.Now + " 已下线....");  
  112.   
  113.                 Console.WriteLine("{0:HH:mm:ss}->[ReceiveMessage]Error: {1}", DateTime.Now, e.Message);  
  114.             }  
  115.         }  
  116.   
  117.         //向所有连接到的客户端广播  
  118.         public void Broadcast(string message)  
  119.         {  
  120.             Console.WriteLine(message);  
  121.             foreach (DictionaryEntry item in Clients)  
  122.             {  
  123.                 ChatServerHandle c = item.Value as ChatServerHandle;  
  124.                 c.SendMessage(message);  
  125.             }  
  126.         }  
  127.     }  
  128. }  

猜你喜欢

转载自blog.csdn.net/Happy_zailing/article/details/79987213