Unity中國象棋(五)——網路對戰的實現(服務端的基本功能)
阿新 • • 發佈:2019-02-18
博主主要是用了socket的方法去實現了伺服器和客戶端之間的通訊,由於樓主對於socket網路程式設計這一方面的基礎相當薄弱,故也是邊學邊學,有些地方的程式碼不夠完善還請大神指正!博主還需學習。
首先,先說說服務端方面:服務端採用的是VS的windows form窗體應用程式做的,也是為了方便檢視和操作吧。
窗體長這樣
首先先宣告一個公共類
public class Common { /// <summary> /// 儲存伺服器來的訊息 /// </summary> public static byte[] ReceiveBuffer = new byte[1024 * 1024]; /// <summary> /// 監聽用的socket /// </summary> public static Socket ListenSocket; /// <summary> /// 儲存所有負責通訊用是socket /// </summary> public static Dictionary<string, Socket> connSocket = new Dictionary<string, Socket>(); }
服務端首先要開啟服務,點選“啟動伺服器”按鈕,則可以從兩個TextBox中讀取輸入的IP地址和埠號的資訊,通過這個資訊開啟服務
開啟服務後,非同步實現監聽和接收訊息,以保證伺服器不會阻塞。void StartServer() { try { string _ip = tbIp.Text; int _point = int.Parse(tbPoint.Text); //建立監聽客戶端請求的socket Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //監聽用的ip和埠 IPAddress address = IPAddress.Parse(_ip); IPEndPoint point = new IPEndPoint(address, _point); //繫結 socket.Bind(point); socket.Listen(10); //非同步 開始監聽 socket.BeginAccept(new AsyncCallback(Listen), socket); //禁用當前按鈕 btnStart.Enabled = false; //啟動時間 startTime.Text = DateTime.Now.ToString(); //底部提示訊息 tssMsg.Text = "伺服器已經啟動"; } catch (Exception ex) { MessageBox.Show(ex.Message); } }
通過接收到的訊息的頭一個位元組——協議位,來判斷客戶端傳送的訊息是要做什麼的
在介紹每一個從客戶端接收的訊息是幹什麼的之前(下一篇再整理),我們要先了解一下changeList(更新列表)和changOnlineCount(更新線上人數)這兩個函式void Listen(IAsyncResult result) { try { //獲取監聽的socket Socket clientSocket = result.AsyncState as Socket; //與伺服器通訊的socket Socket connSocket = clientSocket.EndAccept(result); string ip = connSocket.RemoteEndPoint.ToString(); //連線成功。儲存資訊 if (!Common.connSocket.ContainsKey(ip)) Common.connSocket.Add(ip, connSocket); //連線成功,更新伺服器資訊 changeList(connSocket); //等待新的客戶端連線 ,相當於迴圈呼叫 clientSocket.BeginAccept(new AsyncCallback(Listen), clientSocket); //接收來自客戶端資訊 ,相當於迴圈呼叫 connSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), connSocket); } catch (Exception ex) { MessageBox.Show(ex.Message); } } void Receive(IAsyncResult result) { //與客戶端通訊的socket Socket clientSocket = result.AsyncState as Socket; string ip = clientSocket.RemoteEndPoint.ToString(); try { //獲取實際的長度值 int num = clientSocket.EndReceive(result); if (num > 0) { byte[] data = new byte[num]; //複製實際的長度到data位元組陣列中 Array.Copy(Common.ReceiveBuffer, 0, data, 0, num); //判斷協議位 int command = data[0]; switch (command) { case 1: //伺服器儲存傳送方的IP和其棋子的顏色,並尋求配對 MatchingRival(data,ip); break; case 2: //接收發送方發來的移動棋子的訊息,並轉發給接收方 SendMoveMessageToClient(data); break; case 3: //接收發送方的悔棋訊息,並轉發給接收方 SendRetractMessageToClient(data); break; case 6: //接收到傳送方想要確定接收方是否線上的意願,並做檢測 SendDisconnectMessageToClient(data); break; default: break; } //接收其他資訊 clientSocket.BeginReceive(Common.ReceiveBuffer, 0, Common.ReceiveBuffer.Length, 0, new AsyncCallback(Receive), clientSocket); } else //客戶端斷開 { clientOff(clientSocket); } } catch (Exception ex) { clientOff(clientSocket); } }
void changeList(Socket socket)
{
//獲取客戶端資訊 ip和埠號
string ip = socket.RemoteEndPoint.ToString();
//客戶端登陸時間
string time = DateTime.Now.ToString();
//跨執行緒操作ui
this.Invoke(new Action(() =>
{
//新增一行
dgvList.Rows.Add();
//獲取當前dgvList的行
int rows = dgvList.Rows.Count;
//賦值
dgvList.Rows[rows - 1].Cells[0].Value = ip;
dgvList.Rows[rows - 1].Cells[1].Value = time;
//把ip當作當前行的tag標記一下,為了刪除行的時候可以找到該行
dgvList.Rows[rows - 1].Tag = ip;
//更新線上人數
changOnlineCount(true);
}));
}
void changOnlineCount(bool tag)
{
int num = 0;
if (tag) num = int.Parse(lbCount.Text) + 1;
else num = int.Parse(lbCount.Text) - 1;
this.Invoke(new Action(() =>
{
//更新線上人數
lbCount.Text = num.ToString();
if (num == 0) Common.connSocket.Clear();
}));
}
當有客戶端連線的時候,首先會執行一次changeList函式,將所連線的客戶端的資訊顯示在DataGridView裡面,並在“線上人數”處+1
同樣的,當有客戶端斷開的時候,也會更新列表,“線上人數”-1,刪除在DataGridView裡面的資訊,並關閉該連線
void clientOff(Socket clientSocket)
{
//從集合刪除下線的ip
string outIp = clientSocket.RemoteEndPoint.ToString();
if (Common.connSocket.ContainsKey(outIp))
Common.connSocket.Remove(outIp);
//更新伺服器線上人數
changOnlineCount(false);
this.Invoke(new Action(() =>
{
//更新列表
//刪除退出的ip
for (int i = 0; i < dgvList.Rows.Count; i++)
{
if (dgvList.Rows[i].Tag.ToString() == outIp)
{
dgvList.Rows.RemoveAt(i);
break;
}
}
}));
clientSocket.Shutdown(SocketShutdown.Receive);
clientSocket.Close();
}