1. 程式人生 > >Unity中國象棋(五)——網路對戰的實現(服務端的基本功能)

Unity中國象棋(五)——網路對戰的實現(服務端的基本功能)

博主主要是用了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);
            }
        }
開啟服務後,非同步實現監聽和接收訊息,以保證伺服器不會阻塞。

通過接收到的訊息的頭一個位元組——協議位,來判斷客戶端傳送的訊息是要做什麼的

        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);
            }

        }
在介紹每一個從客戶端接收的訊息是幹什麼的之前(下一篇再整理),我們要先了解一下changeList(更新列表)和changOnlineCount(更新線上人數)這兩個函式
        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();
        }