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阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容