【轉】Unity3d中的Tcp socket通訊(開源)
轉自:https://blog.csdn.net/hiramtan/article/details/72621787
HiSocket_unity
如何使用
功能
- Tcp socket
- Udp socket
- 可伸縮位元組表
- 高效能位元組塊緩衝區
- 訊息註冊和回撥
- 二進位制位元組訊息封裝
- Protobuf訊息封裝
- AES訊息加密
詳情
- Tcp和Udp都是採用主執行緒非同步連線的方式(避免主執行緒阻塞).
- 啟動傳送執行緒和接收執行緒處理資料傳輸(提高效能).
- 供使用者呼叫傳送或接受資料的API在主執行緒中(方便直接操作unity的元件)
- 監聽連線事件獲得當前的連線狀態.
- 監聽接收事件獲得接收的資料.
- 存在位元組陣列佇列,方便用來測試和資料重發.
- 高效能位元組緩衝區避免記憶體空間重複申請,減少GC.
- 如果使用Tcp協議需要實現IPackage介面處理粘包拆包.
- Ping介面因為mono底層的bug會在.net2.0平臺報錯(.net 4.6 沒有問題,或者也可以使用unity的介面獲得Ping,工程中有示例程式碼)
細節
Tcp connection
Tcp協議傳輸位元組流,使用者需要分割位元組流獲得正確的資料包,當建立一個tcp協議的socket時,需要傳入一個Package物件來封包和解包.private IPackage _packer = new Packer(); void Test() { _tcp = new
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
連線
_tcp.Connect("127.0.0.1", 7777);
- 1
斷開連線
當不再執行時需要主動呼叫介面斷開與伺服器的連線(比如響應unity的onapplicationquit執行時)void OnApplicationQuit() { _tcp.DisConnect(); }
- 1
- 2
- 3
- 4
連線狀態變化
如果想獲取當前的連線狀態,可以訂閱連線狀態事件.void Test() { _tcp.StateChangeEvent += OnState; } void OnState(SocketState state) { Debug.Log("current state is: " + state); if (state == SocketState.Connected) { Debug.Log("connect success"); //can send or receive message } else if (state == SocketState.DisConnected) { Debug.Log("connect failed"); } else if (state == SocketState.Connecting) { Debug.Log("connecting"); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
傳送訊息
void Test() { var bytes = BitConverter.GetBytes(100); _tcp.Send(bytes); }
- 1
- 2
- 3
- 4
- 5
接受訊息
You can regist receiveevent and when message come from server, this event will be fire.void Test() { _tcp.ReceiveEvent += OnReceive; } void OnReceive(byte[] bytes) { Debug.Log("receive msg: " + BitConverter.ToInt32(bytes, 0)); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
封包和解包
最初建立連線時我們定義了一個packer來分割資料包,當傳送訊息時我們在資料頭部插入訊息長度/當接收到訊息時我們根據頭部的訊息長度獲得資料包的大小.private bool _isGetHead = false; private int _bodyLength; public void Unpack(IByteArray reader, Queue<byte[]> receiveQueue) { if (!_isGetHead) { if (reader.Length >= 2)//2 is example, get msg's head length { var bodyLengthBytes = reader.Read(2); _bodyLength = BitConverter.ToUInt16(bodyLengthBytes, 0); } else { if (reader.Length >= _bodyLength)//get body { var bytes = reader.Read(_bodyLength); receiveQueue.Enqueue(bytes); _isGetHead = false; } } } } public void Pack(Queue<byte[]> sendQueue, IByteArray writer) { var bytesWaitToPack = sendQueue.Dequeue(); UInt16 length = (UInt16)bytesWaitToPack.Length;//get head lenth var bytesHead = BitConverter.GetBytes(length); writer.Write(bytesHead);//write head writer.Write(bytesWaitToPack);//write body }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
Udp
Udp connection
如果建立upd連線,需要指定傳送接收緩衝區大小._udp = new UdpConnection(1024);
- 1
Ping
public int PingTime; private Ping p; private float timeOut = 1; private float lastTime; void Start() { StartCoroutine(Ping()); } IEnumerator Ping() { p = new Ping("127.0.0.1"); lastTime = Time.realtimeSinceStartup; while (!p.isDone && Time.realtimeSinceStartup - lastTime < 1) { yield return null; } PingTime = p.time; p.DestroyPing(); yield return new WaitForSeconds(1); StartCoroutine(Ping()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 訊息註冊
- Protobuf
- 位元組訊息
- 加密
Tcp Example
Tcp 協議提供可靠有序的流位元組傳輸,使用者需要自己分割資料,在這個框架中可以繼承IPackage介面來實現.
private ITcp _tcp;
private IPackage _packer = new Packer();
// Use this for initialization
void Start()
{
_tcp = new TcpConnection(_packer);
_tcp.StateChangeEvent += OnState;
_tcp.ReceiveEvent += OnReceive;
Connect();
}
void Update()
{
_tcp.Run();
}
void Connect()
{
_tcp.Connect("127.0.0.1", 7777);
}
// Update is called once per frame
void OnState(SocketState state)
{
Debug.Log("current state is: " + state);
if (state == SocketState.Connected)
{
Debug.Log("connect success");
Send();
}
else if (state == SocketState.DisConnected)
{
Debug.Log("connect failed");
}
else if (state == SocketState.Connecting)
{
Debug.Log("connecting");
}
}
void OnApplicationQuit()
{
_tcp.DisConnect();
}
void Send()
{
for (int i = 0; i < 10; i++)
{
var bytes = BitConverter.GetBytes(i);
Debug.Log("send message: " + i);
_tcp.Send(bytes);
}
}
void OnReceive(byte[] bytes)
{
Debug.Log("receive msg: " + BitConverter.ToInt32(bytes, 0));
}
public class Packer : IPackage
{
public void Unpack(IByteArray reader, Queue<byte[]> receiveQueue)
{
//add your unpack logic here
if (reader.Length >= 1024)//1024 is example, it's msg's length
{
var bytesWaitToUnpack = reader.Read(1024);
receiveQueue.Enqueue(bytesWaitToUnpack);
}
}
public void Pack(Queue<byte[]> sendQueue, IByteArray writer)
{
var bytesWaitToPack = sendQueue.Dequeue();
// add your pack logic here
//
writer.Write(bytesWaitToPack);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
Udp Example
Udp協議提供不可靠的報文訊息,使用者無法知道當前連線狀態,但是訊息包時完整的.
private UdpConnection _udp;
// Use this for initialization
void Start()
{
_udp = new UdpConnection(1024);
_udp.ReceiveEvent += OnReceive;
Connect();
Send();
}
void Connect()
{
_udp.Connect("127.0.0.1", 7777);
}
// Update is called once per frame
void Update()
{
_udp.Run();
}
void Send()
{
for (int i = 0; i < 10; i++)
{
var bytes = BitConverter.GetBytes(i);
_udp.Send(bytes);
Debug.Log("send message: " + i);
}
}
private void OnApplicationQuit()
{
_udp.DisConnect();
}
void OnReceive(byte[] bytes)
{
Debug.Log("receive bytes: " + BitConverter.ToInt32(bytes, 0));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
Message Registration Example
void RegistMsg()
{
MsgRegister.Regist("10001", OnMsg_Bytes);
MsgRegister.Regist("10002", OnMsg_Protobuf);
}
void OnMsg_Bytes(IByteArray byteArray)
{
var msg = new MsgBytes(byteArray);
int getInt = msg.Read<int>();
}
void OnMsg_Protobuf(IByteArray byteArray)
{
var msg = new MsgProtobuf(byteArray);
GameObject testClass = msg.Read<GameObject>();//your class's type
var testName = testClass.name;
}
相關推薦
【轉】Unity3d中的Tcp socket通訊(開源)
轉自:https://blog.csdn.net/hiramtan/article/details/72621787HiSocket_unity如何使用可以從此連結下載最新的unity package: 功能Tcp socketUdp socket可伸縮位元組表高效能位元組塊
【Qt】Qt之程序間通訊(QProcess)【轉】
簡述 前幾節裡,分享了程序通訊的幾種方式:Windows訊息機制、Shared Memory(共享記憶體),本節講解下關於程序通訊的另外一種方式-QProcess。 簡述 命令列讀取 說明 實現 更多參考 命令列引數啟動 說明 程序A-帶參啟動
【Qt】Qt之程序間通訊(IPC)【轉】
簡述 程序間通訊,就是在不同程序之間傳播或交換資訊。那麼不同程序之間存在著什麼雙方都可以訪問的介質呢?程序的使用者空間是互相獨立的,一般而言是不能互相訪問的,唯一的例外是共享記憶體區。但是,系統空間卻是“公共場所”,所以核心顯然可以提供這樣的條件。除此以外,那就是雙方都可以訪問的外設了。在這個意義上,兩
【轉】深入淺出理解決策樹演算法(二)-ID3演算法與C4.5演算法
從深入淺出理解決策樹演算法(一)-核心思想 - 知乎專欄文章中,我們已經知道了決策樹最基本也是最核心的思想。那就是其實決策樹就是可以看做一個if-then規則的集合。我們從決策樹的根結點到每一個都葉結點構建一條規則。 並且我們將要預測的例項都可以被一條路徑或者一條規則所覆蓋。 如下例:假設我
【轉】深入淺出理解決策樹演算法(一)-核心思想
演算法思想 決策樹(decision tree)是一個樹結構(可以是二叉樹或非二叉樹)。 其每個非葉節點表示一個特徵屬性上的測試,每個分支代表這個特徵屬性在某個值域上的輸出,而每個葉節點存放一個類別。 使用決策樹進行決策的過程就是從根節點開始,測試待分類項中相應的特徵屬性,並按照其值選擇
QT TCP socket通訊(一)
TCP即Transmission Control Protocol,傳輸控制協議。與UDP不同,它是面向連線和資料流的可靠傳輸協議。也就是說,它能使一臺計算機上的資料無差錯的發往網路上的其他計算機,所以當要傳輸大量資料時,我們選用TCP協議。 TCP協議的程式使用的是客
【轉】深入理解Java:註解(Annotation)--註解處理器
display 枚舉 lec con null cto run toolbar int https://www.cnblogs.com/peida/archive/2013/04/26/3038503.html 如果沒有用來讀取註解的方法和工作,那麽註解也就
【轉】js中15個常用的正則表達式
顏色 字符 8.4 特殊字符 cnp 浮點數 == div mail 1 用戶名正則 //用戶名正則,4到16位(字母,數字,下劃線,減號) var uPattern = /^[a-zA-Z0-9_-]{4,16}$/; //輸出 true console.log(uPa
【轉】VS2010中 C++創建DLL圖解
-a rar cls ret ria endif -s pan 項目 標簽: dllc++2010threadlibraryc 本文章已收錄於: .embody { padding: 10px 10px 10px; margin: 0 -20px; b
【轉】QT中QWidget、QDialog及QMainWindow的區別
屏幕 編輯 派生 標記 裝飾 按鈕 set 沒有 idg QWidget類是所有用戶界面對象的基類。 窗口部件是用戶界面的一個基本單元:它從窗口系統接收鼠標、鍵盤和其它事件,並且在屏幕上繪制自己。每一個窗口部件都是矩形的,並且它們按Z軸順序排列。一個窗口部件可以被它的父窗口
【轉】C#中的兩把雙刃劍:抽象類和接口
實例 可維護 對象 為什麽不使用 程序 一定的 代碼 方式 索引 轉:http://www.cnblogs.com/djzxjblogs/p/7587735.html 第一次面試的時候, 面試官問我,抽象類和接口的區別。 本人也是,按照面試寶典上的回答,說了一大堆。
【轉】C++中const在函數名前面和函數後面的區別
const成員函數 調用 參數傳遞 成員 指向 int 區別 ... urn 一、概念 當const在函數名前面的時候修飾的是函數返回值,在函數名後面表示是常成員函數,該函數不能修改對象內的任何成員,只能發生讀操作,不能發生寫操作。 二、原理: 我們都知道在調用
【轉】瀏覽器中輸入url後發生了什麽
正常 工作 orb 問題: serve es2017 背景 ace perm 原文地址:http://www.jianshu.com/p/c1dfc6caa520 在學習前端的過程中經常看到這樣一個問題:當你在瀏覽器中輸入url後發生了什麽?下面是個人學習過程中的總結,供個
【轉】js中的事件委托或是事件代理詳解
ava 程序 fff 員工 我們 cnblogs 上傳 on() 類型 起因: 1、這是前端面試的經典題型,要去找工作的小夥伴看看還是有幫助的; 2、其實我一直都沒弄明白,寫這個一是為了備忘,二是給其他的知其然不知其所以然的小夥伴們以參考; 概述: 那什麽叫
【轉】SQL中的取整函數FLOOR、ROUND、CEIL、TRUNC、SIGN
log rec 截取 符號 floor 個數 clas 絕對值 sign --------------------------------------------------------------------------1 trunc(value,precision)按精
AppDomain 詳解二【轉】-C#中動態加載和卸載DLL
all created 新版本 odin generic reflect 可能 params 詳細 在C++中加載和卸載DLL是一件很容易的事,LoadLibrary和FreeLibrary讓你能夠輕易的在程序中加載DLL,然後在任何地方 卸載。在C#中我們也能使用Asse
【轉】Python中操作mysql的pymysql模塊詳解
定義 padding 參數化查詢 finall 支持 順序 執行sql mysq syntax Python中操作mysql的pymysql模塊詳解 前言 pymsql是Python中操作MySQL的模塊,其使用方法和MySQLdb幾乎相同。但目前pymysql支持p
【轉】CSS中的浮動和清除浮動
但是 spa 下拉 而已 ges 推薦 授權 自己的 -c 以下轉自《CSS中的浮動和清除浮動,梳理一下!》 浮動到底是什麽? 浮動核心就一句話:浮動元素會脫離文檔流並向左/向右浮動,直到碰到父元素或者另一個浮動元素。請默念3次! 浮動最初設計的目
【轉】C#中Func與Action的理解
.net ring UNC 簡單 代碼 操作 不必要 返回值 tps 原文地址:https://www.cnblogs.com/ultimateWorld/p/5608122.html Action 與 Func是.NET類庫中增加的內置委托,以便更加簡潔方便的使用委托。最
android開發學習 ------- 【轉】 android中的單例模式 (詳解)
lan post tail -- and 使用 href details android開發 https://blog.csdn.net/u011418943/article/details/60139644 這篇文章 前因後果 都說出來了 ,值得學習。 htt