在 .Net Core 中将 Nexmo 连接到 Websocket 失败(远程方关闭了 WebSocket)

Connecting Nexmo to a Websocket fails(remote party closed the WebSocket) in .Net Core

我正在尝试在进行呼入时将 Nexmo 连接到网络套接字(用户将使用 nexmo 购买的号码和 link 呼叫到应用程序) .

到目前为止,我只是在尝试这个 Sample Code (which simply echoes back what the caller says) and to connect to this websocket via Nexmo following the "documentation" Here

我成功地向 nexmo 发送了一个动作 "connect"。拨打使用 Nexmo 购买的号码时,它会正确重定向到端点 (api/nexmo/socket),如使用断点时所示,但在到达 webSocket.ReceiveAsync 时挂断在 Echo 方法中。

    using System;
    using System.Net.WebSockets;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Newtonsoft.Json.Linq;

    namespace MyProject.Web.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class NexmoController : ControllerBase
        {
            private WebSocket _webSocket;

            [HttpGet("answer")]
            public IActionResult AnswerHandler()
            {
                const string host = "MY_NGROK_URL";
                const string locale = "fr-FR";

                var nccos = new JArray();
                var nccoConnect = new JObject()
                {
                    { "action", "connect" },
                    { "endpoint", new JArray(new JObject{
                            { "type", "websocket" },
                            { "uri", $"wss://{host}/api/nexmo/socket"},
                            { "content-type", "audio/l16;rate=16000"},
                            { "headers", new JObject {
                                    { "language", locale },
                                    { "callerID", "MY_NUMBER_HARDCODED_WHILE_TESTING" }
                                }
                            }
                        })
                    }
                };
                nccos.Add(nccoConnect);
                return Content(nccos.ToString(), "application/json");
            }

            [HttpPost("event")]
            public IActionResult EventHandler()
            {
                return Ok();
            }

            [HttpGet("socket")]
            public async Task GetAudio()
            {
                if (HttpContext.WebSockets.IsWebSocketRequest)
                {
                    _webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
                    await Echo(HttpContext, _webSocket);
                }
                else
                {
                    HttpContext.Response.StatusCode = 400;
                }
            }

            //Copy Paste from the Sample Code
            private async Task Echo(HttpContext context, WebSocket webSocket)
            {
                var buffer = new byte[1024 * 4];
                //Breakpoint : ReceiveAsync generates an exception
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                while (!result.CloseStatus.HasValue)
                {
                    await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                }
                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
            }
        }
    }

这里是捕获到的异常:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.

更多异常:

at System.Net.WebSockets.ManagedWebSocket.ThrowIfEOFUnexpected(Boolean throwOnPrematureClosure) at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken, Boolean throwOnPrematureClosure) at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter) at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter) at ....Controllers.NexmoController.Echo(HttpContext context, WebSocket webSocket) in C:...\Controllers\NexmoController.cs:line 97 at ....Controllers.NexmoController.GetAudio() in C:...\Controllers\NexmoController.cs:line 68 at lambda_method(Closure , Object ) at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult() at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at System.Threading.Tasks.ValueTask`1.get_Result() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

有什么想法吗?或者我该如何解决这个问题? 我真的不明白这里有什么问题。

我也尝试检查 Websocket 的 WebSocketState,它设置在 "Open"。 有关信息,我自己测试了示例代码(websocket 回显用户的输入)并且它有效。

我们找到了解决方案:

  • ngrock 不适用于 websockets(并非免费),因此我们在 azure 上发布了我们的应用程序。
  • 我们需要实例化 websocket StartUp 而不是在我们的控制器中(如 示例代码 列于原文post)。我们将 "Echo" 方法放入 另一个 class (更重要的是保持良好的结构)并将其设置为 静态。

现在一切正常:)

澄清:ngrock 不适用于安全的 websockets (wss)