C# Web套接字在以非常快的速度发送数据时会出现异常行为

C# Web套接字在以非常快的速度发送数据时会出现异常行为,c#,sockets,networking,websocket,C#,Sockets,Networking,Websocket,我是WebSocket新手,所以如果是因为我的错误,我很抱歉,但是当我以while循环(只是为了测试)将数据从WebSocket(chrome)发送到服务器(C#,TCPListener)时,数据不会正确到达服务器。它的第一个字节通常会消失,有时键也会在两条消息之间混合。这导致我的服务器崩溃。当数据不规则发送或不是一个接一个地超高速发送时,这种情况永远不会发生 以下是服务器接收器代码: #region Receiver public class Receiver {

我是WebSocket新手,所以如果是因为我的错误,我很抱歉,但是当我以while循环(只是为了测试)将数据从WebSocket(chrome)发送到服务器(C#,TCPListener)时,数据不会正确到达服务器。它的第一个字节通常会消失,有时键也会在两条消息之间混合。这导致我的服务器崩溃。当数据不规则发送或不是一个接一个地超高速发送时,这种情况永远不会发生

以下是服务器接收器代码:

#region Receiver

    public class Receiver
    {
        private const int DEFAULT_DATA_LIMIT = 256; // Bytes

        private const int HEADER_LENGTH = 2;
        private const int KEYS_LENGTH = 4;

        private const int SHORT_BYTES = 2;
        private const int LONG_BYTES = 8;

        private const int SHORT_DATA = 126;
        private const int LONG_DATA = 127;

        private readonly Socket TargetSocket;

        private readonly object DataLock = new object();
        private readonly object amountOfDataReceivedLock = new object();
        private readonly object RecevingLock = new object();
        private readonly object CompletingRecieveLock = new object();

        private bool receiving;

        private int _amoutOfDataReceived;
        private byte[] Data;

        private int amountOfDataReceived
        {
            get
            {
                lock(amountOfDataReceivedLock)
                    return _amoutOfDataReceived;
            }
            set
            {
                lock(amountOfDataReceivedLock)
                    _amoutOfDataReceived = value;
            }
        }

        public int Capacity
        {
            get
            {
                return Data.Length;
            }
        }

        public int FreeSpace
        {
            get
            {
                return Capacity - amountOfDataReceived;
            }
        }

        public bool ReceivingData
        {
            get
            {
                return receiving;
            }
        }

        public bool ChunkReady
        {
            get
            {
                int wholeChunkLength = WholeChunkLength();

                if(wholeChunkLength == -1)
                    return false;

                return WholeChunkLength() <= amountOfDataReceived;
            }
        }

        public Receiver(Socket socket, int dataLimit)
        {
            this.TargetSocket = socket;
            Data = new byte[dataLimit];
        }

        public Receiver(Socket socket) : this(socket, DEFAULT_DATA_LIMIT) { }

        private int ChunkDataLength()
        {
            lock(DataLock)
            {
                lock(amountOfDataReceivedLock)
                {
                    if(amountOfDataReceived < 1)
                        return -1;

                    int lengthInfoIndex = HEADER_LENGTH - 1;

                    if(amountOfDataReceived < lengthInfoIndex + 1)
                        return -1;

                    int rawLength = Data[lengthInfoIndex] -128;

                    int bytesRequiredToGetLength = 0;

                    if(rawLength == SHORT_DATA)
                        bytesRequiredToGetLength = SHORT_BYTES;
                    else if(rawLength == LONG_DATA)
                        bytesRequiredToGetLength = LONG_BYTES;
                    else
                        return rawLength;

                    if(amountOfDataReceived < lengthInfoIndex + 1 + bytesRequiredToGetLength)
                        return -1;

                    return Utilities.ToInt32(Data, lengthInfoIndex + 1, bytesRequiredToGetLength);
                }
            }
        }

        private int WholeChunkLength()
        {
            int dataLength = ChunkDataLength();
            int lengthInfoLength = dataLength < SHORT_DATA ? 0 : dataLength < short.MaxValue ? SHORT_BYTES : LONG_BYTES;

            if(dataLength == -1)
                return -1;

            return HEADER_LENGTH + lengthInfoLength + KEYS_LENGTH + dataLength;
        }

        private void ReceiveDataInternal(int dataToReceiveLength)
        {
            if(dataToReceiveLength == 0)
                return;

            lock(RecevingLock)
            {
                if(receiving)
                    return;

                receiving = true;
            }

            if(dataToReceiveLength > FreeSpace)
                dataToReceiveLength = FreeSpace;

            TargetSocket.BeginReceive(Data, amountOfDataReceived, dataToReceiveLength, SocketFlags.None, result =>
            {
                OnRecevingComplete(result, dataToReceiveLength);
            }, null);

        }

        private void OnRecevingComplete(System.IAsyncResult result, int receivedDataLength)
        {
            lock(CompletingRecieveLock) // This is not needed really
            {
                TargetSocket.EndReceive(result);
                this.amountOfDataReceived += receivedDataLength;
                receiving = false;
            }
        }

        public void ReceiveData()
        {
            ReceiveDataInternal(TargetSocket.Available);
        }

        public byte[] GetChunk()
        {
            lock(DataLock)
            {
                lock(amountOfDataReceivedLock)
                {
                    int chunkLength = WholeChunkLength();

                    if(chunkLength == -1 || chunkLength > amountOfDataReceived)
                        return null;
                     //                            throw new System.InvalidOperationException("Chunk is yet not ready!");

                    byte[] chunk = new byte[chunkLength];

                    for(int i = 0; i < chunkLength; i++)
                        chunk[i] = Data[i];

                    ArrayUtilities<byte>.ShiftArrayLeft(Data, chunkLength, amountOfDataReceived);
                    amountOfDataReceived -= chunkLength;

                    return chunk;
                }
            }
        }

    }

    #endregion
下面是WebSocket代码:

        Engine.Loader.loadEngine(function() {

        var client = new Engine.Client("ws:192.168.1.105:8080");
        var connected = false;

        client.addConnectListener(function()
        {
            connected = true;
            console.log("Connection successful!");              
        });

        client.addRecieveListener(function(data)
        {
            console.log(data);
        });

        var gl = new Engine.GameLoop(new Engine.Renderer(), new Engine.Input(), client);
        gl.start();

        var i = 0;

        gl.addEventListener("UPDATE", function()
        {
            /*The code that works*/

            if(connected)
                client.sendString("" + i++);

            /*The code that causes problems*/

            var j = 10;

            while(connected && j-- > 0)
                client.sendString("" + i++);

        });

    });
编辑:更新事件由window.requestAnimationFrame调用

编辑:我先前发布的答案也失败了。结果表明,只有在错误发生之前数据[]被填充时,它才起作用。因此,如果我增加data[]的容量,我还需要增加接收数据的间隔时间

编辑:整件事都顺利进行了。其中有不止一个问题。事实证明,数据是由Socket.BeginReceive()方法在内部执行的操作和我同时调用的DataReceiver.GetChunk()修改的。 因此,现在我首先在一个临时缓冲区中接收数据,然后在触发complete事件时将其写入主缓冲区,同时锁定它,这样就不会有其他线程弄乱它(据我所知,在调用BeginRecieve函数的线程中不会调用complete方法)

正如@vtorola所说,当触发完整事件时,所有数据可能都不在缓冲区中,因此我也考虑了这一点

下面是实际有效的脚本。它是重新编写的,但有些方法是从旧脚本复制的(是的,我很懒):

公共类数据接收器
{
private const int DEFAULT\u DIRECT\u BUFFER\u LIMIT=256;
private const int DEFAULT_DATA_LIMIT=256;//字节
私有常量int头长度=2;
私有常量int key_长度=4;
私有常量int SHORT_DATA=126;
private const int SHORT_BYTES=2;
私有常量int LONG_DATA=127;
private const int LONG_字节=8;
专用只读套接字TargetSocket;
私有只读对象DataUpdateLock=新对象();
私人楼宇接收数据;
专用字节[]DirectReceiveBuffer;
接收到私有int数据;
专用字节[]数据;
公共int容量
{
得到
{
返回数据。长度;
}
}
已收到的公共int AMOUNTOFDATA
{
得到
{
收到的返回数据;
}
}
公共空间
{
得到
{
返回容量-接收的数据量;
}
}
公共广播接收数据
{
得到
{
返回接收数据;
}
}
公共厕所准备好了吗
{
得到
{
int chunkLength=WholeChunkLength();
if(chunkLength<1)
返回false;
返回长度(自由空间)
expectedDataLength=自由空间;
如果(expectedDataLength>DirectReceiveBuffer.Length)
expectedDataLength=DirectReceiveBuffer.Length;
TargetSocket.BeginReceive(DirectReceiveBuffer,0,expectedDataLength,SocketFlags.None,结果=>
{
int receivedDataLength=TargetSocket.EndReceive(结果);
锁(数据更新锁)
{
对于(int i=0;idataReceived)
返回null;
byte[]chunk=新字节[chunkLength];
for(int i=0;i
当您调用
receivier.ReceiveData()时;
,您假定在该方法完成时已读取数据,但这是不对的。对该方法的调用只会启动读取,该读取将在调用OnReceivingComplete的匿名委托上异步结束。当应用程序的负载大于一个简单调用时,这将更为明显。这样会更容易如果使用
TcpListener
,并使用
async/await
而不是
Socket
BeginXXX
EndXXX
方法

您似乎还假设,一旦读取完成,所有数据都在缓冲区中,但它可能不在缓冲区中。例如,对于读取6个字节,您可能需要读取6次,或3次,或可能仅读取1次。您需要一个
while
循环,在完成所有预期负载之前将数据一直读取到缓冲区

请参见此处的示例:

作为旁注:

return Utilities.ToInt32(Data, lengthInfoIndex + 1, bytesRequiredToGetLength);
Int32
有4个字节,因此如果将8个字节解析为
Int32
,则可能会溢出。另外,请记住长度为
ushort
uint
ulong
(即:无符号i
        Engine.Loader.loadEngine(function() {

        var client = new Engine.Client("ws:192.168.1.105:8080");
        var connected = false;

        client.addConnectListener(function()
        {
            connected = true;
            console.log("Connection successful!");              
        });

        client.addRecieveListener(function(data)
        {
            console.log(data);
        });

        var gl = new Engine.GameLoop(new Engine.Renderer(), new Engine.Input(), client);
        gl.start();

        var i = 0;

        gl.addEventListener("UPDATE", function()
        {
            /*The code that works*/

            if(connected)
                client.sendString("" + i++);

            /*The code that causes problems*/

            var j = 10;

            while(connected && j-- > 0)
                client.sendString("" + i++);

        });

    });
public class DataReceiver
{
private const int DEFAULT_DIRECT_BUFFER_LIMIT = 256;
private const int DEFAULT_DATA_LIMIT = 256; // bytes

private const int HEADER_LENGTH = 2;
private const int KEYS_LENGTH = 4;

private const int SHORT_DATA = 126;
private const int SHORT_BYTES = 2;

private const int LONG_DATA = 127;
private const int LONG_BYTES = 8;

private readonly Socket TargetSocket;

private readonly object dataUpdatingLock = new object();

private bool receivingData;

private byte[] directRecieveBuffer;

private int dataReceived;
private byte[] data;

public int Capacity
{
    get
    {
        return data.Length;
    }
}

public int AmountOfDataReceived
{
    get
    {
        return dataReceived;
    }
}

public int FreeSpace
{
    get
    {
        return Capacity - AmountOfDataReceived;
    }
}

public bool ReceivingData
{
    get
    {
        return receivingData;
    }
}

public bool ChunkReady
{
    get
    {
        int chunkLength = WholeChunkLength();

        if(chunkLength < 1)
            return false;

        return chunkLength <= dataReceived;
    }
}

private DataReceiver(Socket socket, int bufferLength, int directBufferLength)
{
    this.TargetSocket = socket;
    this.data = new byte[bufferLength];
    this.directRecieveBuffer = new byte[directBufferLength];
}

public DataReceiver(Socket socket, int bufferLength) : this(socket, bufferLength, DEFAULT_DIRECT_BUFFER_LIMIT) { }

public DataReceiver(Socket socket) : this(socket, DEFAULT_DATA_LIMIT, DEFAULT_DIRECT_BUFFER_LIMIT) { }

private void ReceiveDataInternally()
{
    receivingData = true;

    int expectedDataLength = TargetSocket.Available;

    if(expectedDataLength > FreeSpace)
        expectedDataLength = FreeSpace;

    if(expectedDataLength > directRecieveBuffer.Length)
        expectedDataLength = directRecieveBuffer.Length;

    TargetSocket.BeginReceive(directRecieveBuffer, 0, expectedDataLength, SocketFlags.None, result =>
    {
        int receivedDataLength = TargetSocket.EndReceive(result);

        lock(dataUpdatingLock)
        {
            for(int i = 0; i < receivedDataLength; i++)
            {
                data[dataReceived++] = directRecieveBuffer[i];
                directRecieveBuffer[i] = 0;
            }
        }

        receivingData = false;

    }, null);
}

public byte[] GetChunk()
{
    int chunkLength = WholeChunkLength();

    if(chunkLength == -1 || chunkLength > dataReceived)
        return null;

    byte[] chunk = new byte[chunkLength];

    for(int i = 0; i < chunkLength; i++)
        chunk[i] = data[i];

    lock(dataUpdatingLock)
    {
        ArrayUtilities<byte>.ShiftArrayLeft(data, chunkLength, dataReceived);
        dataReceived -= chunkLength;
    }

    return chunk;
}

private int ChunkDataLength()
{
    if(dataReceived < 1)
        return -1;

    int lengthInfoIndex = HEADER_LENGTH - 1;

    if(dataReceived < HEADER_LENGTH)
        return -1;

    int rawLength = data[lengthInfoIndex] & 127;

    int bytesRequiredToGetLength = 0;

    if(rawLength == SHORT_DATA)
        bytesRequiredToGetLength = SHORT_BYTES;
    else if(rawLength == LONG_DATA)
        bytesRequiredToGetLength = LONG_BYTES;
    else
        return rawLength;

    if(dataReceived < HEADER_LENGTH + bytesRequiredToGetLength)
        return -1;

    return Utilities.ToInt32(data, lengthInfoIndex + 1, bytesRequiredToGetLength);
}

private int WholeChunkLength()
{
    int dataLength = ChunkDataLength();
    int lengthInfoLength = dataLength < SHORT_DATA ? 0 : dataLength < short.MaxValue ? SHORT_BYTES : LONG_BYTES;

    if(dataLength == -1)
        return -1;

    return HEADER_LENGTH + lengthInfoLength + KEYS_LENGTH + dataLength;
}

public void StartReceving()
{
    if(!receivingData)
        ReceiveDataInternally();
}
}
  public void ContinuousReceive(){
    byte[] buffer = new byte[1024];
    bool terminationCodeReceived = false;
    while(!terminationCodeReceived){
      try{
          if(server.Receive(buffer)>0){
             // We got something
             // Parse the received data and check if the termination code
             // is received or not
          }
      }catch (SocketException e){
          Console.WriteLine("Oops! Something bad happened:" + e.Message);
      }
    }
  }
return Utilities.ToInt32(Data, lengthInfoIndex + 1, bytesRequiredToGetLength);