2011-04-29 105 views
7

我想创建一个C#WebSocket服务器,但我似乎并没有得到它的工作。 我现在有一台接受TCPClient的服务器,接收来自客户端的HTTP请求,并尝试发回HTTP响应,以便完成HTML5 WebSocket握手。C#HTML5 Websocket服务器

我相信服务器发送给客户端的握手有问题。 我读了草稿(Websocket 76 draft),其中规定在握手结束时必须给出给出的两个键的响应。该响应由服务器计算。

这是我的代码:

static void Main(string[] args) 
    { 
     int port = 8181; 
     IPAddress localAddr = IPAddress.Loopback; 

     TcpListener server = new TcpListener(localAddr, port); 
     server.Start(); 

     // Buffer for reading data 
     Byte[] receivedBytes = new Byte[256]; 
     String data = null; 

     // Enter the listening loop. 
     while (true) 
     { 
      Console.WriteLine("Waiting for a connection..."); 

      // Perform a blocking call to accept requests. 
      // You could also user server.AcceptSocket() here. 
      TcpClient client = server.AcceptTcpClient(); 
      Console.WriteLine("Connected!\n"); 

      data = null; 

      // Get a stream object for reading and writing 
      NetworkStream stream = client.GetStream(); 

      int i; 


      // Loop to receive all the data sent by the client. 
      while ((i = stream.Read(receivedBytes, 0, receivedBytes.Length)) != 0) 
      { 
       // Translate data bytes to a ASCII string. 
       data = System.Text.Encoding.UTF8.GetString(receivedBytes, 0, i); 

       Console.WriteLine("Received:"); 
       Console.WriteLine(data); 
       Byte[] response_token = hashResponse(data); 


       string handshake = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" 
        + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" 
        + "Sec-WebSocket-Origin: http://localhost\r\n" 
        + "Sec-WebSocket-Location: ws://localhost:8181/websession\r\n" 
        + "\r\n"; 

       Byte[] writtenBytes = Encoding.UTF8.GetBytes(handshake); 

       stream.Write(writtenBytes, 0, writtenBytes.Length); 
       stream.Write(response_token, 0, response_token.Length); 

       Console.WriteLine("Send:"); 
       Console.WriteLine(handshake); 

       string strHash = Encoding.UTF8.GetString(response_token); 
       Console.WriteLine(strHash); 
      }     
     } 
    } 

    static Byte[] hashResponse(string receivedData) 
    { 
     string strDel = "\r\n"; 
     char[] delimeter = strDel.ToCharArray(); 

     string Key1 = null; 
     string Key2 = null; 
     string hash = null; 
     MD5 md5 = MD5.Create(); 

     string[] lines = receivedData.Split(delimeter); 
     Key1 = lines[10].Substring(20); 
     Key2 = lines[12].Substring(20); 
     hash = lines[16]; 

     Int64 numbersKey1 = Convert.ToInt64(string.Join(null, Regex.Split(Key1, "[^\\d]"))); 
     Int64 numbersKey2 = Convert.ToInt64(string.Join(null, Regex.Split(Key2, "[^\\d]"))); 

     Int64 numberSpaces1 = countSpaces(Key1); 
     Int64 numberSpaces2 = countSpaces(Key2); 

     int dividedKey1 = (int) (numbersKey1/numberSpaces1); 
     int dividedKey2 = (int) (numbersKey2/numberSpaces2); 

     Byte[] encodedKey1 = Encoding.UTF8.GetBytes(dividedKey1.ToString()); 
     Byte[] encodedKey2 = Encoding.UTF8.GetBytes(dividedKey2.ToString()); 
     Byte[] encodedHash = Encoding.UTF8.GetBytes(hash); 

     Byte[] combined = Encoding.UTF8.GetBytes(dividedKey1.ToString() + dividedKey2.ToString() + hash); 

     Byte[] responseHash = md5.ComputeHash(combined); 
     return responseHash; 
    } 

    static int countSpaces(string key) 
    { 
     int counter = 0; 
     char[] charArray = key.ToCharArray(); 

     foreach (char c in charArray) 
     { 
      if (c.Equals(' ')) 
       counter++; 
     } 

     return counter; 
    } 

的HTML页面我使用测试(这是创建一个名为Test.html)由运行在我的电脑上的Apache网络服务器托管我通过浏览访问它(在Chrome)到http://localhost/Test.html

有没有人有线索我做错了,因为我越来越绝望。

在此先感谢

丹尼斯

回答

0

尝试尝试接收插座

这里的数据之前,发送握手数据,可以帮助你举个例子

websocksample

+0

我不是真的想握手之前接收数据,这是请求客户端发送到我在控制台屏幕上写入的服务器。 – user729932 2011-04-30 11:51:54

+0

在发送握手响应“HTTP/1.1 101 WebSocket协议等”之前,您正在尝试接收数据的一个连接。我认为在您收到连接时您需要发送此消息。在您接收到的数据流之前, – 2011-04-30 18:54:23

