กรอบการทำงานสำหรับการสร้างและใช้บริการข้ามแพลตฟอร์มใน .Net Standard
ข้ามแพลตฟอร์ม ดูเพล็กซ์ ปรับขนาดได้ กำหนดค่าได้ และขยายได้
Xeeny เป็นเฟรมเวิร์กสำหรับการสร้างและใช้บริการบนอุปกรณ์และเซิร์ฟเวอร์ที่รองรับมาตรฐาน .net
ด้วย Xeeny คุณสามารถโฮสต์และใช้บริการได้ทุกที่ที่มาตรฐาน .net สามารถทำงานได้ (เช่น Xamarin android, Windows Server, ...) เป็น Cross Platform, Duplex, Multiple Transports, Asynchronous, Typed Proxies, กำหนดค่าได้ และขยายได้
Install-Package Xeeny
For extensions:
Install-Package Xeeny.Http
Install-Package Xeeny.Extentions.Loggers
Install-Package Xeeny.Serialization.JsonSerializer
Install-Package Xeeny.Serialization.ProtobufSerializer
คุณสมบัติปัจจุบัน:
กำลังมา:
public interface IService
{
Task < string > Echo ( string message ) ;
} public class Service : IService
{
public Task < string > Echo ( string message )
{
return Task . FromResult ( message ) ;
}
}ServiceHost โดยใช้ ServiceHostBuilder<TService> โดยที่การใช้งานบริการAddXXXServer var tcpAddress = "tcp://myhost:9999/myservice" ;
var httpAddress = "http://myhost/myservice" ;
var host = new ServiceHostBuilder < Service > ( InstanceMode . PerCall )
. AddTcpServer ( tcpAddress )
. AddWebSocketServer ( httpAddress ) ;
await host . Open ( ) ;ConnctionBuilder<T> var tcpAddress = "tcp://myhost/myservice" ;
var client = await new ConnectionBuilder < IService > ( )
. WithTcpTransport ( tcpAddress )
. CreateConnection ( ) ; var msg = await client . Echo ( "Hellow World!" ) ; public interface ICallback
{
Task OnCallback ( string serverMessage ) ;
}OperationContext.Current.GetCallback<T> public Service : IService
{
public Task < string > Join ( string name )
{
CallBackAfter ( TimeSpan . FromSeconds ( 3 ) ) ;
return Task . FromResult ( "You joined" ) ;
}
async void CallBackAfter ( TimeSpan delay )
{
var client = OperationContext . Current . GetCallback < ICallback > ( ) ;
await Task . Delay ( ( int ) delay . TotalMilliseconds ) ;
await client . OnCallBack ( "This is a server callback" ) ;
}
}WithCallback<T> กับตัวสร้าง var host = new ServiceHostBuilder < Service > ( InstanceMode . Single )
. WithCallback < ICallback > ( )
. AddTcpServer ( address )
. CreateHost ( ) ;
await host . Open ( ) ; public class Callback : ICallback
{
public void OnServerUpdates ( string msg )
{
Console . WriteLine ( $ "Received callback msg: { msg } " ) ;
}
}DuplexConnectionBuilder เพื่อสร้างไคลเอ็นต์ duplex โปรดทราบว่าเป็นคลาสทั่วไป อาร์กิวเมนต์ทั่วไปตัวแรกคือสัญญาบริการ ในขณะที่อีกอันคือการใช้งานการโทรกลับไม่ใช่อินเทอร์เฟซสัญญา ดังนั้นตัวสร้างจึงรู้ว่าจะสร้างอินสแตนซ์ประเภทใดเมื่อคำขอโทรกลับ ได้รับ. var address = "tcp://myhost/myservice" ;
var client = await new DuplexConnectionBuilder < IService , Callback > ( InstanceMode . Single )
. WithTcpTransport ( address )
. CreateConnection ( ) ;
await client . Join ( "My Name" ) ; Xeeny กำหนดสามโหมดสำหรับการสร้างอินสแตนซ์บริการ
คุณกำหนดโหมดอินสแตนซ์บริการโดยใช้ InstanceMode enum เมื่อสร้าง ServiceHost
var host = new ServiceHostBuilder < Service > ( InstanceMode . PerCall )
.. .
. CreateHost ( ) ;
await host . Open ( ) ; เมื่อคุณสร้างการเชื่อมต่อแบบดูเพล็กซ์ คุณจะส่งประเภทการโทรกลับและ InstanceMode ไปยัง DuplexConnectionBuilder InstanceMode ทำหน้าที่ในลักษณะเดียวกันกับบริการเมื่อสร้าง ServiceHost
ServiceHostBuilder มีโอเวอร์โหลดหนึ่งตัวที่รับอินสแตนซ์ของประเภทบริการ ซึ่งช่วยให้คุณสร้างอินสแตนซ์และส่งต่อไปยังตัวสร้าง ผลลัพธ์คือ InstanceMode.Single โดยใช้อ็อบเจ็กต์ที่คุณส่งผ่านServiceHostBuilder DuplextConnectionBuilder รับอินสแตนซ์ของประเภทการโทรกลับเพื่อให้คุณสามารถสร้างซิงเกิลตันได้ด้วยตัวเองPerCall และ PerConnection ถูกสร้างขึ้นโดยเฟรมเวิร์ก คุณยังคงสามารถเริ่มต้นอินสแตนซ์เหล่านั้นได้หลังจากถูกสร้างขึ้นและก่อนที่จะดำเนินการวิธีการใดๆ โดยการฟังเหตุการณ์: ServiceHost<TService>.ServiceInstanceCreated event และ DuplextConnectionBuilder<TContract, TCallback>.CallbackInstanceCreated host . ServiceInstanceCreated += service =>
{
service . MyProperty = "Something" ;
}
.. .
var builder = new DuplexConnectionBuilder < IService , Callback > ( InstanceMode . PerConnection )
. WithTcpTransport ( tcpAddress ) ;
builder . CallbackInstanceCreated += callback =>
{
callback .. .
}
var client = builder . CreateConnection ( ) ; Operation ที่ส่งผ่าน IsOneWay = true ในสัญญา (อินเทอร์เฟซ) public interface IService
{
[ Operation ( IsOneWay = true ) ]
void FireAndForget ( string message ) ;
} เมื่อคุณมีเมธอดโอเวอร์โหลดในอินเทอร์เฟซเดียว (หรือลายเซ็นเมธอดที่คล้ายกันในอินเทอร์เฟซหลัก) คุณต้องแยกแยะเมธอดเหล่านั้นโดยใช้แอตทริบิวต์ Operation โดยการตั้งค่าคุณสมบัติ Name สิ่งนี้ใช้กับทั้งสัญญาบริการและสัญญาติดต่อกลับ
public interface IOtherService
{
[ Operation ( Name = "AnotherEcho" ) ]
Task < string > Echo ( string message ) ;
}
public interface IService : IOhterService
{
Task < string > Echo ( string message ) ;
}
class Service : IService , IOtherService
{
public Task < string > Echo ( string message )
{
return Task . FromResult ( $ "Echo: { message } " ) ;
}
Task < string > IOtherService . Echo ( string message )
{
return Task . FromResult ( $ "This is the other Echo: { message } " ) ;
}
} คุณจะต้องเข้าถึงการเชื่อมต่อพื้นฐานเพื่อจัดการ เช่น การตรวจสอบสถานะ ฟังเหตุการณ์ หรือจัดการด้วยตนเอง (ปิดหรือเปิด) การเชื่อมต่อถูกเปิดเผยผ่านอินเทอร์เฟซ IConnection ซึ่งมีฟังก์ชันการทำงานเหล่านี้:
State : สถานะการเชื่อมต่อ: Connecting , Connected , Closing , ClosedStateChanged : เหตุการณ์เกิดขึ้นเมื่อสถานะการเชื่อมต่อเปลี่ยนแปลงConnect() : เชื่อมต่อกับที่อยู่ระยะไกลClose() : ปิดการเชื่อมต่อSessionEnded : เหตุการณ์เกิดขึ้นเมื่อการเชื่อมต่อกำลังปิด ( State เปลี่ยนเป็นการ Closing )Dispose() : กำจัดการเชื่อมต่อConnectionId : Guid ระบุการเชื่อมต่อแต่ละรายการ (สำหรับตอนนี้ Id บนเซิร์ฟเวอร์และไคลเอนต์ไม่ตรงกัน)ConnectionName : ชื่อการเชื่อมต่อที่เป็นมิตรเพื่อการดีบักและการวิเคราะห์บันทึกที่ง่ายขึ้นOperationContext.Current.GetConnection() ที่จุดเริ่มต้นของวิธีการของคุณและก่อนที่วิธีการบริการจะสร้างเธรดใหม่OperationContext.Current.GetConnection() แต่ส่วนใหญ่แล้วจะเกิดจากการเรียก OperationContext.Current.GetCallback<TCallback> อินสแตนซ์ที่ส่งคืนคืออินสแตนซ์ที่ปล่อยออกมาขณะรันไทม์และใช้สัญญาการโทรกลับของคุณ (กำหนดไว้ในพารามิเตอร์ทั่วไป TCallback ) ประเภทที่สร้างขึ้นอัตโนมัตินี้ใช้ IConnection เช่นกัน ดังนั้นเมื่อใดก็ตามที่คุณต้องการเข้าถึงฟังก์ชันการเชื่อมต่อของช่องทาง challback เพียงแค่ส่งไปที่ IConnection public class ChatService : IChatService
{
ConcurrentDictionary < string , ICallback > _clients = new ConcurrentDictionary < string , ICallback > ( ) ;
ICallback GetCaller ( ) => OperationContext . Current . GetCallback < ICallback > ( ) ;
public void Join ( string id )
{
var caller = GetCaller ( ) ;
_clients . AddOrUpdate ( id , caller , ( k , v ) => caller ) ;
( ( IConnection ) caller ) . SessionEnded += s =>
{
_clients . TryRemove ( id , out ICallback _ ) ;
} ;
}
} ไคลเอนต์คืออินสแตนซ์ของประเภทที่สร้างขึ้นอัตโนมัติซึ่งจะถูกส่งออกมาที่รันไทม์และใช้อินเทอร์เฟซสัญญาบริการของคุณ เมื่อรวมกับสัญญาแล้วประเภทที่ปล่อยออกมาจะใช้ IConnection ซึ่งหมายความว่าคุณสามารถส่งไคลเอนต์ใด ๆ (ดูเพล็กซ์หรือไม่ก็ได้) ไปยัง IConnection
var client = await new ConnectionBuilder < IService > ( )
. WithTcpTransport ( address )
. CreateConnection ( ) ;
var connection = ( IConnection ) client ;
connection . StateChanged += c => Console . WriteLine ( c . State ) ;
connection . Close ( )CreateConnection รับพารามิเตอร์ทางเลือกหนึ่งประเภทบูลีนซึ่งเป็น true ตามค่าเริ่มต้น การตั้งค่าสถานะนี้ระบุว่าการเชื่อมต่อที่สร้างขึ้นจะเชื่อมต่อกับเซิร์ฟเวอร์หรือไม่ โดยค่าเริ่มต้นทุกครั้งที่เรียกว่า CreateConnection การเชื่อมต่อที่สร้างขึ้นจะเชื่อมต่อโดยอัตโนมัติ บางครั้งคุณต้องการสร้างการเชื่อมต่อและต้องการเชื่อมต่อในภายหลัง โดยคุณจะต้องส่งค่า false ไปยังเมธอด CreateConnection จากนั้นจึงเปิดการเชื่อมต่อด้วยตนเองเมื่อคุณต้องการ var client = await new ConnectionBuilder < IService > ( )
. WithTcpTransport ( address )
. CreateConnection ( false ) ;
var connection = ( IConnection ) client ;
.. .
await connection . Connect ( ) ;ผู้สร้างทั้งหมดเปิดเผยตัวเลือกการเชื่อมต่อเมื่อคุณเพิ่มเซิร์ฟเวอร์หรือการขนส่ง ตัวเลือกคือ:
Timeout : ตั้งค่าการหมดเวลาการเชื่อมต่อ ( ค่าเริ่มต้น 30 วินาที )ReceiveTiemout : เป็นการหมดเวลาระยะไกลที่ไม่ได้ใช้งาน ( ค่าเริ่มต้นของเซิร์ฟเวอร์: 10 นาที ค่าเริ่มต้นของไคลเอ็นต์: Infinity )KeepAliveInterval : รักษาช่วงเวลาส่ง Ping ให้คงอยู่ ( ค่าเริ่มต้น 30 วินาที )KeepAliveRetries : จำนวนการลองใหม่ก่อนที่จะตัดสินใจว่าการเชื่อมต่อปิดอยู่ ( ค่าเริ่มต้นคือลองใหม่ 10 ครั้ง )SendBufferSize : ขนาดบัฟเฟอร์การส่ง ( ค่าเริ่มต้น 4096 ไบต์ = 4 KB )ReceiveBufferSize : ขนาดบัฟเฟอร์การรับ ( ค่าเริ่มต้น 4096 ไบต์ = 4 KB )MaxMessageSize : ขนาดสูงสุดของข้อความ ( ค่าเริ่มต้น 1000000 ไบต์ = 1 MB )ConnectionNameFormatter : มอบหมายให้ตั้งค่าหรือจัดรูปแบบ ConnectionName ( ค่าเริ่มต้นคือ null ) (ดูการบันทึก)SecuritySettings : การตั้งค่า SSL ( ค่าเริ่มต้นคือ null ) (ดูความปลอดภัย)คุณได้รับการดำเนินการกำหนดค่าตัวเลือกเหล่านี้บนเซิร์ฟเวอร์เมื่อคุณเรียก AddXXXServer:
var host = new ServiceHostBuilder < ChatService > ( InstanceMode . Single )
. WithCallback < ICallback > ( )
. AddTcpServer ( address , options =>
{
options . Timeout = TimeSpan . FromSeconds ( 10 ) ;
} )
. WithConsoleLogger ( )
. CreateHost ( ) ;
await host . Open ( ) ;ในฝั่งไคลเอ็นต์ คุณจะได้รับเมื่อโทร WithXXXTransport
var client = await new DuplexConnectionBuilder < IChatService , MyCallback > ( new MyCallback ( ) )
. WithTcpTransport ( address , options =>
{
options . KeepAliveInterval = TimeSpan . FromSeconds ( 5 ) ;
} )
. WithConsoleLogger ( )
. CreateConnection ( ) ; เมื่อคุณตั้งค่า Timeout และคำขอไม่เสร็จสมบูรณ์ในช่วงเวลานั้น การเชื่อมต่อจะถูกปิด และคุณต้องสร้างไคลเอนต์ใหม่ หากมีการตั้งค่า Timeout บนฝั่งเซิร์ฟเวอร์ซึ่งจะกำหนดการหมดเวลาการโทรกลับและการเชื่อมต่อจะถูกปิดเมื่อการโทรกลับไม่สมบูรณ์ในช่วงเวลานั้น โปรดจำไว้ว่าการโทรกลับเป็นการดำเนินการทางเดียวและการดำเนินการทางเดียวทั้งหมดจะเสร็จสมบูรณ์เมื่ออีกฝ่ายได้รับข้อความและก่อนที่วิธีระยะไกลจะถูกดำเนินการ
ReceiveTimeout คือ " Idle Remote Timeout " หากคุณตั้งค่าไว้บนเซิร์ฟเวอร์ มันจะกำหนดการหมดเวลาสำหรับเซิร์ฟเวอร์เพื่อปิดไคลเอนต์ที่ไม่ได้ใช้งานซึ่งเป็นไคลเอนต์ที่ไม่ได้ส่งคำขอหรือข้อความ KeepAlive ใด ๆ ในช่วงเวลานั้น
ReceiveTimeout บนไคลเอนต์ถูกตั้งค่าเป็น Infinity ตามค่าเริ่มต้น หากคุณตั้งค่าบนไคลเอนต์ดูเพล็กซ์ คุณกำลังสั่งให้ไคลเอนต์ละเว้นการโทรกลับที่ไม่ได้มาในช่วงเวลานั้น ซึ่งเป็นสถานการณ์แปลก ๆ แต่ก็ยังเป็นไปได้หากคุณเลือกที่จะทำเช่นนั้น .
ReceiveBufferSize คือขนาดของบัฟเฟอร์การรับ การตั้งค่าเป็นค่าต่ำจะไม่ส่งผลต่อความสามารถในการรับข้อความขนาดใหญ่ แต่ถ้าขนาดนั้นเล็กมากเมื่อเทียบกับข้อความที่จะรับ ก็จะแนะนำการดำเนินการ IO มากขึ้น คุณควรปล่อยให้ค่าเริ่มต้นอยู่ที่จุดเริ่มต้นหากจำเป็นให้ทำการทดสอบโหลดและวิเคราะห์เพื่อค้นหาขนาดที่ทำงานได้ดีและตรงบริเวณ
SendBufferSize คือขนาดของบัฟเฟอร์การส่ง การตั้งค่าเป็นค่าต่ำจะไม่ส่งผลต่อความสามารถในการส่งข้อความขนาดใหญ่ แต่ถ้าขนาดนั้นเล็กมากเมื่อเปรียบเทียบกับข้อความที่จะส่ง ก็จะแนะนำการดำเนินการ IO มากขึ้น คุณควรปล่อยให้ค่าเริ่มต้นอยู่ที่จุดเริ่มต้น หากจำเป็นให้ทำการทดสอบโหลดและวิเคราะห์เพื่อค้นหาขนาดที่ทำงานได้ดีและใช้หน่วยความจำน้อยลง
ReceiveBufferSize ของผู้รับควรเท่ากับ SendBufferSize ของผู้ส่ง เนื่องจากการขนส่งบางอย่างเช่น UDP จะทำงานได้ไม่ดีหากทั้งสองขนาดนี้ไม่เท่ากัน สำหรับตอนนี้ Xeeny จะไม่ตรวจสอบขนาดบัฟเฟอร์ แต่ในอนาคต ฉันกำลังแก้ไขโปรโตคอลเพื่อรวมการตรวจสอบนี้ในระหว่างการประมวลผล Connect
MaxMessageSize คือจำนวนไบต์สูงสุดที่อนุญาตให้รับได้ ค่านี้ไม่เกี่ยวข้องกับบัฟเฟอร์ ดังนั้นจึงไม่ส่งผลต่อหน่วยความจำหรือประสิทธิภาพ ค่านี้มีความสำคัญสำหรับการตรวจสอบไคลเอนต์ของคุณและป้องกันข้อความขนาดใหญ่จากไคลเอนต์ Xeeny ใช้โปรโตคอลคำนำหน้าขนาด ดังนั้นเมื่อข้อความมาถึงมันจะถูกบัฟเฟอร์บนบัฟเฟอร์ขนาด ReceiveBufferSize ซึ่งจะต้องเล็กกว่า MaxMessageSize มาก หลังจากที่ข้อความมาถึง ส่วนหัวของขนาดถูกอ่าน ถ้าขนาดใหญ่กว่า MaxMessageSize ข้อความจะถูกปฏิเสธและการเชื่อมต่อจะถูกปิด
Xeeny ใช้ข้อความ Keep-alive ของตัวเอง เนื่องจากการขนส่งบางประเภทไม่ได้มีกลไก Keep-alive ในตัว ข้อความเหล่านี้มีขนาด 5 ไบต์จากไคลเอ็นต์ไปยังเซิร์ฟเวอร์เท่านั้น ช่วงเวลา KeepAliveInterval คือ 30 วินาทีตามค่าเริ่มต้น เมื่อคุณตั้งค่าไว้บนไคลเอนต์ ไคลเอนต์จะส่งข้อความ ping หากไม่สามารถส่งสิ่งใดได้สำเร็จในช่วง KeepAliveInterval ครั้งล่าสุด
คุณต้องตั้ง KeepAliveInterval ให้น้อยกว่า ReceiveTimeout ของเซิร์ฟเวอร์ อย่างน้อย 1/2 หรือ 1/3 ของ ReceiveTimeout ของเซิร์ฟเวอร์ เนื่องจากเซิร์ฟเวอร์จะหมดเวลาและปิดการเชื่อมต่อหากไม่ได้รับสิ่งใดๆ ในระหว่างที่เป็น ReceiveTimeout
KeepAliveRetries คือจำนวนข้อความ Keep-alive ที่ล้มเหลว เมื่อถึงไคลเอ็นต์จะตัดสินใจว่าการเชื่อมต่อขาดและปิด
การตั้งค่า KeepAliveInterval หรือ KeepAliveRetries บนเซิร์ฟเวอร์ไม่มีผลใดๆ
เพื่อให้ Xeeny สามารถรวบรวมพารามิเตอร์เมธอดและส่งคืนประเภทบนเส้นลวดได้ จำเป็นต้องทำให้อนุกรมพารามิเตอร์เหล่านั้น มีซีเรียลไลเซอร์สามตัวที่ได้รับการสนับสนุนในกรอบงานแล้ว
MessagePackSerializer : การทำให้ซีเรียลไลซ์ MessagePack ดำเนินการโดย MsgPack.Cli หรือไม่ มันเป็นซีเรียลไลเซอร์เริ่มต้นเนื่องจากข้อมูลที่ซีเรียลไลซ์มีขนาดเล็กและการนำไปใช้งานสำหรับ .net ในไลบรารีที่กำหนดนั้นรวดเร็วJsonSerializer : Json serializer ใช้งานโดย NewtonsoftProtobufSerializer : ProtoBuffers serializer ของ Google ใช้งานโดย Protobuf-net คุณสามารถเลือกซีเรียลไลเซอร์โดยใช้ผู้สร้างได้โดยการเรียก WithXXXSerializer เพียงตรวจสอบให้แน่ใจว่าประเภทของคุณสามารถซีเรียลไลซ์ได้โดยใช้ซีเรียลไลเซอร์ที่เลือก
var host = new ServiceHostBuilder < ChatService > ( InstanceMode . Single )
. WithCallback < ICallback > ( )
. WithProtobufSerializer ( )
. CreateHost ( ) ;
await host . Open ( ) ;WithSerializer(ISerializer serializer) Xeeny ใช้ TLS 1.2 (บน TCP เท่านั้นในตอนนี้) คุณต้องเพิ่ม X509Certificate ไปยังเซิร์ฟเวอร์
var host = new ServiceHostBuilder < Service > ( .. . )
. AddTcpServer ( tcpAddress , options =>
{
options . SecuritySettings = SecuritySettings . CreateForServer ( x509Certificate2 ) ;
} )
.. . และบนไคลเอนต์คุณต้องผ่าน Certificate Name :
await new ConnectionBuilder < IService > ( )
. WithTcpTransport ( tcpAddress , options =>
{
options . SecuritySettings = SecuritySettings . CreateForClient ( certificateName ) ;
} )
.. . หากคุณต้องการตรวจสอบใบรับรองระยะไกล คุณสามารถส่งตัวแทนเสริม RemoteCertificateValidationCallback ไปยัง SecuritySettings.CreateForClient
Xeeny ใช้ระบบการบันทึกแบบเดียวกับที่พบใน Asp.Net Core
หากต้องการใช้ตัวบันทึกให้เพิ่มแพ็คเกจ nuget ของตัวบันทึก จากนั้นโทร WithXXXLogger ซึ่งคุณสามารถส่ง LogLevel
คุณอาจต้องการตั้งชื่อการเชื่อมต่อเพื่อให้มองเห็นได้ง่ายเมื่อทำการดีบั๊กหรือวิเคราะห์บันทึก คุณสามารถทำได้โดยการตั้งค่าการมอบหมายฟังก์ชัน ConnectionNameFormatter ในตัวเลือกที่ส่งผ่าน IConnection.ConnectionId เป็นพารามิเตอร์ และการส่งคืนจะถูกกำหนดให้กับ IConnection.ConnectionName
var client1 = await new DuplexConnectionBuilder < IChatService , Callback > ( callback1 )
. WithTcpTransport ( address , options =>
{
options . ConnectionNameFormatter = id => $ "First-Connection ( { id } )" ;
} )
. WithConsoleLogger ( LogLevel . Trace )
. CreateConnection ( ) ; Xeeny ถูกสร้างขึ้นเพื่อให้มีประสิทธิภาพสูงและไม่ซิงค์กัน การมีสัญญาแบบอะซิงก์ทำให้เฟรมเวิร์กเป็นแบบอะซิงก์โดยสมบูรณ์ พยายามให้การดำเนินการของคุณส่งคืน Task หรือ Task<T> แทนที่จะเป็น void หรือ T วิธีนี้จะบันทึกเธรดพิเศษหนึ่งเธรดที่จะรอซ็อกเก็ตอะซิงก์ที่จำเป็นให้ดำเนินการให้เสร็จสิ้น ในกรณีที่การดำเนินการของคุณไม่ซิงก์กัน
ค่าใช้จ่ายใน Xeeny คือเมื่อจำเป็นต้องปล่อยประเภท "ใหม่" ณ รันไทม์ มันทำเช่นนั้นเมื่อคุณสร้าง ServiceHost<TService> (การเรียก ServiceHostBuilder<TService>.CreateHost() ) แต่สิ่งนั้นจะเกิดขึ้นหนึ่งครั้งต่อประเภท ดังนั้นเมื่อ xeeny ปล่อยโฮสต์แรกของประเภทที่กำหนดซึ่งสร้างโฮสต์ประเภทนั้นเพิ่มขึ้นก็ไม่มีปัญหาด้านประสิทธิภาพ อย่างไรก็ตาม โดยปกติแล้ว นี่จะเป็นการเริ่มต้นแอปพลิเคชันของคุณ
อีกที่ที่ประเภทการเปล่งแสงเกิดขึ้นคือเมื่อคุณสร้างไคลเอนต์แรกของสัญญาหรือประเภทการติดต่อกลับที่กำหนด (การเรียก CreateConnection ) เมื่อพร็อกซีประเภทแรกนั้นเป็นตัวปล่อย ไคลเอนต์ถัดไปจะถูกสร้างขึ้นโดยไม่มีค่าใช้จ่าย (โปรดทราบว่าคุณยังคงสร้างซ็อกเก็ตใหม่และการเชื่อมต่อใหม่ เว้นแต่คุณจะส่งค่า false ไปที่ CreateConnection )
การเรียก OperationContext.Current.GetCallback<T> ยังปล่อยประเภทรันไทม์ เช่นเดียวกับการปล่อยอื่น ๆ ทั้งหมดที่อยู่เหนือประเภทที่ปล่อยออกมานั้นจะถูกแคชไว้ และโอเวอร์เฮดจะเกิดขึ้นเมื่อมีการโทรครั้งแรกเท่านั้น คุณสามารถเรียกวิธีนี้ได้มากเท่าที่คุณต้องการ แต่คุณควรแคชการคืนสินค้าไว้ดีกว่า
คุณสามารถรับฟีเจอร์กรอบงาน Xeeny ทั้งหมดด้านบนเพื่อทำงานกับการขนส่งที่คุณกำหนดเองได้ (สมมติว่าคุณต้องการให้อยู่หลังอุปกรณ์ Bluetooth)
XeenyListenerServiceHostBuilder<T>.AddCustomServer() IXeenyTransportFactoryConnectionBuilder<T>.WithCustomTransport() หากคุณต้องการมีโปรโตคอลเป็นของตัวเองตั้งแต่เริ่มต้น คุณจะต้องใช้การเชื่อมต่อ การจัดเฟรมข้อความ การทำงานพร้อมกัน การบัฟเฟอร์ การหมดเวลา การรักษาการทำงาน ฯลฯ ของคุณเอง
IListenerServiceHostBuilder<T>.AddCustomServer() ITransportFactoryConnectionBuilder<T>.WithCustomTransport()