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