シンプルなC#WebSockets Server

私は、次のような単純なWebSocketsサーバーを作成しようとしています。

namespace ConsoleWebSocketServer
{
    class Program
    {
        const string c_MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        static void Main(string[] args)
        {
            TcpListener l_Listener = new TcpListener(IPAddress.Loopback, 8181);
            l_Listener.Start();
            while (true)
            {
                using (TcpClient l_Client = l_Listener.AcceptTcpClient())
                using (NetworkStream l_Stream = l_Client.GetStream())
                {
                    var l_headers = new Dictionary();
                    string l_line = string.Empty;
                    while ((l_line = ReadLine(l_Stream)) != string.Empty)
                    {
                        var tokens = l_line.Split(new char[] { ':' }, 2);
                        if (!string.IsNullOrWhiteSpace(l_line) && tokens.Length > 1)
                        {
                            l_headers[tokens[0]] = tokens[1].Trim();
                        }
                    }

                    string l_secKey = l_headers["Sec-WebSocket-Key"];
                    string l_responseSecKey = ComputeWebSocketHandshakeSecurityHash09(l_secKey);

                    string l_response =
                        "HTTP/1.1 101 Switching Protocols" + Environment.NewLine +
                        "Upgrade: websocket" + Environment.NewLine +
                        "Connection: Upgrade" + Environment.NewLine +
                        "Sec-WebSocket-Accept: " + l_responseSecKey + Environment.NewLine + Environment.NewLine; 

                    var l_bufferedResponse = Encoding.UTF8.GetBytes(l_response);
                    l_Stream.Write(l_bufferedResponse, 0, l_bufferedResponse.Length);
                }
            }
        }

        public static string ComputeWebSocketHandshakeSecurityHash09(string secWebSocketKey)
        {
            string secWebSocketAccept = null; ;
            string l_combinedKey = secWebSocketKey + c_MagicKey;

            SHA1 l_Sha1Crypto = new SHA1CryptoServiceProvider();
            byte[] l_sha1Hash = l_Sha1Crypto.ComputeHash(Encoding.UTF8.GetBytes(l_combinedKey));
            secWebSocketAccept = Convert.ToBase64String(l_sha1Hash);

            return secWebSocketAccept ?? String.Empty;
        }

        static string ReadLine(Stream stream)
        {
            var l_Sb = new StringBuilder();
            var l_buffer = new List();
            while (true)
            {
                l_buffer.Add((byte)stream.ReadByte());
                string l_line = Encoding.ASCII.GetString(l_buffer.ToArray());
                if (l_line.EndsWith(Environment.NewLine))
                {
                    return l_line.Substring(0, l_line.Length - 2);
                }
            }
        }
    }
}

そして、次のような単純なページでテストしています:

function testWebSocket() {
    if (!window.WebSocket) {
        alert('WebSockets are NOT supported by your browser.');
        return;
    }

    var ws = new WebSocket('ws://localhost:8181/demo');
    ws.onopen = function() {
        alert('Handshake successfully established. Ready for data...');
    };

    ws.onmessage = function (e) {
        alert('Got WebSockets message: ' + e.data);
    }

    ws.onclose = function() {
        alert('Connection closed.');
    };
}

私は "Handshake"アラートがすぐに "Closed"メッセージの後に続くのを見ているだけです。何か不足していますか?

9
追加された 著者 Richard Chambers,
@the_ajp:ハンドシェークを送信した後のl_Stream.Write(Encoding.UTF8.GetBytes( "Test")、0、5)の場合、クライアント上に表示されません。
追加された 著者 Bullines,
FYI:WebSocketは.NET 4.5に含まれます
追加された 著者 dtb,
私はソケットにヘッダー以外のものを書いているのを見ません。
追加された 著者 albertjan,

1 答え

クライアントに「テスト」を送信する前に、もう少し手間のかかるプロトコルがあります。ここに行く。それは簡単なので、私はバイトのリストを使用して:)

List lb= new List();
// see http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 
//     page 30 for this:
// 0                   1                   2                   3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-------+-+-------------+-------------------------------+
// |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
// |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
// |N|V|V|V|       |S|             |   (if payload len==126/127)   |
// | |1|2|3|       |K|             |                               |
// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
// |     Extended payload length continued, if payload len == 127  |
// + - - - - - - - - - - - - - - - +-------------------------------+
// |                               |Masking-key, if MASK set to 1  |
// +-------------------------------+-------------------------------+
// | Masking-key (continued)       |          Payload Data         |
// +-------------------------------- - - - - - - - - - - - - - - - +
// :                     Payload Data continued ...                :
// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
// |                     Payload Data continued ...                |
// +---------------------------------------------------------------+
//
//0x81 = 10000001 which says according to the table above:
//       1        it's the final message 
//        000     RSV1-3 are extensions which must be negotiated first
//           0001 opcode %x1 denotes a text frame
lb.Add(0x81);
//0x04 = 00001000
//       0        No mask
//        0001000 Rest of the 7 bytes left is the length of the payload.
lb.Add(0x04);
// add the payload
lb.AddRange(Encoding.UTF8.GetBytes("Test"));
//write it!
l_Stream.Write (lb.ToArray(), 0, 6);

Whoohooここに結果!:

Screenshost of alerts

8
追加された
私も+1してくれました。
追加された 著者 Paul Aldred-Bann,
+1は広告として動作します。
追加された 著者 James,