這是一個獨立的 ENet 實現,具有針對 C、C++、C# 和其他語言的修改協定。
特徵:
請閱讀常見錯誤,以了解可能發生的問題。
要建立本機庫,需要適當的軟體:
對於桌面平台 CMake 與 GNU Make 或 Visual Studio。
對於行動平台,適用於 Android 的 NDK 和適用於 iOS 的 Xcode。確保所有編譯的函式庫都分配給適當的平台和 CPU 架構。
要建立 Nintendo Switch 庫,請遵循本指南。
可以使用支援 C# 3.0 或更高版本的任何可用編譯平台來建立託管組件。
您可以從發布部分或 NuGet 取得已編譯的函式庫:
ENet-CSharp
包含用於 .NET 環境(.NET Standard 2.1)的本機函式庫的編譯組件。
ENet-Unity
包含帶有 Unity 原生程式庫的腳本。
強烈建議刪除包含二進位檔案的資料夾,而不是取代它來升級。
這些軟體套件僅適用於傳統平台:Windows、Linux 和 macOS (x64)。
支援的作業系統版本:
在開始工作之前,應使用ENet.Library.Initialize();
功能。
工作完成後,使用ENet.Library.Deinitialize();
功能。
using ( Host server = new Host ( ) ) {
Address address = new Address ( ) ;
address . Port = port ;
server . Create ( address , maxClients ) ;
Event netEvent ;
while ( ! Console . KeyAvailable ) {
bool polled = false ;
while ( ! polled ) {
if ( server . CheckEvents ( out netEvent ) <= 0 ) {
if ( server . Service ( 15 , out netEvent ) <= 0 )
break ;
polled = true ;
}
switch ( netEvent . Type ) {
case EventType . None :
break ;
case EventType . Connect :
Console . WriteLine ( "Client connected - ID: " + netEvent . Peer . ID + ", IP: " + netEvent . Peer . IP ) ;
break ;
case EventType . Disconnect :
Console . WriteLine ( "Client disconnected - ID: " + netEvent . Peer . ID + ", IP: " + netEvent . Peer . IP ) ;
break ;
case EventType . Timeout :
Console . WriteLine ( "Client timeout - ID: " + netEvent . Peer . ID + ", IP: " + netEvent . Peer . IP ) ;
break ;
case EventType . Receive :
Console . WriteLine ( "Packet received from - ID: " + netEvent . Peer . ID + ", IP: " + netEvent . Peer . IP + ", Channel ID: " + netEvent . ChannelID + ", Data length: " + netEvent . Packet . Length ) ;
netEvent . Packet . Dispose ( ) ;
break ;
}
}
}
server . Flush ( ) ;
}
using ( Host client = new Host ( ) ) {
Address address = new Address ( ) ;
address . SetHost ( ip ) ;
address . Port = port ;
client . Create ( ) ;
Peer peer = client . Connect ( address ) ;
Event netEvent ;
while ( ! Console . KeyAvailable ) {
bool polled = false ;
while ( ! polled ) {
if ( client . CheckEvents ( out netEvent ) <= 0 ) {
if ( client . Service ( 15 , out netEvent ) <= 0 )
break ;
polled = true ;
}
switch ( netEvent . Type ) {
case EventType . None :
break ;
case EventType . Connect :
Console . WriteLine ( "Client connected to server" ) ;
break ;
case EventType . Disconnect :
Console . WriteLine ( "Client disconnected from server" ) ;
break ;
case EventType . Timeout :
Console . WriteLine ( "Client connection timeout" ) ;
break ;
case EventType . Receive :
Console . WriteLine ( "Packet received from server - Channel ID: " + netEvent . ChannelID + ", Data length: " + netEvent . Packet . Length ) ;
netEvent . Packet . Dispose ( ) ;
break ;
}
}
}
client . Flush ( ) ;
}
Packet packet = default ( Packet ) ;
byte [ ] data = new byte [ 64 ] ;
packet . Create ( data ) ;
peer . Send ( channelID , ref packet ) ;
byte [ ] buffer = new byte [ 1024 ] ;
netEvent . Packet . CopyTo ( buffer ) ;
AllocCallback OnMemoryAllocate = ( size ) => {
return Marshal . AllocHGlobal ( size ) ;
} ;
FreeCallback OnMemoryFree = ( memory ) => {
Marshal . FreeHGlobal ( memory ) ;
} ;
NoMemoryCallback OnNoMemory = ( ) => {
throw new OutOfMemoryException ( ) ;
} ;
Callbacks callbacks = new Callbacks ( OnMemoryAllocate , OnMemoryFree , OnNoMemory ) ;
if ( ENet . Library . Initialize ( callbacks ) )
Console . WriteLine ( "ENet successfully initialized using a custom memory allocator" ) ;
使用方法與.NET環境下幾乎相同,只是控制台功能必須替換為Unity提供的功能。如果要在遊戲循環中呼叫Host.Service()
,請確保逾時參數設為 0,這表示非阻塞。此外,透過在播放器設定中啟用適當的選項,使 Unity 在背景運行。
最著名的策略是在獨立的 I/O 線程中使用 ENet,並利用線程間訊息傳遞技術在線程/任務之間傳輸數據,而無需任何鎖/互斥鎖。像環形緩衝區這樣的非阻塞佇列就是為此目的而設計的。高級抽象和邏輯可以使用工作執行緒進行並行化,然後與 I/O 執行緒進行通信,並將訊息入隊/出隊以透過網路發送/接收資料。
一般來說,ENet不是線程安全的,但是如果使用者夠小心的話,它的一些功能是可以安全使用的:
Packet
結構及其功能是安全的,直到資料包僅按值跨線程移動並且不使用自訂記憶體分配器。
Peer.ID
一旦從本機端獲得了指向對等點的指針,該 ID 將被緩存在Peer
結構中,以便對分配給該 ID 的物件進行進一步操作。 Peer
結構可以按值跨線程移動,但其功能不是線程安全的,因為記憶體中的資料可能會被另一個線程中的服務更改。
Library.Time
在內部利用原子原語來管理本地單調時間。
Peer.Send()
函數的標誌定義:
PacketFlags.None
不可靠的排序,不保證資料包的傳送。
PacketFlags.Reliable
可靠排序,目標對等方必須接收資料包,並且應嘗試重新傳送,直到資料包被傳送。
PacketFlags.Unsequenced
封包不會與其他資料包一起排序,並且可能會亂序傳送。該標誌使交付不可靠。
PacketFlags.NoAllocate
封包不會分配數據,使用者必須提供它。應使用PacketFreeCallback
回呼來追蹤封包生命週期。
PacketFlags.UnreliableFragmented
如果封包超過 MTU,則封包將被不可靠地分段。預設情況下,超過MTU的不可靠資料包會被分片並可靠傳輸。此標誌應用於明確指示應保持不可靠的資料包。
PacketFlags.Instant
封包在下一次服務迭代時不會與其他封包捆綁在一起,而是立即發送。這種傳輸類型以多路復用效率換取延遲。同一資料包不能用於多個Peer.Send()
呼叫。
PacketFlags.Unthrottled
排隊發送不可靠的資料包不應因限製而被丟棄,並在可能的情況下發送。
PacketFlags.Sent
封包已從其進入的所有佇列發送。
Event.Type
屬性的事件類型定義:
EventType.None
在指定的時間限制內沒有發生任何事件。
EventType.Connect
由Peer.Connect()
函數發起的連線請求已完成。 Event.Peer
傳回成功連線的對等點。 Event.Data
傳回使用者提供的描述連接的數據,如果沒有可用的數據,則傳回 0。
EventType.Disconnect
對等點已斷開連線。此事件在成功完成由Peer.Disconnect()
函數發起的斷開連線時產生。 Event.Peer
傳回已斷開連線的對等點。 Event.Data
傳回使用者提供的描述斷開連接的數據,如果沒有可用的數據,則傳回 0。
EventType.Receive
已從對等方收到資料包。 Event.Peer
傳回發送資料包的對等點。 Event.ChannelID
指定接收資料包的頻道號碼。 Event.Packet
傳回收到的資料包,使用後必須使用Event.Packet.Dispose()
函式銷毀該資料包。
EventType.Timeout
對等方已逾時。如果對等方逾時或由Peer.Connect()
初始化的連線請求逾時,則會發生此事件。 Event.Peer
傳回一個逾時的對等點。
Peer.State
屬性的對等狀態定義:
PeerState.Uninitialized
對等點未初始化。
PeerState.Disconnected
對等點已斷開連接或逾時。
PeerState.Connecting
正在進行中的對等連接。
PeerState.Connected
已成功連接對等點。
PeerState.Disconnecting
正在進行中的對等斷開連線。
PeerState.Zombie
對等點未正確斷開連接。
提供每個應用程式的事件。
AllocCallback(IntPtr size)
通知何時請求分配記憶體。需要指向新分配的記憶體的指標。應保留對委託的引用,以免被垃圾收集。
FreeCallback(IntPtr memory)
通知何時可以釋放記憶體。應保留對委託的引用,以免被垃圾收集。
NoMemoryCallback()
在記憶體不足時發出通知。應保留對委託的引用,以免被垃圾收集。
提供每包事件。
PacketFreeCallback(Packet packet)
在封包被銷毀時發出通知。指示是否確認了可靠的資料包。應保留對委託的引用,以免被垃圾收集。
提供每個主機的事件。
InterceptCallback(ref Event @event, ref Address address, IntPtr receivedData, int receivedDataLength)
在截獲原始 UDP 封包時發出通知。此回呼傳回的狀態代碼指示 ENet 應如何處理設定的事件。傳回 1 表示服務調度設定的事件。傳回 0 表示 ENet 子系統應該處理接收到的資料。返回-1表示有錯誤。應保留對委託的引用,以免被垃圾收集。
ChecksumCallback(IntPtr buffers, int bufferCount)
通知何時應在發送和接收時為緩衝區計算校驗和。此回調傳回的值是 64 位元校驗和。如果兩端都啟用了校驗和機制,ENet 會自動處理封包的完整性驗證。可與ENet.Library.CRC64()
函數一起使用。應保留對委託的引用,以免被垃圾收集。
包含具有匿名主機資料和連接埠號碼的結構。
Address.Port
取得或設定連接埠號碼。
Address.GetIP()
取得 IP 位址。
Address.SetIP(string ip)
設定 IP 位址。若要在本機網路中使用 IPv4 廣播,可以將用戶端的位址設定為255.255.255.255 。 ENet將自動回應廣播並將位址更新為伺服器的實際IP。
Address.GetHost()
嘗試從地址進行反向查找。傳回具有已解析名稱或 IP 位址的字串。
Address.SetHost(string hostName)
設定主機名稱或 IP 位址。應用於綁定到網路介面或連接到外部主機。成功時傳回 true,失敗時傳回 false。
包含事件類型的結構、指向對等點的託管指標、通道 ID、使用者提供的資料以及指向資料包的託管指標。
Event.Type
傳回事件的類型。
Event.Peer
傳回產生連線、斷開連線、接收或逾時事件的對等點。
Event.ChannelID
傳回產生事件的對等方的通道 ID(如果適用)。
如適用, Event.Data
傳回使用者提供的資料。
如果適用, Event.Packet
傳回與事件關聯的封包。
包含指向資料包的託管指標。
Packet.Dispose()
銷毀資料包。僅當從EventType.Receive
事件取得封包時才應呼叫。
Packet.IsSet
傳回託管指標的狀態。
Packet.Data
傳回指向封包資料的託管指標。
Packet.UserData
取得或設定使用者提供的資料。
Packet.Length
傳回封包中有效負載的長度。
Packet.HasReferences
檢查對封包的引用。
Packet.SetFreeCallback(PacketFreeCallback callback)
設定回呼以在適當的封包被銷毀時發出通知。可以使用指向回呼的指標IntPtr
來取代對委託的參考。
Packet.Create(byte[] data, int offset, int length, PacketFlags flags)
建立一個可以傳送到對等方的封包。 offset參數表示數組中資料的起始點,length為數組中資料的結束點。所有參數都是可選的。可以一次指定多個資料包標誌。可以使用指向本機緩衝區的指標IntPtr
來取代對位元組陣列的參考。
Packet.CopyTo(byte[] destination)
將有效負載從資料包複製到目標陣列。
包含指向對等點的託管指標和快取的 ID。
Peer.IsSet
傳回託管指標的狀態。
Peer.ID
傳回對等 ID。在客戶端它始終為零。
Peer.IP
以可列印的形式傳回 IP 位址。
Peer.Port
返回埠號。
Peer.MTU
返回 MTU。
Peer.State
傳回PeerState
枚舉中所描述的對等狀態。
Peer.RoundTripTime
返回以毫秒為單位的往返時間。
Peer.LastRoundTripTime
返回自上次確認以來的往返時間(以毫秒為單位)。
Peer.LastSendTime
傳回最後一個封包傳送時間(以毫秒為單位)。
Peer.LastReceiveTime
傳回最後一個資料包接收時間(以毫秒為單位)。
Peer.PacketsSent
傳回連線期間傳送的資料包總數。
Peer.PacketsLost
傳回根據重傳邏輯在連線期間被認為遺失的資料包總數。
Peer.PacketsThrottle
根據與對等點的連接條件傳回封包限制比率。
Peer.BytesSent
傳回連線期間傳送的位元組總數。
Peer.BytesReceived
傳回連線期間接收到的位元組總數。
Peer.Data
取得或設定使用者提供的資料。應與明確轉換為適當的資料類型一起使用。
Peer.ConfigureThrottle(uint interval, uint acceleration, uint deceleration, uint threshold)
為peer配置油門參數。為了回應對等點連接條件的變化,ENet 會丟棄不可靠的資料包。節流閥表示不可靠資料包不應該被丟棄並因此由 ENet 發送到對等點的機率。從發送可靠資料包到收到其確認的最短平均往返時間是在間隔參數指定的時間量(以毫秒為單位)內測量的。如果測量的往返時間恰好明顯小於在該時間間隔內測量的平均往返時間,則將增加節流機率以允許更多流量,加速參數中指定的量是與Library.throttleScale
常數。如果測量的往返時間恰好明顯大於在該時間間隔內測量的平均往返時間,則降低節流機率以將流量限制在減速度參數中指定的量,該量是與Library.throttleScale
。當節流閥的值為Library.throttleScale
時,ENet 不會丟棄任何不可靠的資料包,因此將發送 100% 的所有不可靠的資料包。當節流閥值為 0 時,所有不可靠資料包都會被 ENet 丟棄,因此將發送所有不可靠資料包的 0%。節流的中間值表示發送不可靠資料包的介於 0% 和 100% 之間的中間機率。考慮本地和外部主機的頻寬限制來確定節流機率的合理限制,即使在最佳條件下,節流機率也不應高於該限制。若要停用節流,應將減速度參數設為零。在具有高抖動和低平均延遲的不穩定網路環境中,閾值參數可用於減少相對於測量的往返時間的資料包限制,這是擁擠場所 Wi-Fi 網路的常見情況。預設情況下,閾值參數設定為Library.throttleThreshold
以毫秒為單位)。
Peer.Send(byte channelID, ref Packet packet)
將要發送的資料包排隊。成功時傳回 true,失敗時傳回 false。
Peer.Receive(out byte channelID, out Packet packet)
嘗試將任何傳入的排隊資料包出隊。如果資料包已出隊,則傳回 true;如果沒有可用資料包,則傳回 false。
Peer.Ping()
向對等方發送 ping 請求。 ENet 會定期自動 ping 所有連線的對等點,但是,可以呼叫此函數以確保更頻繁的 ping 要求。
Peer.PingInterval(uint interval)
設定傳送 ping 的時間間隔給對等點。 Ping 既可用於監控連接的活躍度,也可在低流量期間動態調整節流閥,以便節流閥在流量高峰期間具有合理的響應能力。
Peer.Timeout(uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum)
設定對等點的逾時參數。超時參數控制對等方因無法確認可靠流量而逾時的方式和時間。半線性機制中使用的超時值,其中如果在平均往返時間加上方差容差內未確認可靠資料包,直到逾時達到設定限制。如果逾時達到此限制,且已發送可靠資料包但在某個最小時間段內未確認,則對等方將中斷連線。或者,如果已發送可靠資料包但在某個最大時間段內未確認,則無論當前逾時限制值為何,對等方都會中斷連線。
Peer.Disconnect(uint data)
請求與對等點斷開連線。
Peer.DisconnectNow(uint data)
強制立即中斷與對等點的連線。
Peer.DisconnectLater(uint data)
請求與對等點斷開連接,但僅在所有排隊的傳出資料包發送完畢後才進行。
Peer.Reset()
強制斷開對等點的連接。對等方代表的外部主機不會收到斷開連線的通知,並且與本機的連線將逾時。
包含指向主機的託管指標。
Host.Dispose()
銷毀主機。
Host.IsSet
傳回託管指標的狀態。
Host.PeersCount
傳回已連接對等點的數量。
Host.PacketsSent
傳回會話期間傳送的資料包總數。
Host.PacketsReceived
傳回會話期間收到的資料包總數。
Host.BytesSent
傳回會話期間傳送的位元組總數。
Host.BytesReceived
傳回會話期間接收到的位元組總數。
Host.Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize)
建立一個用於與對等點通訊的主機。頻寬參數決定連接的視窗大小,這限制了在任何給定時間傳輸的可靠資料包的數量。 ENet 將有策略地在主機之間連接的特定端丟棄資料包,以確保主機的頻寬不會被淹沒。緩衝區大小參數用於設定傳送和接收資料封包的套接字緩衝區大小。所有參數都是可選的,除了位址和對等限制(在該函數用於建立偵聽傳入連接的主機的情況下)。
Host.PreventConnections(bool state)
阻止新傳入連線存取主機。此功能使主機在網路中完全不可見,任何嘗試連接到它的對等點都會逾時。
Host.Broadcast(byte channelID, ref Packet packet, Peer[] peers)
將資料包排隊,發送到一定範圍的對等點或發送到與主機關聯的所有對等點(如果未使用可選的對等點參數)。數組中任何歸零的Peer
結構將從廣播中排除。可以將單一Peer
傳遞給將從廣播中排除的函數,而不是數組。
Host.CheckEvents(out Event @event)
檢查主機上是否有任何排隊的事件,並在可用時調度一個事件。如果已調度事件,則傳回 > 0;如果沒有可用事件,則傳回 0;如果失敗,則傳回 < 0。
Host.Connect(Address address, int channelLimit, uint data)
發起與外部主機的連線。成功時傳回代表外部主機的對等點,失敗時拋出例外。在Host.Service()
通知EventType.Connect
事件之前,傳回的對等方不會完成連線。通道限制和使用者提供的資料參數是可選的。
Host.Service(int timeout, out Event @event)
等待指定主機上的事件並在主機與其對等方之間傳輸資料包。 ENet 使用輪詢事件模型來通知使用者重大事件。使用此功能對 ENet 主機進行輪詢以取得事件,其中可以指定可選的逾時值(以毫秒為單位)來控制 ENet 輪詢的時間長度。如果指定逾時為 0,則如果沒有要分派的事件,此函數將立即傳回。否則,如果在指定的逾時時間內調度了事件,它將傳回 1。應定期呼叫此函數以確保資料包的發送和接收,否則將出現流量峰值,導致延遲增加。逾時參數設定為 0 表示非阻塞,這在遊戲循環中呼叫函數的情況下是必要的。
Host.SetBandwidthLimit(uint incomingBandwidth, uint outgoingBandwidth)
調整主機的頻寬限制(以每秒位元組數為單位)。
Host.SetChannelLimit(int channelLimit)
限制未來傳入連線允許的最大通道數。
Host.SetMaxDuplicatePeers(ushort number)
限制來自相同主機的最大允許重複對等點,如果超過則阻止連線。預設為Library.maxPeers
,不能小於 1。
Host.SetInterceptCallback(InterceptCallback callback)
設定回呼以在攔截原始 UDP 封包時發出通知。可以使用指向回呼的指標IntPtr
來取代對委託的參考。
Host.SetChecksumCallback(ChecksumCallback callback)
設定回呼以通知何時應計算校驗和。可以使用指向回呼的指標IntPtr
來取代對委託的參考。
Host.Flush()
將指定主機上的任何排隊資料包傳送到其指定的對等方。
包含常量欄位。
Library.maxChannelCount
最大可能的通道數。
Library.maxPeers
最大可能的對等點數量。
Library.maxPacketSize
資料包的最大大小。
Library.version
相對於本機庫的目前相容性版本。
Library.Time
傳回目前本地單調時間(以毫秒為單位)。當應用程式保持活動狀態時,它永遠不會重置。
Library.Initialize(Callbacks callbacks)
初始化本機函式庫。 Callbacks 參數是可選的,並且只能與自訂記憶體分配器一起使用。應在開始工作之前調用。成功時傳回 true,失敗時傳回 false。
Library.Deinitialize()
取消初始化本機函式庫。工作完成後應呼叫。
Library.CRC64(IntPtr buffers, int bufferCount)
計算非託管緩衝區的校驗和。
此計畫由以下機構贊助:
飛鼠娛樂
平方根工作室
奇怪的循環遊戲