หลังจากทำการsocket
เขียนโปรแกรมasync "ระดับต่ำ" บางอย่าง (มากหรือน้อย) เมื่อหลายปีก่อน (ในรูปแบบเหตุการณ์แบบ Asynchronous Pattern (EAP) ) และเมื่อเร็ว ๆ นี้ได้ย้าย "up" เป็นรูปTcpListener
แบบการเขียนโปรแกรม Asynchronous (APM)แล้ว พยายามที่จะย้ายไปที่async/await
(รูปแบบอะซิงโครนัสตามรูปแบบ(TAP) ) ฉันค่อนข้างจะมีมันด้วยต้องรำคาญกับ 'ประปาในระดับต่ำ' ทั้งหมดนี้ ดังนั้นฉันจึงหา; ทำไมไม่ยอมแพ้RX
( ส่วนขยายรีแอคทีฟ ) เพราะมันอาจเข้ากับโดเมนปัญหาของฉันได้
รหัสจำนวนมากที่ฉันเขียนมีให้กับไคลเอนต์จำนวนมากที่เชื่อมต่อผ่าน Tcp ไปยังแอปพลิเคชันของฉันซึ่งจะเริ่มการสื่อสารแบบสองทาง (async) ลูกค้าหรือเซิร์ฟเวอร์อาจตัดสินใจว่าจะต้องส่งข้อความและทำเช่นนั้นดังนั้นนี่ไม่ใช่request/response
การตั้งค่าแบบดั้งเดิมของคุณแต่เป็นแบบเรียลไทม์แบบสองทาง "สาย" เปิดให้ทั้งสองฝ่ายส่งสิ่งที่พวกเขาต้องการ เมื่อใดก็ตามที่พวกเขาต้องการ (หากใครมีชื่อที่เหมาะสมในการอธิบายสิ่งนี้ฉันยินดีที่จะได้ยิน!)
"โปรโตคอล" นั้นแตกต่างกันไปตามแต่ละแอปพลิเคชัน (และไม่เกี่ยวข้องกับคำถามของฉันจริงๆ) ฉันมี แต่คำถามเริ่มต้น:
- ระบุว่า "เซิร์ฟเวอร์" เดียวเท่านั้นกำลังทำงานอยู่ แต่จะต้องติดตามการเชื่อมต่อจำนวนมาก (ปกติหลายพัน) ของการเชื่อมต่อ (เช่นลูกค้า) แต่ละคนมี (สำหรับการขาดคำอธิบายที่ดีกว่า) ของพวกเขาเอง "เครื่องรัฐ" เพื่อติดตามภายในของพวกเขา รัฐ ฯลฯ คุณต้องการวิธีการแบบใด EAP / TAP / APM? RX จะพิจารณาเป็นตัวเลือกหรือไม่? ถ้าไม่ทำไม
ดังนั้นฉันต้องทำงาน Async ตั้งแต่) มันไม่ใช่โปรโตคอลคำขอ / ตอบกลับดังนั้นฉันจึงไม่สามารถมีเธรด / ไคลเอนต์ใน "กำลังรอข้อความ" - บล็อกการโทรหรือ "ส่งข้อความ" - บล็อกการโทร (อย่างไรก็ตามถ้าการส่งคือ การปิดกั้นสำหรับลูกค้ารายนั้นเท่านั้นที่ฉันสามารถอยู่กับมันได้) และ b) ฉันต้องจัดการการเชื่อมต่อพร้อมกันจำนวนมาก ฉันไม่เห็นวิธีการทำเช่นนี้ (เชื่อถือได้) โดยใช้การบล็อกการโทร
แอปพลิเคชันของฉันส่วนใหญ่เกี่ยวข้องกับ VoiP ไม่ว่าจะเป็นข้อความ SIP จากลูกค้า SIP หรือการส่งข้อความ PBX (ที่เกี่ยวข้อง) จากแอปพลิเคชันเช่นFreeSwitch / OpenSIPSเป็นต้น แต่คุณสามารถทำได้ในรูปแบบที่ง่ายที่สุดลองจินตนาการถึงเซิร์ฟเวอร์ "แชท" ที่พยายามจัดการกับลูกค้า "แชท" โปรโตคอลส่วนใหญ่เป็นข้อความ (ASCII)
ดังนั้นหลังจากใช้วิธีการเรียงสับเปลี่ยนของเทคนิคต่าง ๆ ที่กล่าวมาแล้วฉันต้องการทำให้งานของฉันง่ายขึ้นโดยการสร้างวัตถุที่ฉันสามารถยกตัวอย่างบอกสิ่งที่IPEndpoint
ฟังและบอกฉันเมื่อใดก็ตามที่มีสิ่งที่น่าสนใจเกิดขึ้น ใช้เหตุการณ์สำหรับดังนั้น EAP บางอย่างมักจะผสมกับอีกสองเทคนิค) ชั้นไม่ควรกังวลกับการพยายามเข้าใจโปรโตคอล; มันควรจัดการกับสตริงที่เข้า / ส่งออกเท่านั้น และด้วยการที่ฉันจับจ้องที่ RX หวังว่า (ท้ายที่สุด) จะทำให้งานง่ายขึ้นฉันจึงสร้าง "ซอ" ขึ้นมาใหม่ตั้งแต่ต้น:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Reactive.Linq;
using System.Text;
class Program
{
static void Main(string[] args)
{
var f = new FiddleServer(new IPEndPoint(IPAddress.Any, 8084));
f.Start();
Console.ReadKey();
f.Stop();
Console.ReadKey();
}
}
public class FiddleServer
{
private TcpListener _listener;
private ConcurrentDictionary<ulong, FiddleClient> _clients;
private ulong _currentid = 0;
public IPEndPoint LocalEP { get; private set; }
public FiddleServer(IPEndPoint localEP)
{
this.LocalEP = localEP;
_clients = new ConcurrentDictionary<ulong, FiddleClient>();
}
public void Start()
{
_listener = new TcpListener(this.LocalEP);
_listener.Start();
Observable.While(() => true, Observable.FromAsync(_listener.AcceptTcpClientAsync)).Subscribe(
//OnNext
tcpclient =>
{
//Create new FSClient with unique ID
var fsclient = new FiddleClient(_currentid++, tcpclient);
//Keep track of clients
_clients.TryAdd(fsclient.ClientId, fsclient);
//Initialize connection
fsclient.Send("connect\n\n");
Console.WriteLine("Client {0} accepted", fsclient.ClientId);
},
//OnError
ex =>
{
},
//OnComplete
() =>
{
Console.WriteLine("Client connection initialized");
//Accept new connections
_listener.AcceptTcpClientAsync();
}
);
Console.WriteLine("Started");
}
public void Stop()
{
_listener.Stop();
Console.WriteLine("Stopped");
}
public void Send(ulong clientid, string rawmessage)
{
FiddleClient fsclient;
if (_clients.TryGetValue(clientid, out fsclient))
{
fsclient.Send(rawmessage);
}
}
}
public class FiddleClient
{
private TcpClient _tcpclient;
public ulong ClientId { get; private set; }
public FiddleClient(ulong id, TcpClient tcpclient)
{
this.ClientId = id;
_tcpclient = tcpclient;
}
public void Send(string rawmessage)
{
Console.WriteLine("Sending {0}", rawmessage);
var data = Encoding.ASCII.GetBytes(rawmessage);
_tcpclient.GetStream().WriteAsync(data, 0, data.Length); //Write vs WriteAsync?
}
}
ฉันทราบว่าใน "ซอ" นี้มีรายละเอียดเฉพาะการใช้งานเล็กน้อย ในกรณีนี้ฉันกำลังทำงานกับFreeSwitch ESLดังนั้น"connect\n\n"
ซอในซอร์ฟแวร์ควรจะถูกลบออกไป
ฉันยังทราบด้วยว่าฉันจำเป็นต้อง refactor วิธีการที่ไม่ระบุชื่อวิธีการของอินสแตนซ์ส่วนตัวบนคลาสเซิร์ฟเวอร์ ฉันแค่ไม่แน่ใจว่าจะใช้การประชุมแบบไหน (เช่น " OnSomething
") สำหรับชื่อเมธอด
นี่คือพื้นฐาน / จุดเริ่มต้น / รากฐานของฉัน (ซึ่งต้องการ "tweaking") ฉันมีคำถามบางอย่างเกี่ยวกับเรื่องนี้:
- ดูคำถามข้างต้น "1"
- ฉันกำลังติดตามใช่ไหม? หรือ "การออกแบบ" การตัดสินใจของฉันไม่ยุติธรรมหรือไม่?
- Concurrency-wise: สิ่งนี้จะรับมือกับลูกค้าหลายพันราย (แยกวิเคราะห์ / จัดการข้อความจริงนอกเหนือ)
- ในข้อยกเว้น: ฉันไม่แน่ใจว่าจะรับข้อยกเว้นที่เพิ่มขึ้นภายในไคลเอนต์ "up" ไปยังเซิร์ฟเวอร์ ("RX-wise") ได้อย่างไร อะไรจะเป็นวิธีที่ดี?
- ตอนนี้ฉันสามารถรับไคลเอนต์ที่เชื่อมต่อจากคลาสเซิร์ฟเวอร์ของฉัน (โดยใช้มัน
ClientId
) โดยสมมติว่าฉันเปิดเผยลูกค้าในทางใดทางหนึ่งและอีกวิธีการโทรกับพวกเขาโดยตรง ฉันยังสามารถเรียกใช้เมธอดผ่านคลาสเซิร์ฟเวอร์ (ตัวอย่างเช่นSend(clientId, rawmessage)
เมธอด (ในขณะที่วิธีการหลังจะเป็นวิธี "ความสะดวก" สำหรับการรับข้อความอย่างรวดเร็วไปยังอีกด้านหนึ่ง) - ฉันไม่แน่ใจว่าจะไปจากที่นี่ (และอย่างไร):
- a) ฉันต้องจัดการกับข้อความที่เข้ามา; ฉันจะตั้งค่านี้ได้อย่างไร ฉันจะได้รับ ofcourse กระแส แต่ที่ฉันจะจัดการกับการเรียกไบต์ได้รับ? ฉันคิดว่าฉันต้องการ "ObservableStream" บางอย่างที่ฉันสามารถสมัครสมาชิกได้บ้าง ฉันจะใส่นี้ใน
FiddleClient
หรือFiddleServer
? - ข) สมมติว่าผมต้องการที่จะหลีกเลี่ยงการใช้เหตุการณ์เหล่านี้จนกว่า
FiddleClient
/FiddleServer
ชั้นเรียนจะดำเนินการมากขึ้นโดยเฉพาะการตัดโปรแกรมเฉพาะการจัดการ ฯลฯ โดยใช้โปรโตคอลเฉพาะเจาะจงมากขึ้นของพวกเขาFooClient
/FooServer
ชั้นเรียน: วิธีการที่ฉันจะไปจากการได้รับข้อมูลในต้นแบบ 'Fiddle' เรียนของพวกเขา คู่ที่เฉพาะเจาะจงมากขึ้น?
- a) ฉันต้องจัดการกับข้อความที่เข้ามา; ฉันจะตั้งค่านี้ได้อย่างไร ฉันจะได้รับ ofcourse กระแส แต่ที่ฉันจะจัดการกับการเรียกไบต์ได้รับ? ฉันคิดว่าฉันต้องการ "ObservableStream" บางอย่างที่ฉันสามารถสมัครสมาชิกได้บ้าง ฉันจะใส่นี้ใน
บทความ / ลิงค์ที่ฉันอ่าน / อ่านแล้ว / ใช้เพื่อการอ้างอิง:
- ใช้ซ็อกเก็ตโดยใช้การขยายปฏิกิริยา
- การสร้างและการสมัครสมาชิกลำดับที่สังเกตได้ง่าย
- วิธีเพิ่มหรือเชื่อมโยงบริบทเข้ากับการเชื่อมต่อ / เซสชัน ObservableTcpListener แต่ละรายการ
- การเขียนโปรแกรมไม่ตรงกันกับปฏิกิริยากรอบและห้องสมุดขนานงาน - ส่วนที่ 1 , 2และ3
- ใช้ Reactive Extensions (Rx) สำหรับการเขียนโปรแกรมซ็อกเก็ตจริง
- .NET Rx เซิร์ฟเวอร์ขับเคลื่อนเว็บและ.NET Rx ขับเคลื่อน Web Server, Take 2
- บทเรียน Rxบางส่วน