服务器端的整体思路:
1、初始化IP地址和端口号以及套接字等字段;
2、绑定IP启动服务器,开始监听消息 socketServer.Listen(10);
3、开启一个后台线程接受客户端的连接 socketServer.Accept(),这里需要注意的是服务器端有两个Socket,一个负责监听,另一个负责传输消息,分工明确;
4、接受客户端消息 socketMsg.Receive();
5、向客户端发送消息 Send(bySendArray);
6、当然还应该包括信息显示方法和系统的退出方法。
/*** * * * 服务器端 * */ using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System.Threading; using System.Net; using System.Net.Sockets; using UnityEngine.UI; using System; public class Server : MonoBehaviour { public InputField InpIPAdress; //IP地址 public InputField InpPort; //端口号 public InputField InpDisplayInfo; //显示信息 public InputField InpSendMsg; //发送信息 public Dropdown Dro_IPList; //客户端的IP列表 private Socket socketServer; //服务端套接字 private bool IsListionContect = true; //是否监听 private StringBuilder _strDisplayInfo = new StringBuilder();//追加信息 private string _CurClientIPValue = string.Empty; //当前选择的IP地址 //保存客户端通讯的套接字 private Dictionary<string, Socket> _DicSocket = new Dictionary<string, Socket>(); //保存DropDown的数据,目的为了删除节点信息 private Dictionary<string, Dropdown.OptionData> _DicDropdowm = new Dictionary<string, Dropdown.OptionData>(); // Start is called before the first frame update void Start() { //控件初始化 InpIPAdress.text = "127.0.0.1"; InpPort.text = "1000"; InpSendMsg.text = string.Empty; //下拉列表处理 Dro_IPList.options.Clear(); //清空列表信息 //添加一个空节点 Dropdown.OptionData op = new Dropdown.OptionData(); op.text = ""; Dro_IPList.options.Add(op); } /// <summary> /// 退出系统 /// </summary> public void ExitSystem() { //退出会话Socket //退出监听Socket if (socketServer != null) { try { socketServer.Shutdown(SocketShutdown.Both); //关闭连接 } catch (Exception) { throw; } socketServer.Close(); //清理资源 } Application.Quit(); } /// <summary> /// 获取当前好友 /// </summary> public void GetCurrentIPInformation() { //选择玩家当前列表 _CurClientIPValue = Dro_IPList.options[Dro_IPList.value].text; } /// <summary> /// 服务端开始监听 /// </summary> public void EnableServerReady() { //需要绑定地址和端口号 IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(InpIPAdress.text), Convert.ToInt32(InpPort.text)); //定义监听Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //绑定 socketServer.Bind(endPoint); //开始socket监听 socketServer.Listen(10); //显示内容 DisplayInfo("服务器端启动"); //开启后台监听(监听客户端连接) Thread thClientCon = new Thread(ListionCilentCon); thClientCon.IsBackground = true; //后台线程 thClientCon.Name = "thListionClientCon"; thClientCon.Start(); } /// <summary> /// 接听到客户端的连接 /// </summary> private void ListionCilentCon() { Socket socMesgServer = null; //会话Socket try { while (IsListionContect) { //等待接受客户端连接,此处会“阻断”当前线程 socMesgServer = socketServer.Accept(); //获取远程节点信息 string strClientIPAndPort = socketServer.RemoteEndPoint.ToString(); //把远程节点信息添加到DropDown控件中 Dropdown.OptionData op = new Dropdown.OptionData(); op.text = strClientIPAndPort; Dro_IPList.options.Add(op); //把会话Socket添加到集合中(为后面发送信息使用) _DicSocket.Add(strClientIPAndPort, socMesgServer); //控件显示,有客户端连接 DisplayInfo("有客户端连接"); //开启后台线程,接受客户端信息 Thread thClientMsg = new Thread(ReceivMsg); thClientMsg.IsBackground = true; thClientMsg.Name = "thListionClientMsg"; thClientMsg.Start(socMesgServer); } } catch (Exception) { IsListionContect = false; //关闭会话Socket if (socketServer != null) { socketServer.Shutdown(SocketShutdown.Both); socketServer.Close(); } if (socMesgServer != null) { socMesgServer.Shutdown(SocketShutdown.Both); socMesgServer.Close(); } } } /// <summary> /// 后台线程,接受客户端信息 /// </summary> /// <param name="sockMsg"></param> private void ReceivMsg(object sockMsg) { Socket socketMsg = sockMsg as Socket; try { while (true) { //准备接受数据缓存 byte[] msgAarry = new byte[1024 * 0124]; //准备接受客户端发来的套接字信息 int trueClientMsgLenth = socketMsg.Receive(msgAarry); //byte数组转换为字符串 string strMsg = System.Text.Encoding.UTF8.GetString(msgAarry, 0, trueClientMsgLenth); //显示接受的客户端消息内容 DisplayInfo("客户端消息:" + strMsg); } } catch (Exception) { } finally { DisplayInfo("有客户端断开连接了:" + socketMsg.RemoteEndPoint.ToString()); //字典类断开连接的客户端消息 _DicSocket.Remove(socketMsg.RemoteEndPoint.ToString()); //客户端列表移除 if (_DicDropdowm.ContainsKey(socketMsg.RemoteEndPoint.ToString())) { Dro_IPList.options.Remove(_DicDropdowm[socketMsg.RemoteEndPoint.ToString()]); } //关闭Socket socketMsg.Shutdown(SocketShutdown.Both); socketMsg.Close(); } } //向发送客户端会话 public void SendMsg() { //参数检查 _CurClientIPValue = _CurClientIPValue.Trim(); if (string.IsNullOrEmpty(_CurClientIPValue)) { DisplayInfo("请选择要聊天的用户名称"); return; } //判断是否与指定的Socket通信 if (_DicSocket.ContainsKey(_CurClientIPValue)) { //得到发送的消息 string strSendMsg = InpSendMsg.text; strSendMsg = strSendMsg.Trim(); if (!string.IsNullOrEmpty(strSendMsg)) { //信息转码 byte[] bySendArray = System.Text.Encoding.UTF8.GetBytes(strSendMsg); //发送数据 _DicSocket[_CurClientIPValue].Send(bySendArray); //记录发送数据 DisplayInfo("发送的数据:" + strSendMsg); //控件重置 InpSendMsg.text = string.Empty; } else { DisplayInfo("发送的消息不能为空!"); } } else { DisplayInfo("请选择合法的聊天用户!"); } } /// <summary> /// 主显示控件,显示消息 /// </summary> /// <param name="str"></param> private void DisplayInfo(string str) { str = str.Trim(); if (!string.IsNullOrEmpty(str)) { _strDisplayInfo.Append(System.DateTime.Now.ToString()); _strDisplayInfo.Append(" "); _strDisplayInfo.Append(str); _strDisplayInfo.Append(" "); InpDisplayInfo.text = _strDisplayInfo.ToString(); } } // Update is called once per frame void Update() { } }
客户端整体思路:
1、初始化IP地址和端口号以及一些显示信息和套接字;
2、启动客户端连接 _SockClient.Connect(endPoint);
3、开启后台线程监听客户端发来的信息 _SockClient.Receive(byMsgArray);
4、客户端发送数据到服务器 _SockClient.Send(byteArray);
5、当然也应该包括退出系统和显示信息方法。
/*** * * * * 客户端 * * */ using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Threading; using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine.UI; public class Client : MonoBehaviour { public InputField InpIPAdress; //IP地址 public InputField InpPort; //端口号 public InputField InpDisplayInfo; //显示信息 public InputField InpSendMsg; //发送信息 private Socket _SockClient; //客户端Socket private IPEndPoint endPoint; private bool _IsSendDataConnection=true; //发送数据连接 private StringBuilder _SbDisplayInfo = new StringBuilder();//控件追加信息 // Start is called before the first frame update void Start() { InpIPAdress.text = "127.0.0.1"; InpPort.text = "1000"; } //客户端退出系统 public void ExitSystem() { //关闭客户端Socket if (_SockClient != null) { try { //关闭连接 _SockClient.Shutdown(SocketShutdown.Both); } catch { } //清理资源 _SockClient.Close(); } //退出 Application.Quit(); } //启动客户端连接 public void EnableClientCon() { //通讯IP和端口号 endPoint = new IPEndPoint(IPAddress.Parse(InpIPAdress.text), Convert.ToInt32(InpPort.text)); //建立客户端Socket _SockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { //建立连接 _SockClient.Connect(endPoint); //启动线程,监听服务端返回的消息 Thread thrListenMsgFromServer = new Thread(ListionMsgFromServer); thrListenMsgFromServer.IsBackground = true; thrListenMsgFromServer.Name = "thrListenMsgFromServer"; thrListenMsgFromServer.Start(); } catch (Exception) { throw; } //控件显示连接服务端成功 DisplayInfo("连接服务器成功!"); } /// <summary> /// 后台线程监听服务端返回的消息 /// </summary> public void ListionMsgFromServer() { try { while (true) { //开辟消息内存区域 byte[] byMsgArray = new byte[1024 * 0124]; //客户端接受服务器返回的数据 int intTrueMsgLengh = _SockClient.Receive(byMsgArray); //转化为字符串 string strMsg = System.Text.Encoding.UTF8.GetString(byMsgArray, 0, intTrueMsgLengh); //显示收到的消息 DisplayInfo("服务器返回消息:" + strMsg); } } catch { } finally { DisplayInfo("服务器断开连接"); _SockClient.Disconnect(true); _SockClient.Close(); } } //客户端发送数据到服务器 public void SendMsg() { string strSendMsg = InpSendMsg.text; if (!string.IsNullOrEmpty(strSendMsg)) { //字节转化 byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(strSendMsg); //发送 _SockClient.Send(byteArray); //显示发送内容 DisplayInfo("我:" + strSendMsg); //控件清空 InpSendMsg.text = string.Empty; } else { DisplayInfo("提示:发送信息不能为空!"); } } /// <summary> /// 主显示控件,显示消息 /// </summary> /// <param name="str"></param> private void DisplayInfo(string str) { str = str.Trim(); if (!string.IsNullOrEmpty(str)) { _SbDisplayInfo.Append(System.DateTime.Now.ToString()); _SbDisplayInfo.Append(" "); _SbDisplayInfo.Append(str); _SbDisplayInfo.Append(" "); InpDisplayInfo.text = _SbDisplayInfo.ToString(); } } // Update is called once per frame void Update() { } }