mirror of
https://github.com/islehorse/HISP.git
synced 2025-04-20 11:49:14 +12:00
Improve WebSocket performance
This commit is contained in:
parent
eefc2b926d
commit
458748e6b0
8 changed files with 118 additions and 73 deletions
|
@ -59,9 +59,23 @@ namespace HISP.Cli
|
|||
|
||||
shutdownHandle.Set();
|
||||
}
|
||||
|
||||
private static string formatMessage(string type, string text)
|
||||
{
|
||||
#if OS_WINDOWS
|
||||
string newline = "\r\n";
|
||||
#else
|
||||
string newline = "\n";
|
||||
#endif
|
||||
|
||||
return DateTime.Now.ToString("MM-dd-yyyy HH:mm:dd") + ": [" + type + "] " + text + newline;
|
||||
}
|
||||
|
||||
public static void LogToFile(bool error, string type,string text)
|
||||
{
|
||||
sw.WriteLine(DateTime.Now.ToString("MM-dd-yyyy HH:mm:dd") + ": [" + type + "] " + text + sw.NewLine);
|
||||
sw.WriteLine(formatMessage(type, text));
|
||||
if (error)
|
||||
sw.Flush();
|
||||
}
|
||||
public static void LogStdout(bool error, string type, string text)
|
||||
{
|
||||
|
@ -69,10 +83,10 @@ namespace HISP.Cli
|
|||
LogToFile(error, type, text);
|
||||
|
||||
if (error)
|
||||
Console.Error.WriteAsync(DateTime.Now.ToString("MM-dd-yyyy HH:mm:dd")+": [" + type + "] " + text + Console.Error.NewLine);
|
||||
Console.Error.WriteAsync(formatMessage(type, text));
|
||||
else
|
||||
Console.Out.WriteAsync(DateTime.Now.ToString("MM-dd-yyyy HH:mm:dd") + ": [" + type + "] " + text + Console.Out.NewLine);
|
||||
|
||||
Console.Out.WriteAsync(formatMessage(type, text));
|
||||
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
|
@ -92,12 +106,12 @@ namespace HISP.Cli
|
|||
switch (arg)
|
||||
{
|
||||
case "--install-service":
|
||||
#if OS_LINUX
|
||||
#if OS_LINUX
|
||||
File.WriteAllBytes("/etc/systemd/system/HISP.service", Resources.HISPService);
|
||||
LogStdout(false, "INFO", "Crreated Service! enable it with \"sudo systemctl enable HISP\"");
|
||||
#else
|
||||
#else
|
||||
LogStdout(true, "ERROR", "Installing as a service unsupported on this platform");
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
if (arg.Contains("="))
|
||||
|
|
|
@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.8.42.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.42.0")]
|
||||
[assembly: AssemblyVersion("1.8.45.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.45.0")]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Package: hisp
|
||||
Version: 1.8.42
|
||||
Version: 1.8.45
|
||||
Depends: coreutils,systemd,mariadb-server,libsqlite3-dev,zlib1g-dev,libicu-dev,libkrb5-dev
|
||||
Maintainer: Li
|
||||
Homepage: https://islehorse.com
|
||||
|
|
|
@ -30,8 +30,8 @@ using System.Runtime.InteropServices;
|
|||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.8.42.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.42.0")]
|
||||
[assembly: AssemblyVersion("1.8.45.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.45.0")]
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ namespace HISP.Server.Network
|
|||
internal Action<byte[]> onReceiveCallback;
|
||||
internal Action onDisconnectCallback;
|
||||
|
||||
internal List<byte> currentPacket = new List<byte>();
|
||||
internal byte[] workBuffer = new byte[0xFFFF];
|
||||
|
||||
internal bool isDisconnecting = false;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//#define WEBSOCKET_DEBUG
|
||||
#define WEBSOCKET_DEBUG
|
||||
using HISP.Security;
|
||||
using HISP.Util;
|
||||
using System;
|
||||
|
@ -26,8 +26,8 @@ namespace HISP.Server.Network
|
|||
private const int WEBSOCKET_EXPECTED_SIZE_SET = 0;
|
||||
private const int WEBSOCKET_EXPECTED_SIZE_UNSET = -1;
|
||||
|
||||
private List<byte> currentMessage = new List<byte>();
|
||||
|
||||
private byte[] currentMessage = new byte[0];
|
||||
private byte[] currentPacket = new byte[0];
|
||||
|
||||
private byte lastOpcode;
|
||||
private Int64 expectedLength = -1;
|
||||
|
@ -126,59 +126,58 @@ namespace HISP.Server.Network
|
|||
|
||||
public override void ProcessReceivedPackets(int available, byte[] buffer)
|
||||
{
|
||||
Int64 received = 0;
|
||||
// add to current packet
|
||||
// if current packet is less than size of an expected incoming message
|
||||
// then keep receiving until full message received.
|
||||
|
||||
for (received = 0; received < available; received++)
|
||||
currentPacket.Add(buffer[received]);
|
||||
int oldLength = currentPacket.Length;
|
||||
Array.Resize(ref currentPacket, oldLength + available);
|
||||
Array.ConstrainedCopy(buffer, 0, currentPacket, oldLength, available);
|
||||
|
||||
if (isCurrentPacketLengthLessThanExpectedLength())
|
||||
return;
|
||||
else
|
||||
setUnknownExpectedLength();
|
||||
|
||||
byte[] webSocketMsg = currentPacket.ToArray();
|
||||
//byte[] webSocketMsg = currentPacket.ToArray();
|
||||
|
||||
if (!handshakeDone)
|
||||
{
|
||||
|
||||
if (IsStartOfHandshake(webSocketMsg) && IsEndOfHandshake(webSocketMsg))
|
||||
if (IsStartOfHandshake(currentPacket) && IsEndOfHandshake(currentPacket))
|
||||
{
|
||||
string httpHandshake = Encoding.UTF8.GetString(webSocketMsg);
|
||||
string httpHandshake = Encoding.UTF8.GetString(currentPacket);
|
||||
byte[] handshakeResponse = parseHandshake(httpHandshake);
|
||||
base.Send(handshakeResponse);
|
||||
|
||||
currentPacket.Clear();
|
||||
Array.Resize(ref currentPacket, 0);
|
||||
handshakeDone = true;
|
||||
}
|
||||
}
|
||||
else if(currentPacket.Count > 2) // else, begin parsing websocket message
|
||||
else if(currentPacket.Length > 2) // else, begin parsing websocket message
|
||||
{
|
||||
|
||||
byte[] unmaskKey = new byte[4];
|
||||
|
||||
bool finished = (webSocketMsg[0] & 0b10000000) != 0;
|
||||
bool finished = (currentPacket[0] & 0b10000000) != 0;
|
||||
|
||||
bool rsv1 = (webSocketMsg[0] & 0b01000000) != 0;
|
||||
bool rsv2 = (webSocketMsg[0] & 0b00100000) != 0;
|
||||
bool rsv3 = (webSocketMsg[0] & 0b00010000) != 0;
|
||||
bool rsv1 = (currentPacket[0] & 0b01000000) != 0;
|
||||
bool rsv2 = (currentPacket[0] & 0b00100000) != 0;
|
||||
bool rsv3 = (currentPacket[0] & 0b00010000) != 0;
|
||||
|
||||
byte opcode = Convert.ToByte(webSocketMsg[0] & 0b00001111);
|
||||
byte opcode = Convert.ToByte(currentPacket[0] & 0b00001111);
|
||||
|
||||
bool mask = (webSocketMsg[1] & 0b10000000) != 0;
|
||||
Int64 messageLength = Convert.ToInt64(webSocketMsg[1] & 0b01111111);
|
||||
bool mask = (currentPacket[1] & 0b10000000) != 0;
|
||||
Int64 messageLength = Convert.ToInt64(currentPacket[1] & 0b01111111);
|
||||
|
||||
int offset = 2;
|
||||
|
||||
|
||||
if (messageLength == WEBSOCKET_LENGTH_INT16)
|
||||
{
|
||||
if (webSocketMsg.LongLength >= offset + 2)
|
||||
if (currentPacket.LongLength >= offset + 2)
|
||||
{
|
||||
byte[] uint16Bytes = new byte[2];
|
||||
Array.ConstrainedCopy(webSocketMsg, offset, uint16Bytes, 0, uint16Bytes.Length);
|
||||
Array.ConstrainedCopy(currentPacket, offset, uint16Bytes, 0, uint16Bytes.Length);
|
||||
uint16Bytes = uint16Bytes.Reverse().ToArray();
|
||||
messageLength = BitConverter.ToUInt16(uint16Bytes);
|
||||
|
||||
|
@ -187,10 +186,10 @@ namespace HISP.Server.Network
|
|||
}
|
||||
else if (messageLength == WEBSOCKET_LENGTH_INT64)
|
||||
{
|
||||
if (webSocketMsg.LongLength >= offset + 8)
|
||||
if (currentPacket.LongLength >= offset + 8)
|
||||
{
|
||||
byte[] int64Bytes = new byte[8];
|
||||
Array.ConstrainedCopy(webSocketMsg, offset, int64Bytes, 0, int64Bytes.Length);
|
||||
Array.ConstrainedCopy(currentPacket, offset, int64Bytes, 0, int64Bytes.Length);
|
||||
int64Bytes = int64Bytes.Reverse().ToArray();
|
||||
messageLength = BitConverter.ToInt64(int64Bytes);
|
||||
|
||||
|
@ -200,13 +199,13 @@ namespace HISP.Server.Network
|
|||
|
||||
if (mask)
|
||||
{
|
||||
Array.ConstrainedCopy(webSocketMsg, offset, unmaskKey, 0, unmaskKey.Length);
|
||||
Array.ConstrainedCopy(currentPacket, offset, unmaskKey, 0, unmaskKey.Length);
|
||||
offset += unmaskKey.Length;
|
||||
}
|
||||
|
||||
// Handle tcp fragmentation
|
||||
|
||||
Int64 actualLength = (webSocketMsg.LongLength - offset);
|
||||
Int64 actualLength = (currentPacket.LongLength - offset);
|
||||
|
||||
// check if full message received, if not then set expected length
|
||||
// and return, thus entering the loop at the beginning
|
||||
|
@ -216,11 +215,13 @@ namespace HISP.Server.Network
|
|||
webSocketLog("Partial websocket frame received, expected size: " + messageLength + " got size: " + actualLength);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
setUnknownExpectedLength();
|
||||
currentPacket.Clear();
|
||||
}
|
||||
|
||||
// clone current packet array
|
||||
byte[] currentPacketCopy = currentPacket.ToArray();
|
||||
|
||||
// set current packet array size back to 0
|
||||
setUnknownExpectedLength();
|
||||
Array.Resize(ref currentPacket, 0);
|
||||
|
||||
// dont care about extensions
|
||||
if (rsv1 || rsv2 || rsv3) return;
|
||||
|
@ -237,8 +238,17 @@ namespace HISP.Server.Network
|
|||
case WEBSOCKET_BINARY:
|
||||
case WEBSOCKET_TEXT:
|
||||
case WEBSOCKET_PING:
|
||||
for (Int64 i = 0; i < messageLength; i++)
|
||||
currentMessage.Add(mask ? Convert.ToByte(webSocketMsg[offset + i] ^ unmaskKey[i % unmaskKey.Length]) : Convert.ToByte(webSocketMsg[offset + i]));
|
||||
oldLength = currentMessage.Length;
|
||||
Array.Resize(ref currentMessage, oldLength + Convert.ToInt32(messageLength));
|
||||
if (mask)
|
||||
{
|
||||
for (int i = 0; i < Convert.ToInt32(messageLength); i++)
|
||||
currentMessage[oldLength + i] = Convert.ToByte(currentPacketCopy[offset + i] ^ unmaskKey[i % unmaskKey.Length]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.ConstrainedCopy(currentPacketCopy, offset, currentMessage, oldLength, Convert.ToInt32(messageLength));
|
||||
}
|
||||
break;
|
||||
case WEBSOCKET_CLOSE:
|
||||
this.Disconnect();
|
||||
|
@ -249,14 +259,13 @@ namespace HISP.Server.Network
|
|||
if (finished)
|
||||
{
|
||||
|
||||
byte[] message = currentMessage.ToArray();
|
||||
|
||||
if (lastOpcode != WEBSOCKET_PING && message.LongLength > 0)
|
||||
onReceiveCallback(message);
|
||||
if (lastOpcode != WEBSOCKET_PING && currentMessage.LongLength > 0)
|
||||
onReceiveCallback(currentMessage);
|
||||
else
|
||||
Send(message);
|
||||
Send(currentMessage);
|
||||
|
||||
currentMessage.Clear();
|
||||
Array.Resize(ref currentMessage, 0);
|
||||
Array.Resize(ref currentPacket, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -274,22 +283,22 @@ namespace HISP.Server.Network
|
|||
if (totalSent <= total)
|
||||
total = Convert.ToInt32(totalSent);
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
buffer[i] = webSocketMsg[loc + i];
|
||||
|
||||
|
||||
Array.ConstrainedCopy(currentPacketCopy, Convert.ToInt32(loc), buffer, 0, total);
|
||||
|
||||
webSocketLog("Found another frame at the end of this one, processing!");
|
||||
ProcessReceivedPackets(total, buffer);
|
||||
|
||||
loc += total;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// specify transport name is "WebSocket"
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
|
@ -321,7 +330,7 @@ namespace HISP.Server.Network
|
|||
|
||||
// despite its name, this has nothing to do with graphics
|
||||
// rather this is for WebSocket frames
|
||||
List<byte> frameBuffer = new List<byte>();
|
||||
List<byte> frameHeader = new List<byte>();
|
||||
|
||||
for (Int64 remain = totalData; remain > 0; remain -= toSend)
|
||||
{
|
||||
|
@ -337,7 +346,7 @@ namespace HISP.Server.Network
|
|||
finish = true;
|
||||
}
|
||||
|
||||
frameBuffer.Add(Convert.ToByte((0x00) | (finish ? 0b10000000 : 0b00000000) | opcode));
|
||||
frameHeader.Add(Convert.ToByte((0x00) | (finish ? 0b10000000 : 0b00000000) | opcode));
|
||||
|
||||
// do special length encoding
|
||||
byte maskAndLength = Convert.ToByte((0x00) | (mask ? 0b10000000 : 0b00000000));
|
||||
|
@ -363,23 +372,40 @@ namespace HISP.Server.Network
|
|||
}
|
||||
|
||||
// Add to buffer
|
||||
frameBuffer.Add(maskAndLength);
|
||||
Helper.ByteArrayToByteList(additionalLengthData, frameBuffer);
|
||||
frameHeader.Add(maskAndLength);
|
||||
Helper.ByteArrayToByteList(additionalLengthData, frameHeader);
|
||||
|
||||
// Generate masking key;
|
||||
byte[] maskingKey = new byte[4];
|
||||
GameServer.RandomNumberGenerator.NextBytes(maskingKey);
|
||||
|
||||
if (mask)
|
||||
Helper.ByteArrayToByteList(maskingKey, frameBuffer);
|
||||
if (mask)
|
||||
{
|
||||
GameServer.RandomNumberGenerator.NextBytes(maskingKey);
|
||||
Helper.ByteArrayToByteList(maskingKey, frameHeader);
|
||||
}
|
||||
|
||||
int headerSize = frameHeader.Count;
|
||||
|
||||
byte[] frame = new byte[toSend + headerSize];
|
||||
Array.Copy(frameHeader.ToArray(), frame, headerSize);
|
||||
frameHeader.Clear();
|
||||
|
||||
// Mask data using key.
|
||||
Int64 totalSent = (totalData - remain);
|
||||
for (int i = 0; i < toSend; i++)
|
||||
frameBuffer.Add(mask ? Convert.ToByte(data[i + totalSent] ^ maskingKey[i % maskingKey.Length]) : Convert.ToByte(data[i + totalSent]));
|
||||
|
||||
if (mask) // are we masking this response?
|
||||
{
|
||||
// Mask data using key.
|
||||
for (int i = 0; i < toSend; i++)
|
||||
frame[i + headerSize] = Convert.ToByte(data[i + totalSent] ^ maskingKey[i % maskingKey.Length]);
|
||||
}
|
||||
else if(data.LongLength < Int32.MaxValue) // is out packet *really* bigger than 32 max int??
|
||||
{
|
||||
Array.ConstrainedCopy(data, Convert.ToInt32(totalSent), frame, headerSize, toSend);
|
||||
}
|
||||
|
||||
// Finally send complete frame over the network
|
||||
base.Send(frame);
|
||||
|
||||
// Finally send it over the network
|
||||
base.Send(frameBuffer.ToArray());
|
||||
if (this.Disconnected) return; // are we still here?
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
using HISP.Security;
|
||||
using HISP.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HISP.Server.Network
|
||||
{
|
||||
public class XmlSocket : Transport
|
||||
{
|
||||
private List<byte> currentPacket = new List<byte>();
|
||||
private const byte XMLSOCKET_PACKET_TERMINATOR = 0x00;
|
||||
public override void ProcessReceivedPackets(int available, byte[] buffer)
|
||||
{
|
||||
// In XmlSocket Packets are terminates by 0x00 so we have to read until we receive that terminator
|
||||
|
||||
for (int i = 0; i < available; i++)
|
||||
{
|
||||
if (buffer[i] == XMLSOCKET_PACKET_TERMINATOR) // Read until \0...
|
||||
|
@ -38,10 +41,13 @@ namespace HISP.Server.Network
|
|||
|
||||
public override void Send(byte[] data)
|
||||
{
|
||||
int oldLength = data.Length;
|
||||
|
||||
// Resize the array to be 1 extra byte in size;
|
||||
Array.Resize(ref data, oldLength + 1);
|
||||
|
||||
// add \0 to the end of the buffer
|
||||
byte[] buffer = new byte[data.Length + 1];
|
||||
Array.Copy(data, buffer, data.Length); // copy packet to buffer
|
||||
buffer[buffer.Length - 1] = XMLSOCKET_PACKET_TERMINATOR;
|
||||
data[oldLength] = XMLSOCKET_PACKET_TERMINATOR;
|
||||
|
||||
// send to the server
|
||||
base.Send(data);
|
||||
|
|
|
@ -30,8 +30,8 @@ using System.Runtime.InteropServices;
|
|||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.8.42.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.42.0")]
|
||||
[assembly: AssemblyVersion("1.8.45.0")]
|
||||
[assembly: AssemblyFileVersion("1.8.45.0")]
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue