在 .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)
我正在尝试在进行呼入时将 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)