9

以下是我写的示例服务器,说明了根据的握手阶段210:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Net; 
using System.Net.Sockets; 
using System.Security.Cryptography; 
using System.Text; 
using System.Text.RegularExpressions; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var listener = new TcpListener(IPAddress.Loopback, 8080); 
     listener.Start(); 
     while (true) 
     { 
      using (var client = listener.AcceptTcpClient()) 
      using (var stream = client.GetStream()) 
      { 
       var headers = new Dictionary<string, string>(); 
       string line = string.Empty; 
       while ((line = ReadLine(stream)) != string.Empty) 
       { 
        var tokens = line.Split(new char[] { ':' }, 2); 
        if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1) 
        { 
         headers[tokens[0]] = tokens[1].Trim(); 
        } 
       } 

       var key = new byte[8]; 
       stream.Read(key, 0, key.Length); 

       var key1 = headers["Sec-WebSocket-Key1"]; 
       var key2 = headers["Sec-WebSocket-Key2"]; 

       var numbersKey1 = Convert.ToInt64(string.Join(null, Regex.Split(key1, "[^\\d]"))); 
       var numbersKey2 = Convert.ToInt64(string.Join(null, Regex.Split(key2, "[^\\d]"))); 
       var numberSpaces1 = CountSpaces(key1); 
       var numberSpaces2 = CountSpaces(key2); 

       var part1 = (int)(numbersKey1/numberSpaces1); 
       var part2 = (int)(numbersKey2/numberSpaces2); 

       var result = new List<byte>(); 
       result.AddRange(GetBigEndianBytes(part1)); 
       result.AddRange(GetBigEndianBytes(part2)); 
       result.AddRange(key); 

       var response = 
        "HTTP/1.1 101 WebSocket Protocol Handshake" + Environment.NewLine + 
        "Upgrade: WebSocket" + Environment.NewLine + 
        "Connection: Upgrade" + Environment.NewLine + 
        "Sec-WebSocket-Origin: " + headers["Origin"] + Environment.NewLine + 
        "Sec-WebSocket-Location: ws://localhost:8080/websession" + Environment.NewLine + 
        Environment.NewLine; 

       var bufferedResponse = Encoding.UTF8.GetBytes(response); 
       stream.Write(bufferedResponse, 0, bufferedResponse.Length); 
       using (var md5 = MD5.Create()) 
       { 
        var handshake = md5.ComputeHash(result.ToArray()); 
        stream.Write(handshake, 0, handshake.Length); 
       } 
      } 
     } 
    } 

    static int CountSpaces(string key) 
    { 
     return key.Length - key.Replace(" ", string.Empty).Length; 
    } 

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

    static byte[] GetBigEndianBytes(int value) 
    { 
     var bytes = 4; 
     var buffer = new byte[bytes]; 
     int num = bytes - 1; 
     for (int i = 0; i < bytes; i++) 
     { 
      buffer[num - i] = (byte)(value & 0xffL); 
      value = value >> 8; 
     } 
     return buffer; 
    } 
} 

和样本客户端:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <script type="text/javascript"> 
     var socket = new WebSocket('ws://localhost:8080/websession'); 
     socket.onopen = function() { 
      alert('handshake successfully established. May send data now...'); 
     }; 
     socket.onclose = function() { 
      alert('connection closed'); 
     }; 
    </script> 
</head> 
<body> 
</body> 
</html> 
+0

嗨达林,首先+1工作样本,我喜欢这一点。不过,我已经使用Chrome 16试用了您的示例,并且它不工作,因为Chrome并未按照您的服务器期望发送Sec-WebSockets-Key1标头,您使用哪个UA进行此测试? – 2012-01-13 15:03:43

+0

@EugenioMiró,我用过Chrome,不知道在发布这个答案的时候是哪个版本:2011年5月3日。我的回答是基于'draft-ietf-hybi-thewebsocketprotocol-00'。现在有[RFC 6455](http://tools.ietf.org/html/rfc6455),我相信这是最新的规范,但我不知道Chrome 16实现了哪一个。 – 2012-01-13 15:14:35

+0

嗨达林,我发现Chrome现在发送了13个版本的头文件,所以我从那里开始,感谢您的快速响应! – 2012-01-13 17:17:37

1

Do't知道,如果你可以编译目标C,但this project is really pretty cool ......

黑匣子是一个嵌入式HTTP可可服务器 - 允许您将HTTP 资源与Cocoa“响应者”对象(类似于Twisted Web 对Python所做的)相关联,而不是文件系统上的文件。

使用Blackbox,您可以快速创建个人文件共享器,编写可通过HTTP相互通信的应用程序,并且可以轻松创建用于无头应用程序的Web控制界面。

它基本上是一个很好的包Comet server。我希望我能说更多地了解它,但我仍然是那种试图找出插座自己...

Please note the "Comet demo" window is a concoction of mine, so don't get frustrated when you run the demo, just open your browser!