2012-04-06 167 views
0

我已經寫了一個JavaScript的WebSocket客戶端,連接和握手C#我的服務器。目前,在'刷新'我的輸出流時(僅在我將頭部BACK發送給客戶端並確認了我的連接之後),當我嘗試下次寫入時,客戶端崩潰並生成服務器端異常。後者的行爲是可以預料的,但是我無法弄清楚爲什麼flush會丟失連接。我在服務器端使用了一個TcpListener和一個帶有StreamWriter的Socket,並且在客戶端使用了一個普通的WebSocket。WebSocket連接丟失之前onmessage

這種情況真正令人困惑的是,在'握手'傳輸過程中,文本可以雙向傳輸,並且在每行發送後執行Flush,但只要握手完成,刷新終止連接。

請告訴我,如果在這次修訂中沒有足夠的信息,因爲它現在已經被修改了幾次。

在此先感謝。

客戶端的Javascript:

<!DOCTYPE html> 
<meta charset="utf-8" /> 
<html> 
<head> 
<script language="javascript" type="text/javascript"> 
    var wsUri = "ws://127.0.0.1:9002/cc"; 
    var output; 
    var websocket = null; 

    function init() 
    { 
     StartWebSocket(); 
    } 

    function StartWebSocket() 
    { 
     output = document.getElementById("output"); 
     writeToScreen("#WebSocket Starting"); 
     websocket = new WebSocket(wsUri,"lorem.ipsum.com"); 
     writeToScreen("#WebSocket Instantiated"); 
     websocket.removeEventListener("open",onOpen,false); 
     websocket.addEventListener("open",onOpen,false); 

     websocket.removeEventListener("close",onClose,false); 
     websocket.addEventListener("close",onClose,false); 

     websocket.removeEventListener("message",onMessage,false); 
     websocket.addEventListener("message",onMessage,false); 

     websocket.removeEventListener("error",onError,false); 
     websocket.addEventListener("error",onError,false); 

     writeToScreen("#WebSocket Events Attached"); 
    } 

    function onOpen(evt) 
    { 
     try 
     { 
      writeToScreen("#WebSocket Connection Established"); 
      writeToScreen("#WebSocket BinaryType: " + websocket.binaryType); 
      writeToScreen("#WebSocket Protocol: " + websocket.protocol); 
      writeToScreen("#WebSocket Extensions: " + websocket.extensions); 
      doSend("TestOutput\r\n\r"); 
     } 
     catch(e) 
     { 
      writeToScreen(e); 
     } 
    } 

    function onClose(evt) 
    { 
     writeToScreen("#WebSocket Connection Aborted:"); 
     writeToScreen("&nbsp;&nbsp;&nbsp;&nbsp;Reason: " + evt.code); 
     writeToScreen("&nbsp;&nbsp;&nbsp;&nbsp;Reason: " + evt.reason); 
     writeToScreen("&nbsp;&nbsp;&nbsp;&nbsp;Clean: " + evt.wasClean); 
    } 

    function onMessage(evt) 
    { 
     writeToScreen("#WebSocket Message Event"); 
     try 
     { 
      writeToScreen("<span style=\"color: blue;\">#WebSocket Server Message: " + evt.data+"</span>"); 
     } 
     catch(e) 
     { 
      writeToScreen(e); 
     } 
    } 

    function onError(evt) 
    { 
     writeToScreen("<span style=\"color: red;\">#WebSocket Error:</span> " + evt.data); 
    } 

    function doSend(message) 
    { 
     try 
     { 
      websocket.send(message); 
      writeToScreen("#WebSocket Output Written to Server: " + message); 
     } 
     catch(e) 
     { 
      writeToScreen(e); 
     } 
    } 

    function writeToScreen(message) 
    { 
     try 
     { 
      var pre = document.createElement("a"); 
      pre.style.wordWrap = "break-word"; 
      pre.innerHTML = message + "<br>"; 
      output.appendChild(pre); 
     } 
     catch(e) 
     { 
      writeToScreen(e); 
     } 
    } 

    window.addEventListener("load", init, false); 

