2015-02-04 16 views
2

我有一個WebSocket服務器,它接受來自客戶端的二進制數據流,並以每4MB讀取的另一個文本數據流作爲響應。該服務器使用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任務完成。

客戶

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); 

客戶

然後在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); 
    } 
}