【问题标题】:Freeswitch Event Socket LibraryFreeswitch 事件套接字库
【发布时间】: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 来创建“轮询机制”,但这种方法存在三个问题:

  1. 投票感觉很脏
  2. 每当我想“加入”时,我希望我的消息尽快发送,而不是 50 毫秒(或 1 毫秒)之后
  3. 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


【解决方案1】:

NEventSocket 和 ReactiveExtensions 可能会对您有所帮助。

private static async Task CallTracking()
    {
        var client = await InboundSocket.Connect("10.10.10.36", 8021, "ClueCon");

        string uuid = null;

        client.Events.Where(x => x.EventName == EventName.ChannelAnswer)
              .Subscribe(x =>
                  {
                      uuid = x.UUID;
                      Console.WriteLine("Channel Answer Event {0}", x.UUID);
                  });

        client.Events.Where(x => x.EventName == EventName.ChannelHangup)
              .Subscribe(x =>
                  {
                      uuid = null;
                      Console.WriteLine("Channel Hangup Event {0}", x.UUID);
                  });

        Console.WriteLine("Press enter to hang up the current call");
        Console.ReadLine();

        if (uuid != null)
        {
            Console.WriteLine("Hanging up {0}", uuid);
            await client.Play(uuid, "ivr/8000/ivr-call_rejected.wav");
            await client.Hangup(uuid, HangupCause.CallRejected);
        }

        client.Exit();
    }

【讨论】:

    【解决方案2】:

    我认为您描述的逻辑使用入站套接字会更容易实现。您可以订阅您感兴趣的事件,并执行您的后台任务。您也可以在多线程应用程序中执行此操作,以便一个线程读取 ESL 事件,而其他线程与您的业务逻辑的其他部分进行通信。

    【讨论】:

    • 我的问题是一旦我调用RecvEvent()(这是阻塞)我不能再使用连接发送(使用Execute()Send()或其他方法)我想发送一条消息,因为RecvEvent()-method 调用上的连接被阻塞。
    • 您可以打开第二个连接并将其用于发送命令。或者,您可以重新编写库并使用 select()
    • 来 Skype 或 Google 视频群聊,我很容易找到
    猜你喜欢
    • 1970-01-01
    • 2015-09-30
    • 2021-11-22
    • 1970-01-01
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多