服務器端的整體思路:
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("\r\n"); 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("\r\n"); InpDisplayInfo.text = _SbDisplayInfo.ToString(); } } // Update is called once per frame void Update() { } }