2011-11-07 35 views
2

所以我决定通过给自己一些有趣的任务开始学习它。作为一个Web开发者,我想拥有自己的WebSocket服务器。 所以我写了它,但它只接受第一个请求。之后有算术运算。
下面是一些代码,让你看看我做错了什么:S我真的没有想法。在C中读取套接字时算术运算溢出了#

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

using WebSocket.Utils; 

namespace WebSocket 
{ 
    class SocketReader 
    { 
     public EndPoint ipAddr { get; set; } 
     private Socket userSocket; 
     private byte[] buffer; 
     private string SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 

     public SocketReader(Socket socket) 
     { 
      userSocket = socket; 
      ipAddr = socket.RemoteEndPoint; 
      Read(); 
     } 

     private void Read() 
     { 
      //Read packet size 
      buffer = new byte[2]; 
      userSocket.BeginReceive(buffer, 0, 2, SocketFlags.None, ReadCallbackStatic, null); 
     } 

     private void ReadCallbackStatic(IAsyncResult ar) 
     { 
      try 
      { 
       if (userSocket.EndReceive(ar) >= 1) 
       { 
        int bufferSize = BitConverter.ToInt16(buffer, 0); 
        buffer = new byte[bufferSize - 2]; 
        //Read Packet 
        userSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReadCallback, null); 
       } 
      } 
      catch (Exception se) 
      { 
       Console.WriteLine("Something blew on ReadCallbackStatic"); 
       Console.WriteLine(se.Message); 
       Console.WriteLine(se.StackTrace); 
       Disconnect(); 
      } 
     } 

     private void ReadCallback(IAsyncResult ar) 
     { 
      try 
      { 
       //Copy the buffer so we can receive the next packet ASAP 
       byte[] buff = new byte[buffer.Length]; 
       Array.Copy(buffer, buff, buffer.Length); 
       Read(); 

       string handshakeStr = System.Text.Encoding.UTF8.GetString(buff); 
       string[] list = Regex.Split(handshakeStr, "\r\n"); 

       //Sec-WebSocket-Key: S5o6fCVLRMJhdXTF3H9w3Q== 
       //Sec-WebSocket-Version: 8 

       string key = ""; 
       string clientProtocol = "0"; 

       foreach (string str in list) 
       { 
        if (String.IsNullOrEmpty(str)) { continue; } 
        if (str.Length > 20 && str.Substring(0, 19) == "Sec-WebSocket-Key: ") 
        { 
         key = str.Substring(19); 
         continue; 
        } 

        if (str.Length > 20 && str.Substring(0, 23) == "Sec-WebSocket-Version: ") 
        { 
         clientProtocol = str.Substring(23); 
         continue; 
        } 
       } 

       if (String.IsNullOrEmpty(key)) 
       { 
        Disconnect(); 
       } 

       SHA1 shaEnc = new SHA1CryptoServiceProvider(); 

       byte[] byteString = ASCIIEncoding.ASCII.GetBytes(key + SOCKET_GUID); 
       byte[] hash = shaEnc.ComputeHash(byteString, 0, byteString.Length); 
       string acceptKey = Convert.ToBase64String(hash); 

       List<string> headers = new List<string>(); 
       headers.Add("HTTP/1.1 101 Switching Protocols"); 
       headers.Add("Upgrade: websocket"); 
       headers.Add("Connection: Upgrade"); 
       headers.Add("Sec-WebSocket-Accept: " + acceptKey); 

       foreach (string header in headers) 
       { 
        SendString(header + "\r\n"); 
       } 

       Console.WriteLine(acceptKey); 

       SendString("\r\n"); 

      } 
      catch (SocketException se) 
      { 
       Console.WriteLine("Something blew on ReadCallback"); 
       Console.WriteLine(se.Message); 
       Disconnect(); 
      } 
     } 

     private void SendString(string str) 
     { 
      userSocket.Send(Encoding.UTF8.GetBytes(str)); 
     } 

     private void Disconnect() 
     { 
      userSocket.Disconnect(false); 
      Console.WriteLine("Client with ip {0} Disconnected", ipAddr); 
     } 
    } 
} 

它缩短了我的课的版本,但被窃听我的问题出现在“ReadCallbackStatic”在这条线:

buffer = new byte[bufferSize - 2]; 

我真的不知道我做错了什么:S。
的事情是,...其实我握手正常但后来当我发送来自客户端的一些信息到我的服务器则抛出该异常

我做了一些调试,看来,在某种程度上缓冲变量变为负数OO

+1

你确定你应该减2? – harold

+0

为了回应哈罗德的观点 - 长度标记通常不包括长度标记的大小;意思是:如果标记(长度2)报告200字节,我期望读取*附加* 2字节,因此总共202个字节。 –

回答

0

我相信问题可能是在情况下,当缓冲区包含零个的数字,所以:

int bufferSize = BitConverter.ToInt16(buffer, 0); // 0 
    buffer = new byte[bufferSize - 2]; // 0 - 2 == -2 

这会导致溢出异常,而执行new byte[-2]

我不知道你的代码背后的逻辑,但我相信你应该分配一个新的缓冲区考虑buffer.Length

+0

没有减法也是一样的,是的,数据包长度是2个字节。有趣的是,缓冲区大小更像是“-3550”不只是0:/ 这里是控制台输出 “17735 0pV0tDprwlXs6BqAshUb + 0MPobg = -30079” 第一个数字是握手字符串。第二个是别的:( 曼波jambo字符串是用于与客户端握手的接受键(在本例中为谷歌浏览器) – Sk1ppeR

+0

@ Sk1ppeR查看我的答案; -3550将映射到61​​986,如果它是MSB问题,并且-30079会映射到35457 –

+1

@ Sk1ppeR:任何以这种方式分配缓冲区都很危险的方式,你可能会在某天得到另一个缓冲区溢出异常,所以在执行'bufferSize-2'之前,检查'bufferSize <= 2'或参考入站缓冲区。长度值 – sll

2

我的猜测是,当最高位,你吹起来,即可能的客户端发送一个数字> = 32768和< 65536,它(通过您的ToInt16)正在成为一个大的负16位数字(或者,或者,抛出算术溢出问题)。我会尝试使用ToUInt16来代替。

说实话,你真的不需要BitConverter这里;根据字节顺序有两种情况:

int bufferSize = (buffer[0] << 8) | buffer[1]; 

int bufferSize = buffer[0] | (buffer[1] << 8); 
+0

它与UInt转换现在它不会抛出异常,但它会给相当巨大的缓冲区大小,所以当我登录到控制台时,我实际上看不到它,因为有太多空白的新行。 我想我必须将它记录在文件上以查看正在发生的事情或只是断点调试它 – Sk1ppeR

+0

@ Sk1ppeR只有您的线规可以告诉您什么是正确的解释。请注意,65k并不是特别大,在很多情况下,你的缓冲区可能比数据小很多(这个想法是:你以大块的形式读取它)。 –