Unity客戶端與Netty服務器通信搭建(二):Unity客戶端程序

前言

最近和幾個同學一起做一個手游項目,雖然是單機,但是有檢測新版本的需求,因此還是要搭建一個服務器來實現客戶端檢測更新的功能。在朋友的推薦下,我選擇使用Netty的服務器架構,關于Netty的優點可以百度下,這里不再累贅。從沒聽過Netty到實現與Unity客戶端的通信總共花了兩天,其中遇到了很多坑,我將在這系列文章中復盤這兩天的開發流程,給想要入門Unity客戶端+Netty服務端通信的同學帶下路,少走一些彎路。

參考文章:http://www.itstack.org/?post=18

一、開發環境搭建


我的開發環境
  • Mac OS 10.11.6
  • Unity 5.3.5 P6
  • 編程語言:C#

二、Unity客戶端代碼


Unity工程
工程截圖.png

工程包含兩個C#文件

  • nettyComponent.cs

將nettyComponent.cs拖拽到Unity場景中的Gameobject即可

  • nettyClient.cs

nettyClient.cs實現了:
1.連接服務器
2.網絡檢測
3.失敗重連

nettyComponent.cs

using UnityEngine;
using System.Collections;

public class nettyComponent : MonoBehaviour {

public string IP = "127.0.0.1";
public int Port = 7397;

nettyClient client;
mySocket mysocket;

// Use this for initialization
void Start () {
    //獲得nettyClient實例
    client = nettyClient.GetInstance (IP,Port);
}


// Update is called once per frame
void Update () {
    if (Input.GetKeyDown (KeyCode.Escape))
        client.Closed ();
    }
}

nettyClient.cs

using UnityEngine;
using System.Collections;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;

