我有一个C#服务器端websocket代码,我从Chrome(javascript websocket客户端)发送测试数据。
接下来:https://stackoverflow.com/a/8125509/2508439和其他一些链接,我创建了我的C#webserver(服务器端)解码函数,如下所示:
public String DecodeMessage(Byte[] bytes)
{
Console.WriteLine("+DecodeMessage+");
String incomingData = String.Empty;
Byte secondByte = bytes[1];
bool masked = (bytes[1] & 128) != 0;
//long dataLength = secondByte & 127;
dataLength = secondByte & 127;
//Int32 indexFirstMask = 2;
indexFirstMask = 2;
if (masked)
{
Console.WriteLine("Masked bit SET");
}
if (dataLength == 126)
{
indexFirstMask = 4;
dataLength = bytes[3] | bytes[2] << 8;
}
else if (dataLength == 127)
{
indexFirstMask = 10;
dataLength = bytes[9] | bytes[8] << 8 | bytes[7] << 16 | bytes[6] << 24 | bytes[5] << 32 |
bytes[4] << 40 | bytes[3] << 48 | bytes[2] << 56;
}
//IEnumerable<Byte> keys = bytes.Skip(indexFirstMask).Take(4);
keys = bytes.Skip(indexFirstMask).Take(4);
Int32 indexFirstDataByte = indexFirstMask + 4;
Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
Console.WriteLine("dataLength : " + dataLength + " ; bytes.Length : " + bytes.Length);
Int32 j = 0;
for (Int32 i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeMessage-");
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
public String DecodeRemainingMessage(Byte[] bytes, long bytesAlreadyRead)
{
Console.WriteLine("+DecodeRemainingMessage+");
String incomingData = String.Empty;
Int32 indexFirstDataByte = 0;
if ( indexFirstMask == 10 )//special case, what to do here?
{
indexFirstDataByte = 10;
}
Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
//Byte[] decoded = new Byte[bytes.Length];
Int32 j = 0;
for (Int32 i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeRemainingMessage-");
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
简单的数据包(125或更小的到达就好了)。
如果大小约125并且小于65535,也可以很好地到达(种类:有一些细节,但我现在没有进入[*])。
65535以上的数据包:整个解码功能变得疯狂:
只有第一次正确解码数据包,之后,我收到的数据完全是二进制(不可读),并且在第一个数据包到达后,连续数据包中:
if (dataLength == 126)
{
...
}
else if (dataLength == 127) ...
两个条件永远不会满足,并且dataLength总是小于126,然后将其解码为(小)数据包,因此永远不会正确地重建。
谁能突出我可能做错了什么?
谢谢
[*] =>低于65535长度的数据有时会出现两个以上的数据包,然后其行为与较大的数据包相同,并且第一次触发此功能后的数据包永远不会再次正确重建。
编辑1:@Marc
根据你的评论,我在上面的函数中放了'masked bit check',我可以看到它总是被设置为'1'(正如预期的那样,因为现在这只是服务器端代码)。
我也在不同的函数中解析控制框架,在这个函数中,如果我的代码是正确的,我可能会得到大量的垃圾数据。
详细说明,请参阅以下这些功能:
整个逻辑代码:
枚举:
public enum ControlFrame { NA=0, CloseConnection=1, Ping=2, Pong=4, Text=8, Binary=16, ContinueFrame =32, FinalFrame=64 };
解析控制框架功能:
private int ParseControlFrame(byte controlFrame)
{
int rv = (int)ControlFrame.NA;
bool isFinalFrame = (controlFrame & 0x80) == 0x80 ;
byte opCode = (byte)((controlFrame & 0x0F));
if ( opCode >= 0x3 && opCode <= 0x7 ||
opCode >= 0xB && opCode <= 0xF )//special frame, ignore it
{
Console.WriteLine("Reserved Frame received");
return rv;
}
if (opCode == 0x8 || opCode == 0x0 || opCode == 0x1 || opCode == 0x2 || opCode == 0x9 || opCode == 0xA) //proceed furter
{
if (opCode == 0x0) //continue frame
{
rv |= (int)ControlFrame.ContinueFrame;
Console.WriteLine("Continue Frame received");
}
if (opCode == 0x1) //text frame
{
rv |= (int)ControlFrame.Text;
Console.WriteLine("Text Frame received");
}
if (opCode == 0x2) //binary frame
{
rv |= (int)ControlFrame.Binary;
Console.WriteLine("Binary frame received");
}
if (opCode == 0x8) //connection closed
{
rv |= (int)ControlFrame.CloseConnection;
Console.WriteLine("CloseConnection Frame received");
}
if (opCode == 0x9) //ping
{
rv |= (int)ControlFrame.Ping;
Console.WriteLine("PING received");
}
if (opCode == 0xA) //pong
{
rv |= (int)ControlFrame.Pong;
Console.WriteLine("PONG received");
}
}
else // invalid control bit, must close the connection
{
Console.WriteLine("invalid control frame received, must close connection");
rv = (int)ControlFrame.CloseConnection;
}
if (isFinalFrame) //Final frame ...
{
rv |= (int)ControlFrame.FinalFrame;
Console.WriteLine("Final frame received");
}
//else
//{
// rv |= (int)ControlFrame.ContinueFrame;
// Console.WriteLine("Continue frame received");
//}
return rv;
}
逻辑流程(实际的代码片段):
if (stream.DataAvailable)
{
long bytesAlreadyRead = 0;
bool breakMain = false;
while (client.Available > 0 )
{
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
Console.WriteLine("if (stream.DataAvailable):\nclient.Available : " + client.Available +
" ; bytes.Length : " + bytes.Length);
//translate bytes of request to string
String data = Encoding.UTF8.GetString(bytes);
Console.WriteLine("Message received on: " + DateTime.Now);
if (bytesAlreadyRead == 0)
{
int controlFrame = ParseControlFrame(bytes[0]);
if (controlFrame == (int)ControlFrame.NA ||
(int)(controlFrame & (int)ControlFrame.Ping) > 0 ||
(int)(controlFrame & (int)ControlFrame.Pong) > 0) //ignore it
{
}
else
{
if ((int)(controlFrame & (int)ControlFrame.CloseConnection) > 0)
{
Console.WriteLine("Connection #" + c.Key + " Closed on: " + DateTime.Now);
tcpClients.Remove(c.Key);
breakMain = true;
break;
}
else
{
string result = c.Value.DecodeMessage(bytes);
File.WriteAllText("recvfile.txt", result);
}
}
}
else
{
string result = c.Value.DecodeRemainingMessage(bytes, bytesAlreadyRead);
File.AppendAllText("recvfile.txt", "\n");
File.AppendAllText("recvfile.txt", result);
}
bytesAlreadyRead += bytes.Length;
}
if ( breakMain == true )
{
break;
}
}
我没有垃圾,但数据丢失了。
如果我不进行此检查,那么,我开始接收垃圾。
基于Console.WriteLine输出,我看到类似于小于65535的数据:
Message received on: 12/29/2017 12:59:00 PM
Text Frame received
Final frame received
Masked bit SET
Message received on: 12/29/2017 12:59:12 PM
Text Frame received
Final frame received
Masked bit SET
对于65535以上的数据:
Message received on: 12/29/2017 1:02:51 PM
Text Frame received
Final frame received
Masked bit SET
Message received on: 12/29/2017 1:02:51 PM
Reserved Frame received
即小于65535,我很好(大部分时间)。
超过65535,事情变得奇怪。
当你提到:
I wonder if what is happening is that you're getting multiple messages in a single Read call (perfectly normal in TCP), consuming the first message, and incorrectly treating the entire bytes as consumed.
我以前从未想过这个,也许我还需要以某种方式处理这个问题?
编辑2:
根据你的评论,我修改了'if(stream.DataAvailable)'逻辑,所以它继续在while循环中读取数据,直到实际刷新了本地存储的所有数据。
所以我可能接近解决它(感谢您的反馈),但第二次调用DecodeMessage()函数,它仍然在垃圾(二进制)数据中解密。
我正在努力搞清楚!
谢谢
编辑3:
好的,根据你的建议,我已经整理了大部分逻辑。但是,'DecodeRemainingMessage'功能中的特殊情况仍然是个谜。 [我将一些变量从局部范围转移到了类范围,因此它们在函数中被注释掉] ......
如果我弄错了,我不需要在这里放置任何特殊条件,但在这种情况下,我仍然会收到垃圾。
有什么指针吗?
[对不起凌乱的代码,一旦我得到正确的图片就会更新!]
谢谢
编辑4:在评论/聊天中的所有建议后帮助我达到了大大更新解码逻辑的程度,并且在高于65535字节的情况下仍然无法获得正确的数据。但是当我尝试使用Firefox的最终逻辑时,我得到了所有数据!非常感谢你,而且,我仍然需要弄清楚如何处理有问题的Chrome客户端!谢谢!!
编辑:您的代码假定传入的帧始终被屏蔽。如果您的代码只是服务器,那么这可能是正常的,但您可能想要检查bytes[1] & 128
是设置(屏蔽)还是清除(未屏蔽)。如果未屏蔽:标头缩短了4个字节。如果这只是一个服务器,那么你应该没问题(从RFC6455中的5.2开始):
从客户端发送到服务器的所有帧都将此位设置为1。
但是:仔细检查会很好。你会惊讶地发现有多少违规的客户违反规范。
--
总体代码看起来很好,并且与what I have here大致相当。我什么都看不出来。这让我怀疑这里的问题是TCP流媒体;很明显,你的方法正在做任何事情来报告该帧逻辑上应该消耗多少字节数 - 即总报头长度加上有效载荷长度。为了与我的代码进行比较,这将是out int headerLength
和frame.PayloadLength
的组合。
我想知道发生的事情是你在一个Read
调用中获得多个消息(在TCP中完全正常),消耗第一条消息,并错误地将整个bytes
视为已消耗。这意味着您开始在下一帧标题的错误位置读取。单个Read
调用可以返回一帧,一帧或多于一帧的片段 - 它唯一不能返回的是0字节,除非套接字已关闭。