【发布时间】:2015-02-10 22:54:57
【问题描述】:
我正在尝试使用 FreeSWITCH 中的 Event Socket Library (ESL) (archive),但我想这个问题更多的是关于技术和“如何”。
下面的代码是我的问题所在,可以在here找到。
static void OutboundModeSync(Object stateInfo)
{
//...
while (true)
{
Socket sckClient = tcpListener.AcceptSocket();
//Initializes a new instance of ESLconnection, and connects to the host $host on the port $port, and supplies $password to freeswitch
ESLconnection eslConnection = new ESLconnection(sckClient.Handle.ToInt32());
Console.WriteLine("Execute(\"answer\")");
eslConnection.Execute("answer", String.Empty, String.Empty);
Console.WriteLine("Execute(\"playback\")");
eslConnection.Execute("playback", "music/8000/suite-espanola-op-47-leyenda.wav", String.Empty);
Console.WriteLine("Execute(\"hangup\")");
eslConnection.Execute("hangup", String.Empty, String.Empty);
}
//...
}
static void OutboundModeAsync(Object stateInfo)
{
//...
while (true)
{
tcpListener.BeginAcceptSocket((asyncCallback) =>
{
TcpListener tcpListened = (TcpListener)asyncCallback.AsyncState;
Socket sckClient = tcpListened.EndAcceptSocket(asyncCallback);
//Initializes a new instance of ESLconnection, and connects to the host $host on the port $port, and supplies $password to freeswitch
ESLconnection eslConnection = new ESLconnection(sckClient.Handle.ToInt32());
ESLevent eslEvent = eslConnection.GetInfo();
//...
while (eslConnection.Connected() == ESL_SUCCESS)
{
eslEvent = eslConnection.RecvEvent();
Console.WriteLine(eslEvent.Serialize(String.Empty));
}
sckClient.Close();
}, tcpListener);
Thread.Sleep(50);
}
}
(我在outbound mode 中使用FreeSWITCH ESL)。据我了解,“同步”和“异步”方法都处于无限循环中等待(阻塞)一个事件,每当从 FreeSWITCH 接收到某个事件时都会做出反应。
但是,我想创建一个 Windows 服务来跟踪 FreeSWITCH 向我抛出的所有调用。我希望另一个应用程序调用我的服务,并告诉它发送“Foo”到连接“XYZ”。所以我会跟踪一个连接字典,其中键是连接-(GU)ID。但是,如果该连接正在等待(阻塞)一个事件,我看不出如何使用它来“插入”并发送我的消息,无论是否从 FreeSWITCH 接收到事件以“释放”阻塞线程。
顺便说一句:“插入”是指,例如,如果出于某种原因,我决定要挂断连接(例如触发了某个计时器),我希望能够发送挂断,无论连接是否在等待从 FreeSWITCH 发送的事件(如果没有任何反应,这可能需要很长时间)。
我可以使用recvEventTimed 来创建“轮询机制”,但这种方法存在三个问题:
- 投票感觉很脏
- 每当我想“加入”时,我希望我的消息尽快发送,而不是 50 毫秒(或 1 毫秒)之后
-
recvEventTimed似乎忽略了低于约 500 毫秒的任何内容,因此它会像疯了一样进行轮询。而且 500 毫秒的延迟对我来说太长了。
除了这些(我可以提交错误报告),轮询感觉不对。
我想“接受连接”,告诉 ESL 等待(阻塞)一个事件,然后引发一个事件,以便我可以使用 基于事件的方法。我希望处理大量的连接,所以我更喜欢低延迟、没有(或很少)轮询和一种干净的方法来处理所有这些连接,同时它们持续存在,给我免费的“双向、非轮询、基于事件”的通信。
我考虑过 RX(反应式扩展)并使用 .Net Async 套接字(与 FreeSWITCH 提供的二进制文件相反)和其他方法实现我自己的“ESL”。但是如果没有大量样板或/和讨厌的线程等东西,我似乎无法找到一种方法来巧妙地实现我的愿望/要求。我很确定我应该能够使用async/await 但迷路了关于如何做到这一点。任何帮助将不胜感激。
简单说明:
* Connection comes in
* Accept connection + get GUID from connection
* Put connection in some collection for later reference
* Bind delegate to connection (`EventReceived` event)
* Use collection to get connection and send a message (preferrably async ("fire and forget"), but blocking COULD be acceptable as long as it only blocks that specific connection) to "butt-in" whenever I want to
该集合将托管在 Windows 服务中,我可以联系该服务以获取我的 FreeSWITCH 连接的 I/O/列出可用连接等。
所以,我在“伪代码”中想象一些东西:
MyService {
stopped = false;
void OnStart {
while (!stopped) {
var someconn = esl.waitforconnection();
var id = someconn.getCallId();
someconn.EventReceived += MyDelegate(...);
mydict.add(id, someconn);
}
}
void OnStop {
stopped = true;
}
void ButtIn(string callid, string msg) {
mydict[callid].send(msg);
}
void MyDelegate(EventData evtdata) {
//Do something with evtdata if desired
}
}
鉴于 FreeSwitch 提供的 ESL 不是基于事件的,并且只有阻塞“等待事件”或轮询“等待 X 毫秒事件”,我将如何实现这一点?
tl;dr:不想要无限的while循环/轮询,而是更多基于事件的方法,最好使用async/await,干净+简单的代码使用一旦在我自己的类中包装/抽象了“基础的“ESL 连接””。需要最佳方法的帮助/建议。没有线程/套接字大师。
【问题讨论】:
-
>我已经考虑过 RX(反应式扩展)并使用 .Net 异步套接字实现我自己的“ESL”。我已经做了很多,我会在 GitHub/Nuget 上得到它希望周末。
-
非常感谢。我很乐意看看/看看我可以在哪里(以及是否)提供帮助。
-
github.com/danbarua/NEventSocket - 它还没有在生产中经过实战测试:) 示例代码在这里:github.com/danbarua/NEventSocket/blob/master/src/…
-
在我之前的评论中,似乎有一些人现在正在使用该库并报告他们发现的任何问题。
标签: c# multithreading asynchronous event-handling freeswitch