2015-02-04 4 views
2

У меня есть сервер WebSocket, который принимает поток двоичных данных от клиента и отвечает другим потоком текстовых данных за каждые 4 МБ чтения. Сервер использует IIS 8 и asp.net web api.Как изящно закрыть двухсторонний WebSocket в .Net

Сервер

public class WebSocketController : ApiController 
{ 
    public HttpResponseMessage Get() 
    { 
     if (!HttpContext.Current.IsWebSocketRequest) 
     { 
      return new HttpResponseMessage(HttpStatusCode.BadRequest); 
     } 

     HttpContext.Current.AcceptWebSocketRequest(async (context) => 
     { 
      try 
      { 
       WebSocket socket = context.WebSocket; 

       byte[] requestBuffer = new byte[4194304]; 
       int offset = 0; 

       while (socket.State == WebSocketState.Open) 
       { 
        var requestSegment = new ArraySegment<byte>(requestBuffer, offset, requestBuffer.Length - offset); 
        WebSocketReceiveResult result = await socket.ReceiveAsync(requestSegment, CancellationToken.None); 

        if (result.MessageType == WebSocketMessageType.Close) 
        { 
         // Send one last response before closing 
         var response = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Server got " + offset + " bytes\n")); 
         await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None); 

         // Close 
         await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); 
         break; 
        } 

        offset += result.Count; 
        if (offset == requestBuffer.Length) 
        { 
         // Regular response 
         var response = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Server got 4194304 bytes\n")); 
         await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None); 
         offset = 0; 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       // Log and continue 
      } 
     }); 

     return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); 
    } 
} 

C# клиент использует ClientWebSocket класс для подключения к серверу и отправлять запросы. Он создает задачу для получения ответов от сервера, который выполняется параллельно с отправкой запроса. Когда это делается, отправив запросы, он вызывает CloseAsync в сокете, а затем ждет завершения задачи Receive.

Client

using System; 
using System.Net.WebSockets; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace WebSocketClient 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       CallWebSocketServer().Wait(); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      } 
     } 

     static async Task CallWebSocketServer() 
     { 
      using (ClientWebSocket socket = new ClientWebSocket()) 
      { 
       await socket.ConnectAsync(new Uri("ws://localhost/RestWebController"), CancellationToken.None); 
       byte[] buffer = new byte[128 * 1024]; 

       Task receiveTask = Receive(socket); 
       for (int i = 0; i < 1024; ++i) 
       { 
        await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, CancellationToken.None); 
       } 

       await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); 
       receiveTask.Wait(); 

       Console.WriteLine("All done"); 
      } 
     } 

     static async Task Receive(ClientWebSocket socket) 
     { 
      try 
      { 
       byte[] recvBuffer = new byte[64 * 1024]; 
       while (socket.State == WebSocketState.Open) 
       { 
        var result = await socket.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None); 
        Console.WriteLine("Client got {0} bytes", result.Count); 
        Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count)); 
        if (result.MessageType == WebSocketMessageType.Close) 
        { 
         Console.WriteLine("Close loop complete"); 
         break; 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("Exception in receive - {0}", ex.Message); 
      } 
     } 
    } 
} 

Проблема заключается в том, что клиентские блоки на CloseAsync вызова.

Что было бы правильным способом изящного закрытия WebSocket в этом сценарии?

ответ

7

Выяснено это.

Сервера

В принципе, я должен был назвать ClientWebSocket.CloseOutputAsync (вместо CloseAsync) метода, чтобы сказать рамки не больше выхода будет послан от клиента.

await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); 

Client

Тогда в функции Receive, я должен был обеспечить гнездо состояния WebSocketState.CloseSent получить Close ответ от сервера

static async Task Receive(ClientWebSocket socket) 
{ 
    try 
    { 
     byte[] recvBuffer = new byte[64 * 1024]; 
     while (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent) 
     { 
      var result = await socket.ReceiveAsync(new ArraySegment<byte>(recvBuffer), CancellationToken.None); 
      Console.WriteLine("Client got {0} bytes", result.Count); 
      Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count)); 
      if (result.MessageType == WebSocketMessageType.Close) 
      { 
       Console.WriteLine("Close loop complete"); 
       break; 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("Exception in receive - {0}", ex.Message); 
    } 
} 
-1

я предлагаю вам взглянуть на этих ссылках:

асинхронный сервер: https://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx

асинхронного клиент: https://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx

недавно я implementled что-то подобное с этими ссылками в качестве примера. методы «BeginReceive» (для сервера) и «BeginConnect» (для клиента) запускают каждый новый поток. так что не будет ничего, что блоки

Смежные вопросы