</script> 
</head> 
<body> 
<div id="output"></div> 
</body> 
</html> 

握手報價,我從我的客戶端收到如下:

GET /cc HTTP/1.1 
Upgrade: websocket 
Connection: Upgrade 
Host: 127.0.0.1:9002 
Origin: http://localhost 
Sec-WebSocket-Key: icajBpkAfgA+YbVheBpDsQ== 
Sec-WebSocket-Version: 13 

我解釋握手,像這樣:

public override void Interpret(string Argument) 
{ 
    if (String.IsNullOrEmpty(Argument)) 
    { 
     return; 
    } 
    else 
    { 
     if(!HeaderFinished) 
     { 
      if (!HeaderStarted) 
      { 
       if (Argument.StartsWith("GET /")) 
       { 
        this.Role = "client"; 
        HeaderStarted = true; 
        this.Server.Print("Connection at " + this.Address + " set to client."); 
       } 
       else 
       { 
        return; 
       } 
      } 
      else 
      { 
       if (Argument.StartsWith("Sec-WebSocket-Key:")) 
       { 
        this.Key = Argument.Split(' ')[1].TrimEnd().TrimStart(); 
        return; 
       } 
       else if (Argument.StartsWith("Sec-WebSocket-Version:")) 
       { 
        this.HeaderFinished = true; 
        this.WriteHeaderResponse(); 
        HeaderSent = true; 
        return; 
       } 
      } 
     } 
     else 
     { 
      this.InterpretMessage(DecodeMessage(Argument)); 
      return; 
     } 
    } 
} 

發送我的頭回應:

public void WriteHeaderResponse() 
{ 
    this.WriteLine("HTTP/1.1 101 Switching Protocols"); 
    this.WriteLine("Upgrade: websocket"); 
    this.WriteLine("Connection: Upgrade"); 
    String NewKey = ComputeResponseKey(this.Key); 
    this.WriteLine("Sec-WebSocket-Accept: " + NewKey); 
    this.WriteLine("Sec-WebSocket-Protocol: lorem.ipsum.com"); 
    this.WriteLine("\r\n"); 
} 

,並從客戶端的操作輸出(此時):

#WebSocket Starting 
#WebSocket Instantiated 
#WebSocket Events Attached 
#WebSocket Connection Established 
#WebSocket BinaryType: blob 
#WebSocket Protocol: lorem.ipsum.com 
#WebSocket Extensions: 
#WebSocket Output Written to Server: TestOutput 

在這一點上,如果我嘗試執行下面的服務器端方法,客戶端斷開連接,像這樣:

#WebSocket Connection Aborted: 
    Reason: 1006 
    Reason: 
    Clean: false 

消息代碼: -Taken從我的東西在網絡上發現,修改了一下......

public void WriteMessage(byte[] Payload) 
{ 
    byte[] Message; 
    int Length = Payload.Length; 
    int MaskLength = 4; 

    if (Length < 126) 
    { 
     Message = new byte[2 + MaskLength + Length]; 
     Message[1] = (byte)Length; 
    } 
    else if (Length < 65536) 
    { 
     Message = new byte[4 + MaskLength + Length]; 
     Message[1] = (byte)126; 
     Message[2] = (byte)(Length/256); 
     Message[3] = (byte)(Length % 256); 
    } 
    else 
    { 
     Message = new byte[10 + MaskLength + Length]; 
     Message[1] = (byte)127; 

     int left = Length; 
     int unit = 256; 

     for (int i = 9; i > 1; i--) 
     { 
      Message[i] = (byte)(left % unit); 
      left = left/unit; 

      if (left == 0) 
       break; 
     } 
    } 

    //Set FIN 
    Message[0] = (byte)129;// (0 | 0x80); 

    //Set mask bit 
    //Message[1] = (byte)(Message[1] | 0x80); 

    //GenerateMask(Message, Message.Length - MaskLength - Length); 

    //if (Length > 0) 
     //MaskData(Payload, 0, Length, Message, Message.Length - Length, Message, Message.Length - MaskLength - Length); 

    char[] output = new char[Message.Length-4]; 

    for(int i = 0, y = 0, z = 0; i < Message.Length; i++) 
    { 
     if (Message[z] == '\0') 
     { 
      if (Payload.Length > i-z) 
       output[i] = (char)Payload[y++]; 
     } 
     else 
     { 
      output[i] = (char)Message[z++]; 
     } 
    } 

    this.OutputWriter.Write(output, 0, output.Length); 
    this.OutputWriter.Flush(); 
} 

