ไลบรารีเครือข่ายประสิทธิภาพสูงและใช้งานง่าย รองรับไคลเอนต์พร้อมกัน 16k+ บนโทโพโลยีที่มีให้ทั้งหมด
ออกแบบมาสำหรับแอปพลิเคชันเรียลไทม์แบบกระจายผ่าน LAN หรืออินเทอร์เน็ต
จัดเตรียมโครงสร้างพื้นฐานสำหรับการส่งข้อความที่มีปริมาณงานสูง, P2P, Nat Traversal, Udp ที่เชื่อถือได้
พื้นที่เก็บข้อมูลนี้ประกอบด้วยแอสเซมบลีหลักหลักและแอสเซมบลีย่อยเฉพาะการทำให้เป็นซีเรียลไลซ์หลายรายการ
โปรดตรวจสอบหน้า Wiki สำหรับเอกสารโดยละเอียด
ไลบรารีเครือข่าย รวมตรรกะทั้งหมดที่เกี่ยวข้องกับระบบเครือข่าย เริ่มต้นจากไบต์ดิบไปจนถึงนามธรรม เช่น ล็อบบี้ P2P มีเทมเพลตทั่วไปสำหรับใช้กับซีเรียลไลซ์ประเภทใดก็ได้
Plug&Play โมเดลประสิทธิภาพสูง ทำงานกับไบต์ดิบ ใช้เป็นฐานสำหรับรุ่นระดับสูงกว่าด้วย
Tcp Server/Client model
ที่มีการบัฟเฟอร์แบบไดนามิกและระบบย่อยที่เข้าคิว ไบต์สามารถกระจัดกระจายได้เหมือนกับซ็อกเก็ตทั่วไปTcp Byte Message Server/Client
โดยที่ไบต์จะถูกส่งโดยมีส่วนหัวความยาว 4 ไบต์ ช่วยให้มั่นใจได้ถึงการส่งข้อความอะตอมมิกโดยไม่มีการกระจายตัวUdp Server/Client
udp ที่จำลองเซิร์ฟเวอร์ด้วยการปรับประสิทธิภาพให้เหมาะสมReliable Udp Client/Server
ซึ่งมีการใช้โปรโตคอล TCP สมัยใหม่ผ่าน Udpเกี่ยวข้องกับโมเดลทั่วไปซึ่งสามารถทำงานร่วมกับโปรโตคอลซีเรียลไลเซชันใดก็ได้
Generic Message Server/Client
ที่ออกแบบมาเพื่อส่งและรับข้อความต่อเนื่องแบบอะตอมมิกGeneric MessageProtocol Server/client
คล้ายกับด้านบน แต่มีคลาสผู้ให้บริการเพิ่มเติม "MessageEnvelope" ซึ่งใช้เป็นส่วนหัว/ข้อมูลเมตาP2P Relay Client/Server
โดยที่ Peers (ลูกค้า) ค้นพบกันและกันผ่านเซิร์ฟเวอร์ Relay สามารถใช้ Udp/Rudp/Tcp เพื่อสื่อสารได้ รองรับ Tcp & Udp HolepunchP2P Room/Lobby Server/Client
ต์ของโมเดลรีเลย์ที่เพื่อนสามารถกำหนดห้องได้ คล้ายกับเซิร์ฟเวอร์การค้นหาแมตช์ในเกมไลบรารีได้รับการทดสอบกับไคลเอนต์มากที่สุดเท่าที่ระบบปฏิบัติการ (Windows) รองรับ (พอร์ตไดนามิกประมาณ 16k) ความน่าเชื่อถือของข้อมูลรวมถึง RUDP ได้รับการทดสอบผ่านทางอินเทอร์เน็ตอย่างกว้างขวาง การเจาะรูของ Nat Traversal Udp ก็ได้รับการทดสอบทางอินเทอร์เน็ตด้วยเช่นกัน
หมายเหตุ: Libary มีการจัดสรรรหัสที่ไม่ปลอดภัยและการจัดสรรหน่วยความจำสแต็ก ส่วนที่ไม่ปลอดภัยได้รับการทดสอบอย่างดีและไม่มีการเปลี่ยนแปลง
โมเดลทั่วไปจากแอสเซมบลีหลักถูกนำไปใช้กับซีเรียลไลเซอร์เฉพาะ เหตุผลสำหรับการแบ่งนี้คือเพื่อหลีกเลี่ยงการพึ่งพาที่ไม่จำเป็น ลายเซ็นวิธีการและการใช้งานทั้งหมดเหมือนกัน ประกอบด้วย:
แพ็คเกจ Nuget มีจำหน่าย:
ไลบรารีเครือข่ายหลัก | โปรโตบุฟ | MessagePack | เน็ตเซราไลเซอร์ | เจสัน |
---|---|---|---|---|
การวัดประสิทธิภาพ Echo แบบไม่มีที่สิ้นสุดทำได้โดยการส่งชุดข้อความไปยังเซิร์ฟเวอร์และรับการตอบสนองแบบสะท้อน การตอบสนองแต่ละครั้งทำให้เกิดการร้องขอใหม่ การตอบสนองของเซิร์ฟเวอร์แต่ละครั้งจะนับเป็น 1 Echo
ข้อความเริ่มต้น 1,000 ข้อความ (ข้อความ 32 ไบต์ + 4 ส่วนหัว) แต่ละข้อความ:
โน๊ตบุ๊คเอเอ็มดีRyzen7 5800H
จำนวนลูกค้า | TCP Echo ต่อวินาที | SSL Echo ต่อวินาที |
---|---|---|
100 | 53,400,000 | 41,600,000 |
1,000 | 43,600,000 | 22,200,000 |
5,000 | 43,400,000 | 21,800,000 |
10,000 | 42,800,000 | 21,700,000 |
โน๊ตบุ๊ค Intel i9 13980HX
จำนวนลูกค้า | TCP Echo ต่อวินาที | SSL Echo ต่อวินาที |
---|---|---|
100 | 128,800,000 | 79,400,000 |
1,000 | 110,400,000 | 72,100,000 |
5,000 | 102,100,000 | 67,500,000 |
10,000 | 100,500,000 | 65,500,000 |
ซองข้อความเริ่มต้น 1,000 ซอง (เพย์โหลด 32 ไบต์ รวมทั้งหมด 48 ไบต์):
โน๊ตบุ๊คเอเอ็มดีRyzen7 5800H
จำนวนลูกค้า | Protobuf Echo ต่อวินาที | ปลอดภัย Protobuf Echo ต่อวินาที |
---|---|---|
100 | 9,440,000 | 8,050,000 |
1,000 | 8,780,000 | 7,480,000 |
5,000 | 8,360,000 | 7,390,000 |
10,000 | 8,340,000 | 7,350,000 |
โน๊ตบุ๊ค Intel i9 13980HX
จำนวนลูกค้า | Protobuf Echo ต่อวินาที | ปลอดภัย Protobuf Echo ต่อวินาที |
---|---|---|
100 | 31,200,000 | 20,650,000 |
1,000 | 30,500,000 | 19,500,000 |
5,000 | 28,200,000 | 17,650,000 |
10,000 | 26,400,000 | 16,000,000 |
การวัดประสิทธิภาพนี้เป็นเพียงการส่งซองข้อความที่มีเพย์โหลดไบต์แบบดิบเท่านั้น สำหรับประสิทธิภาพเฉพาะของการทำให้เป็นอนุกรม โปรดดูที่: SerializationBenchmarks
สำหรับข้อมูลโดยละเอียด โปรดดูที่ AsyncTcpClient/Server และ ByteMessageTcpClient/Server
อาร์เรย์ไบต์หรือเซ็กเมนต์อาร์เรย์ใดๆ จะไปถึงปลายทางโดยไม่มีการกระจายตัว
private static void ExampleByteMessage ( )
{
ByteMessageTcpServer server = new ByteMessageTcpServer ( 20008 ) ;
server . OnBytesReceived += ServerBytesReceived ;
server . StartServer ( ) ;
ByteMessageTcpClient client = new ByteMessageTcpClient ( ) ;
client . OnBytesReceived += ClientBytesReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
client . SendAsync ( Encoding . UTF8 . GetBytes ( "Hello I'm a client!" ) ) ;
void ServerBytesReceived ( Guid clientId , byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
server . SendBytesToClient ( clientId , Encoding . UTF8 . GetBytes ( "Hello I'm the server" ) ) ;
}
void ClientBytesReceived ( byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
}
}
output:
Hello I'm a client!
Hello I'm the server
หมายเหตุ: การตั้งค่าประสิทธิภาพที่สำคัญที่นี่คือว่าจะใช้บัฟเฟอร์หรือคิวเป็นนโยบายบัฟเฟอร์หรือไม่ ใช้บัฟเฟอร์หากข้อความส่วนใหญ่เป็นขอบเขตของไบต์[] เช่น (บัฟเฟอร์ ออฟเซ็ต นับ) ใช้ Queue ถ้าข้อความเต็มไบต์[] (0 ถึงสิ้นสุด)
client . GatherConfig = ScatterGatherConfig . UseQueue ;
server . GatherConfig = ScatterGatherConfig . UseBuffer ;
สำหรับความแตกต่างที่แตกต่างของ SSL คือ:
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
client = new SslByteMessageClient ( ccert ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
server = new SslByteMessageServer ( 8888 , scert ) ;
// You can override the SSL cerificate validation callback
server . RemoteCertificateValidationCallback += .. .
client . RemoteCertificateValidationCallback += .. .
สำหรับข้อมูลเพิ่มเติม โปรดดู SSLClient/Server และ SSLByteMessageClient/Server
เซิร์ฟเวอร์ฐาน/ไคลเอนต์ที่มีการถ่ายโอนไบต์ดิบ เมธอดและลายเซ็นการโทรกลับเหมือนกันกับโมเดลข้อความไบต์ ไม่มีการใช้โปรโตคอลบนเซิร์ฟเวอร์/ไคลเอนต์พื้นฐาน ดังนั้นไบต์อาจมีการกระจัดกระจายขึ้นอยู่กับขนาด MTU ของคุณ
AsyncTcpServer server = new AsyncTcpServer ( port : 20000 ) ;
AsyncTpcClient client = new AsyncTpcClient ( ) ;
// SSL variant
// null certificate or default constructor will generate self signed certificate
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
SslServer server = new SslServer ( 2000 , scert ) ;
SslClient client = new SslClient ( ccert ) ;
เครือข่ายแบบซีเรียลไลซ์เป็นการปรับใช้คลาสทั่วไปที่จัดทำโดย Core Library ซึ่งใช้ได้กับโปรโตคอลซีเรียลไลซ์ทั้งหมด
สำหรับข้อมูลเพิ่มเติม: เครือข่ายแบบอนุกรม
Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc)
ใช้โมเดลไคลเอนต์เซิร์ฟเวอร์ซึ่งมีการถ่ายโอนข้อความแบบซีเรียลไลซ์แบบอะตอมมิก ประกาศประเภทของคุณ:
[ ProtoContract ]
class SampleMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
PureProtoServer server = new PureProtoServer ( 11234 ) ;
server . StartServer ( ) ;
server . BytesReceived += ( clientId , bytes , offset , count ) =>
{
SampleMessage msg = server . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
msg . sample = "Jesse Lets cook" ;
server . SendAsync ( clientId , msg ) ;
} ;
PureProtoClient client = new PureProtoClient ( ) ;
client . Connect ( "127.0.0.1" , 11234 ) ;
client . BytesReceived += ( bytes , offset , count ) =>
{
SampleMessage msg = client . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
} ;
client . SendAsync ( new SampleMessage ( ) { sample = "Yo! Mr White" } ) ;
มีการใช้โปรโตคอลข้อความเพื่อห่อข้อความไดนามิกทุกประเภทด้วยส่วนหัวมาตรฐาน โปรดดูที่ Message Protocol สำหรับคำอธิบายโดยละเอียด
คุณสามารถประกาศประเภทเพย์โหลดของคุณ ซึ่งเป็นประเภทใดก็ได้ที่สามารถซีเรียลไลซ์ได้ด้วย protobuf
[ ProtoContract ]
class SamplePayload : IProtoMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
ตัวอย่างสำหรับตัวแปรที่ปลอดภัย:
private static async Task ExampleProtoSecure ( )
{
// null certificate or default constructor will generate self signed certificate
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
SecureProtoMessageServer server = new SecureProtoMessageServer ( 20008 , scert ) ;
server . StartServer ( ) ;
server . OnMessageReceived += ServerMessageReceived ;
var client = new SecureProtoMessageClient ( cert ) ;
client . OnMessageReceived += ClientMessageReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
var Payload = new SamplePayload ( ) { sample = "Hello" } ;
var messageEnvelope = new MessageEnvelope ( ) ;
messageEnvelope . Header = "PayloadTest" ;
// You can just send a message, get replies on ClientMessageReceived.
client . SendAsyncMessage ( messageEnvelope ) ;
client . SendAsyncMessage ( messageEnvelope , Payload ) ;
// Or you can wait for a reply async.
MessageEnvelope result = await client . SendMessageAndWaitResponse ( messageEnvelope , Payload ) ;
var payload = result . UnpackPayload < SamplePayload > ( ) ;
Console . WriteLine ( $ "Client Got Response { payload . sample } " ) ;
void ServerMessageReceived ( Guid clientId , MessageEnvelope message )
{
Console . WriteLine ( $ "Server Received message { message . Header } " ) ;
server . SendAsyncMessage ( clientId , message ) ;
}
void ClientMessageReceived ( MessageEnvelope message )
{
}
}
ProtoMessageServer
และ ProtoMessageClient
มีลายเซ็นที่เหมือนกัน ยกเว้นตัวสร้างที่ไม่ได้ใช้ใบรับรอง โมเดลนี้คือสิ่งที่ฉันใช้เป็นการส่วนตัวในโปรเจ็กต์อื่นๆ ของฉัน เช่น P2PVideocall และ Multiplayer Starfighter Game โดยพื้นฐานแล้ว คุณจะมีเซิร์ฟเวอร์ Relay อยู่ที่ไหนสักแห่งในเครือข่ายของคุณ ซึ่งสามารถทำหน้าที่เป็นฮับเครือข่ายท้องถิ่นใน LAN และ/หรือเปิดให้เชื่อมต่อจากอินเทอร์เน็ต หากเปิดใช้งานการส่งต่อพอร์ต
ไคลเอนต์รีเลย์ (เพียร์) เชื่อมต่อกับเซิร์ฟเวอร์รีเลย์และรับการแจ้งเตือนเกี่ยวกับการมีอยู่ของเพียร์อื่น เพื่อนร่วมงานสามารถส่งข้อความหากันผ่าน Relay Server หรือส่งถึงกันโดยตรง (Udp holepunch)
ตรวจสอบข้อมูลโดยละเอียดของ P2P Fore
เซิร์ฟเวอร์เป็นแบบพาสซีฟโดยสมบูรณ์ ช่วยให้เพื่อนคนอื่นๆ สามารถค้นพบและส่งข้อความหากันได้ นอกจากนี้ วิธีการข้ามผ่าน NAT เช่น การเจาะรู UDP ที่ให้ไว้เพื่อให้สามารถสื่อสารโดยตรงผ่านอินเทอร์เน็ตหรือ LAN (จนถึงตอนนี้มีเพียง UDP เท่านั้น แต่เรามี udp ที่เชื่อถือได้)
Relay Server Is Serialization Agnostic
ซึ่งหมายความว่าเพียร์เครือข่ายแบบอนุกรมใด ๆ (Protobuff, MessagePack ฯลฯ ) สามารถใช้เซิร์ฟเวอร์รีเลย์เดียวกันได้
หากต้องการใช้เซิร์ฟเวอร์ Relay เพียงประกาศเซิร์ฟเวอร์ของคุณเป็น:
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var server = new SecureProtoRelayServer ( 20010 , scert ) ;
server . StartServer ( ) ;
เซิร์ฟเวอร์รีเลย์ได้รับการกำหนดค่าไว้ล่วงหน้าแล้ว
Relay client คือที่ที่ตรรกะของแอปพลิเคชันของคุณถูกนำไปใช้ คุณสามารถใช้แอปพลิเคชันไคลเอนต์ของคุณบนเว็บเพื่อค้นหาและพูดคุยกัน
หากต้องการปฏิเสธไคลเอ็นต์:
// null certificate or default constructor will generate self signed certificate
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var client = new RelayClient ( cert ) ;
client . OnPeerRegistered += ( Guid peerId ) => ..
client . OnPeerUnregistered += ( Guid peerId ) => ..
client . OnMessageReceived += ( MessageEnvelope message ) => ..
client . OnUdpMessageReceived += ( MessageEnvelope message ) => ..
client . OnDisconnected += ( ) => ..
client . Connect ( "127.0.0.1" , 20010 ) ;
ลายเซ็นเมธอดและการเรียกกลับเหมือนกันกับโมเดลไคลเอ็นต์/เซิร์ฟเวอร์โปรโต (รวมถึง Payloads ด้วย) ความแตกต่างเพียงอย่างเดียวคือคุณต้องระบุ Peer Guid Id ปลายทาง มันมาจากเหตุการณ์ OnPeerRegistered เมื่อใดก็ตามที่เพียร์ใหม่เชื่อมต่อกับเซิร์ฟเวอร์รีเลย์ Relay Server รับประกันการซิงโครไนซ์ของชุดเพียร์ปัจจุบันด้วยความสอดคล้องกันในที่สุดระหว่างเพียร์ทั้งหมด ดังนั้นเพื่อนใหม่จะได้รับเพื่อนที่เชื่อมต่ออื่นๆ ทั้งหมดจากกิจกรรมนี้ และเพื่อนเก่าจะได้รับการอัปเดต
client . SendAsyncMessage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
client . SendUdpMesssage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
// Or with an async reply
MessageEnvelope response = await client . SendRequestAndWaitResponse ( destinationPeerId ,
new MessageEnvelope ( ) { Header = "Who Are You?" } ) ;
ข้อความ Udp สามารถเกินขีดจำกัดดาตาแกรมที่ 65,527 ไบต์ ระบบตรวจพบข้อความ udp ขนาดใหญ่เป็นข้อความจัมโบ้และส่งเป็นกลุ่ม การรับจุดสิ้นสุดด้วยจะพยายามสร้างข้อความใหม่ หากชิ้นส่วนทั้งหมดมาไม่ถึงภายในข้อความหมดเวลาจะหายไป ขนาดข้อความสูงสุดสำหรับ udp คือ 16,256,000 ไบต์
โปรโตคอล udp ที่เชื่อถือได้ใช้เป็นอัลกอริทึม TCP ที่ใช้งานผ่าน UDP
client . SendRudpMessage ( peerId , envelope ) ;
client . SendRudpMessage ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope ) ;
การสนับสนุน Nat Traversal/Holepunch:
// Udp
bool result = await client . RequestHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
// Tcp
bool result = await client . RequestTcpHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
หากสำเร็จ จะอนุญาตให้คุณส่งข้อความ udp โดยตรงระหว่างเพียร์ปัจจุบันและปลายทางสำหรับข้อความ udp ที่เหลือในทั้งสองทิศทาง
นี่คือส่วนขยายของเซิร์ฟเวอร์รีเลย์/ไคลเอนต์ เพิ่มเติมคือระบบห้องที่เพื่อนสามารถสร้างหรือเข้าร่วมห้อง สอบถามห้องว่าง ส่งข้อความไปยังห้อง (มัลติคาสต์) นอกจากนี้ยังเก็บระบบข้อความเดิมไว้เพื่อส่งข้อความ 1-1 ข้อความระหว่างเพื่อน
คุณสามารถเข้าร่วมได้หลายห้อง
Room Server Is Serialization Agnostic
ซึ่งหมายความว่าเพียร์เครือข่ายแบบซีเรียลไลซ์ใดๆ (Protobuf, MessagePack ฯลฯ) สามารถใช้เซิร์ฟเวอร์ห้องเดียวกันได้
Decleration ของเซิร์ฟเวอร์และไคลเอนต์
var server = new SecureProtoRoomServer ( 20010 , scert ) ;
server . StartServer ( ) ;
var client1 = new SecureProtoRoomClient ( cert ) ;
หากต้องการสร้าง/เข้าร่วมและออกจากห้อง เพียง:
client1 . CreateOrJoinRoom ( "Kitchen" ) ;
client1 . LeaveRoom ( "Kitchen" ) ;
การเรียกกลับห้องมีดังนี้ การเรียกกลับนี้จะถูกทริกเกอร์เมื่อคุณอยู่ในห้องเดียวกันเท่านั้น
client1 . OnPeerJoinedRoom += ( roomName , peerId ) => ..
client1 . OnPeerLeftRoom += ( roomName , peerId ) => ..
client1 . OnPeerDisconnected += ( peerId ) => ..
นอกเหนือจากการโทรกลับด้วยข้อความ 1-1 มาตรฐานแล้ว เรายังมีการโทรกลับด้วยข้อความในห้องอีกด้วย
client1 . OnTcpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnUdpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnTcpMessageReceived += ( message ) => ..
client1 . OnUdpMessageReceived += ( message ) => ..
ตัวแปร TCP ที่ปลอดภัยทั้งหมดใช้ซ็อกเก็ต SSL มาตรฐานพร้อมการรับรองความถูกต้อง/การตรวจสอบ TLS สำหรับข้อมูลเพิ่มเติม โปรดดูที่: ความปลอดภัย