public class nettyClient {

public string IP = "127.0.0.1";
public int Port = 7397;

public bool isConnected;

//信息接收進程
private Thread _ReceiveThread = null;
//網絡檢測進程
private Thread _connectionDetectorThread = null;

private Socket clientSocket = null;
private static byte[] result = new byte[1024];

//單例模式
private static nettyClient instance;

public static nettyClient GetInstance()
{
    if (instance == null)
    {
        instance = new nettyClient();
    }
    return instance;
}

public static nettyClient GetInstance(string ip,int port)
{
    if (instance == null)
    {
        instance = new nettyClient(ip,port);
    }
    return instance;
}

//默認服務器IP地址構造函數
public nettyClient()
{
    startConnect ();

    //初始化網絡檢測線程
    _connectionDetectorThread = new Thread (new ThreadStart (connectionDetector));
    //開啟網絡檢測線程[用于檢測是否正在連接,否則重新連接]
    _connectionDetectorThread.Start ();

}

//自定義服務器IP地址構造函數
public nettyClient(string ip,int port)
{
    IP = ip;
    Port = port;

    startConnect ();

    //初始化網絡檢測線程
    _connectionDetectorThread = new Thread (new ThreadStart (connectionDetector));
    //開啟網絡檢測線程[用于檢測是否正在連接,否則重新連接]
    _connectionDetectorThread.Start ();

}

private void startConnect()
{

    //創建Socket對象, 這里我的連接類型是TCP
    clientSocket = new Socket (AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

    //服務器IP地址
    IPAddress ipAddress = IPAddress.Parse (IP);
    //服務器端口
    IPEndPoint ipEndpoint = new IPEndPoint (ipAddress, Port);
    //這是一個異步的建立連接,當連接建立成功時調用connectCallback方法
    IAsyncResult result = clientSocket.BeginConnect (ipEndpoint,new AsyncCallback (connectCallback),clientSocket);
    //這里做一個超時的監測,當連接超過5秒還沒成功表示超時
    bool success = result.AsyncWaitHandle.WaitOne( 5000, true );
    if ( !success )
    {
        //超時
        clientSocket.Close();
        Debug.Log("connect Time Out");

        if (_ReceiveThread != null)
                _ReceiveThread.Abort();
    //          Closed();
    }else
            {
        //如果連接成功則開啟接受進程,發送信息
        if (clientSocket.Connected) {
            
            this.isConnected = true;

            //初始化線程
            _ReceiveThread = new Thread (new ThreadStart (Receive));
            //開啟線程[用于接收數據]
            _ReceiveThread.Start ();

            //發送數據
            Send ();
        }
    }


}

/// <summary>
/// 發送數據
/// </summary>
public void Send()
{
    for (int i = 0; i < 2; i++)
    {
        Thread.Sleep(1000);
        //UTF8編碼
        clientSocket.Send(System.Text.Encoding.UTF8.GetBytes((i+1)+"=> Netty服務端您好\r\n"));
    }
}

//向服務端發送一條字符串
//一般不會發送字符串 應該是發送數據包
public void SendMessage(string str)
{
    
    byte[] msg = System.Text.Encoding.UTF8.GetBytes(str);

    if(!clientSocket.Connected)
    {
        clientSocket.Close();
        return;
    }
    try
    {
        IAsyncResult asyncSend = clientSocket.BeginSend (msg,0,msg.Length,SocketFlags.None,new AsyncCallback (sendCallback),clientSocket);
        bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );

        if ( !success )
        {
            clientSocket.Close();
            Debug.Log("Failed to SendMessage server.");
        }else 
            Debug.Log("Message has been sent!");
    }
    catch
    {
        Debug.Log("send message error" );
    }
}

/// <summary>
/// 接收數據線程
/// </summary>
public void Receive()
{
    int receiveLength = 0;

    try
    {
        while (true)
        {
            if(!clientSocket.Connected)
            {
                //與服務器斷開連接跳出循環
                Debug.Log("Failed to clientSocket server.");
                clientSocket.Close();
                break;
            }

            try
            {
                //Receive方法中會一直等待服務端回發消息
                //如果沒有回發會一直在這里等著。
                int i = clientSocket.Receive(result);
                if(i <= 0)
                {
                    clientSocket.Close();

                    _ReceiveThread.Abort();

                    Debug.Log("斷開連接");
                    break;
                }

                if((receiveLength = clientSocket.Receive(result)) > 0)
                {
                    //UTF8解碼
                    Console.WriteLine("接收服務器消息:{0}", Encoding.UTF8.GetString(result, 0, receiveLength));
                    Debug.Log(Encoding.UTF8.GetString(result, 0, receiveLength));
                }
            }
            catch (Exception ex)
            {
                Debug.Log("Failed to clientSocket error." + ex);
                clientSocket.Close();
            }
        }
    }
    catch (Exception)
    {

        throw;
    }
}

/// <summary>
/// 重新連接線程
/// </summary>
public void connectionDetector()
{
    try
    {
        int connectTime = 0;

        while (true)
        {
            try
            {
                
                if(clientSocket.Connected)
                {
                    Debug.Log("網絡檢測中,連接狀態為:" + clientSocket.Connected);

                    connectTime = 0;
                }
                else if(!clientSocket.Connected)
                {
                    Debug.Log("網絡檢測中,連接狀態為:False");

                    this.isConnected = false;

                    //嘗試重連
                    Debug.Log("正在嘗試第"+ connectTime.ToString() +"次重連");
                    //連接
                    startConnect ();
                    //每5秒執行一次重連
                    Thread.Sleep(5000);

                    connectTime +=1 ;
                }
            }
            catch (Exception ex)
            {
                Debug.Log(ex);
            }
        }
    }
    catch (Exception)
    {

        throw;
    }
}

static void Main(string[] args)
{
    new nettyClient();
}

//發送信息-回調
private void sendCallback (IAsyncResult asyncSend)
{
    Debug.Log (asyncSend.AsyncState);
}

//連接-回調
private void connectCallback(IAsyncResult asyncConnect)
{
    
}

//關閉Socket
public void Closed()
{
    try{
        if(clientSocket != null && clientSocket.Connected)
        {
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();

        }
        clientSocket = null;

        //關閉線程
        _ReceiveThread.Abort ();
        _connectionDetectorThread.Abort ();

        Debug.Log ("已關閉Socket");
    }catch(Exception e) {
        throw;
    }
  }
}

三、本地測試


1.連接成功,收發信息正常
Unity控制臺
IntelliJ IDEA控制臺
2.連接失敗,嘗試重新連接
失敗重連
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容