更新: 我剛剛用我最新的文檔替換了本文檔中的所有代碼。

總結:

- The client-server handshake has been matched on both sides. 
- A path has been defined in the URI for the WebSocket. 
- Data packets are now being 'properly' framed. 

一個觀點,我只注意到在編輯這是我的WriteMessage方法的最後幾行。完成我所有的成幀後,我將字節數組轉換爲字符數組,並使用StreamReader.Write發送它。我不確定這是否是一個可行的解決方案,所以請讓我檢查一下,如果不是。

否則我很困惑。這似乎符合我已閱讀過的所有標準,但仍然令我悲傷失敗。如果我能做到這一點,這是相當成功的,所以我真的很感激任何人的幫助。

謝謝。 -DigitalJedi 捂臉

+0

那你寫了client和serverside?你能提供代碼或你執行什麼樣的握手以及你怎麼做? – Kerwindena 2012-04-06 22:31:57

+0

絕對 - 我現在編輯它。 – DigitalJedi805 2012-04-06 22:46:40

+0

如果你需要精煉的東西里面有什麼,請告訴我。 – DigitalJedi805 2012-04-06 22:50:26

回答

1

該問題通過在握手響應使用仲丁基網頁套接字協議引起的。客戶端沒有請求子協議,所以服務器唯一有效的響應是完成握手而不指定子協議。如果服務器響應一個意外的子協議,則客戶端需要關閉連接。有關詳細信息,請參閱RFC 6455的第4.2.2節中的/ subprotocol /部分。

最簡單的解決方法是從響應中刪除Sec-WebSocket協議標頭。如果要保留它,則需要將一個子協議名作爲第二個參數傳遞給客戶端的WebSocket構造函數,並在服務器響應中使用此子協議。詳細信息請參閱client API文檔。

編輯:
一旦你完成握手,服務器將很可能無法嘗試從客戶端讀取的OnOpen了「TestOutput」的消息。 WebSocket消息不是純文本,並且不使用HTTP,所以行this.ReadLine()極不可能找到終止的\ r \ n。有關詳細信息,請參閱規範的data framing部分。這個wiki post有一些有用的websocket讀/寫代碼。或者,你可以試試我的C++ server。有關如何閱讀消息,請參閱WsProtocol80::Read()。或者查看其中一個開源C#服務器,例如Fleck(讀取/寫入消息的代碼已鏈接)。

有,你可以考慮一些其他的小變化,這將使你的代碼更健壯,但不會立即做出傳球的區別和失敗:

  • 指定應該理想地包括你的域名有子協議以最小化意外匹配任何不兼容的協議請求的可能性。 RFC 6455的early section解釋了原因。
  • 在迴應您支持的子協議之前,應該考慮檢查請求中Sec-WebSocket協議頭的存在和值。
  • 無法保證客戶端請求中的標題順序,因此您可能會延遲響應,直到您讀取空行。
+0

可能是我的最終解決方案。我打算採用這個方法並將其作爲答案。謝謝。這可能只是讓我很頭疼。 – DigitalJedi805 2012-04-17 20:07:21

+0

不幸的是 - 即使在評論我的迴應後,我仍然以相同的結果結束。 如果您沒有任何進一步的建議,我可能會碰到這個答案,因爲它看起來像我將要接近,但可悲的是,我仍然在同一條船上。 – DigitalJedi805 2012-04-17 20:10:39

+0

@ DigitalJedi805啊,我剛剛發現了另一個問題。我會在一秒鐘內更新我的答案 – simonc 2012-04-17 20:55:41

相關問題