2012-04-06 18 views
0

私はWebSocketクライアントをJavascriptで書いています。これはC#my serverと接続して握手するものです。現在、出力ストリームを「フラッシュ」すると(ヘッダBACKをクライアントに送って接続を確認した後でのみ)、クライアントがクラッシュし、次回の書き込みを試みるときにサーバ側の例外が生成されます。後者の動作は予想されますが、私が理解できないことは、フラッシュによって接続が切断される理由です。私は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 facepalm

+0

あなたはクライアントとサーバーサイドを書きましたか?あなたはコードを提供することができますか、あなたが実行するハンドシェークの種類とあなたはそれをどのようにしますか? – Kerwindena

+0

絶対に - 私は今すぐ編集します。 – DigitalJedi805

+0

洗練されたものがあれば教えてください。 – DigitalJedi805

答えて

1

この問題は、ハンドシェイク応答でSec-WebSocket-Protocolを使用することによって発生します。クライアントはサブプロトコルを要求しなかったので、サーバからの唯一の有効な応答はサブプロトコルを指定せずにハンドシェイクを完了することだけでした。サーバーが予期しないサブプロトコルで応答する場合、クライアントは接続を閉じる必要があります。詳細については、4.2.2のRFC 6455の/ subprotocol/sectionを参照してください。

最も簡単な解決策は、応答からSec-WebSocket-Protocolヘッダーを削除することです。保持したい場合は、サブプロトコル名をクライアントのWebSocketコンストラクタの2番目の引数として渡し、サーバーの応答でこのサブプロトコルを使用する必要があります。詳細は、client APIのドキュメントをご覧ください。

EDIT:
ハンドシェイクが完了すると、サーバーはクライアントのonOpenから「TestOutput」メッセージを読み取ろうとして失敗する可能性があります。 WebSocketメッセージはプレーンテキストではなく、HTTPを使用しないため、this.ReadLine()という行は、終了することがほとんどないとは思われません。詳細については、仕様のdata framingセクションを参照してください。このwiki postには、websocketの読み書きに便利な擬似コードがあります。または、私はC++ serverを試すことができます。メッセージを読む方法は、WsProtocol80::Read()を参照してください。または、オープンソースのC#サーバーのうち、Fleck(メッセージを読み書きするためのコードがリンクされています)などを見てください。

あなたのコードをより強固になるだろうが、すぐにパス間の違いを確認し、失敗しないであろう検討することもでき、いくつかの他の小さな変更があります。

  • が指定した任意のサブプロトコルは、理想的には、自分のドメイン名を含める必要があります互換性のないプロトコル要求に誤って一致する可能性を最小限に抑えます。 RFC 6455のearly sectionが理由を説明しています。
  • サポートされているサブプロトコルに応答する前に、Sec-WebSocket-Protocolヘッダーの有無と値を確認することを検討する価値はあります。
  • クライアント要求のヘッダーの順序は保証されていないため、空の行を読み取るまで応答を遅らせることができます。
+0

私の最終的な解決策かもしれません。私はこれを採用し、これを答えとして渡すつもりです。ありがとうございました。これはちょうど私にかなり頭痛を救ったかもしれません。 – DigitalJedi805

+0

悲しいことに、私の回答の行をコメントアウトしても、私はまだ同じ結果で終わります。 これ以上のアドバイスがない場合は、私が得ようとしているように近いと思われるので、これを回答にぶつけますが、悲しいことに、私はまだ同じボートにいます。 – DigitalJedi805

+0

@ DigitalJedi805ああ、私はちょうど別の問題を発見しました。私は秒で私の答えを更新します – simonc

関連する問題