如何解决.NET套接字和TCP可能出现的数据包丢失问题?
我需要一些帮助,了解如何使用.NET套接字通过TCP进行大容量数据馈送来解决我遇到的问题 简而言之,当客户端应用程序启动时,它会连接到服务器上的特定端口。一旦连接,服务器开始向客户机发送实时数据,客户机将在类似股票行情的UI中显示信息。服务器支持多个客户端工作站,因此数据将通过多个端口(多个套接字)发送 一切都是实施和工作的速度慢,饲料和低容量伟大。我正在对系统进行压力测试,以确保灵活性和可伸缩性。当我增加频率时,服务器运行良好。然而,我看到客户端上似乎丢失了数据包。这是随机发生的 目前,广播的每条消息的开头都有一个4字节的值,用于标识该消息的长度。当我们在客户机中接收数据时,我们将数据附加到缓冲区(流)中,直到我们接收到该数量的字节。任何附加字节都将被视为下一条消息的开始。同样,在我把频率调高之前,这很有效 在我的测试中,我发送了一个大约225字节的数据包,然后是一个大约310kB的数据包和另一个大约40kb的数据包。每1秒发送一条消息不会失败,大约有12个客户端正在运行。将频率增加到1/2秒,我最终看到客户机的一个显示冻结。到1/4秒,我可以在几秒钟内用4个客户端重现问题 查看我的代码(如果需要,我可以提供),我看到所有客户机都在接收数据,但不知何故,信息“不同步”,预期长度值非常大(在1亿范围内)。结果,我们只是不断地阅读数据,永远也感觉不到信息的结尾 我需要一种更好的方法或方法来确保我得到了预期的数据,并且不会丢失数据包。你能帮忙吗 更新 我做了大量额外的测试,改变了消息的大小和传递频率。这肯定是有关联的。消息的大小越小,频率就越高。但是,不可避免地,我总是能够打破它 因此,要更准确地描述我正在寻找的是:如何解决.NET套接字和TCP可能出现的数据包丢失问题?,.net,sockets,.net,Sockets,我需要一些帮助,了解如何使用.NET套接字通过TCP进行大容量数据馈送来解决我遇到的问题 简而言之,当客户端应用程序启动时,它会连接到服务器上的特定端口。一旦连接,服务器开始向客户机发送实时数据,客户机将在类似股票行情的UI中显示信息。服务器支持多个客户端工作站,因此数据将通过多个端口(多个套接字)发送 一切都是实施和工作的速度慢,饲料和低容量伟大。我正在对系统进行压力测试,以确保灵活性和可伸缩性。当我增加频率时,服务器运行良好。然而,我看到客户端上似乎丢失了数据包。这是随机发生的 目前,广播的
public void StartListening(SocketAsyncEventArgs e)
{
e.Completed += SocketReceive;
socket.ReceiveAsync(e);
}
private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
lock (_receiveLock)
{
ProcessData(e.Buffer, e.BytesTransferred);
socket.ReceiveAsync(e);
}
}
private void ProcessData(Byte[] bytes, Int32 count)
{
if (_currentBuffer == null)
_currentBuffer = new ReceiveBuffer();
var numberOfBytesRead = _currentBuffer.Write(bytes, count);
if (_currentBuffer.IsComplete)
{
// Notify the client that a message has been received (ignore zero-length, "keep alive", messages)
if (_currentBuffer.DataLength > 0)
NotifyMessageReceived(_currentBuffer);
_currentBuffer = null;
// If there are bytes remaining from the original message, recursively process
var numberOfBytesRemaining = count - numberOfBytesRead;
if (numberOfBytesRemaining > 0)
{
var remainingBytes = new Byte[numberOfBytesRemaining];
var offset = bytes.Length - numberOfBytesRemaining;
Array.Copy(bytes, offset, remainingBytes, 0, numberOfBytesRemaining);
ProcessData(remainingBytes, numberOfBytesRemaining);
}
}
}
internal sealed class ReceiveBuffer
{
public const Int32 LengthBufferSize = sizeof(Int32);
private MemoryStream _dataBuffer = new MemoryStream();
private MemoryStream _lengthBuffer = new MemoryStream();
public Int32 DataLength { get; private set; }
public Boolean IsComplete
{
get { return (RemainingDataBytesToWrite == 0); }
}
private Int32 RemainingDataBytesToWrite
{
get
{
if (DataLength > 0)
return (DataLength - (Int32)_dataBuffer.Length);
return 0;
}
}
private Int32 RemainingLengthBytesToWrite
{
get { return (LengthBufferSize - (Int32)_lengthBuffer.Length); }
}
public Int32 Write(Byte[] bytes, Int32 count)
{
var numberOfLengthBytesToWrite = Math.Min(RemainingLengthBytesToWrite, count);
if (numberOfLengthBytesToWrite > 0)
WriteToLengthBuffer(bytes, numberOfLengthBytesToWrite);
var remainingCount = count - numberOfLengthBytesToWrite;
// If this value is > 0, then we have still have more bytes after setting the length so write them to the data buffer
var numberOfDataBytesToWrite = Math.Min(RemainingDataBytesToWrite, remainingCount);
if (numberOfDataBytesToWrite > 0)
_dataBuffer.Write(bytes, numberOfLengthBytesToWrite, numberOfDataBytesToWrite);
return numberOfLengthBytesToWrite + numberOfDataBytesToWrite;
}
private void WriteToLengthBuffer(Byte[] bytes, Int32 count)
{
_lengthBuffer.Write(bytes, 0, count);
if (RemainingLengthBytesToWrite == 0)
{
var length = BitConverter.ToInt32(_lengthBuffer.ToArray(), 0);
DataLength = length;
}
}
}
没有看到你的代码,我们只能猜测。我的猜测是:您是否在考虑读取少于完整4字节头的情况?您可能只读取其中的一个、两个或三个字节。数据量越大,这种情况发生的频率越高 由于TCP是一种可靠的协议,这不是由于数据包丢失造成的。任何丢失的数据包都会导致以下两种情况之一:
将部分长度写入缓冲区后,您的
IsComplete
方法返回true
。这会导致ProcessData()
中的接收器代码丢弃已接收到的长度缓冲区字节,然后失去同步。我不知道您是否听说过它,这似乎至少是您的问题的一部分。如果您查看了当数据进入时正在执行的调用的数量(读取:您的ProcessData方法)。它正在阻止服务器中的所有其他内容,而它拥有控制权,甚至递归地工作
这意味着您必须处理的数据越大,此方法返回的时间就越长。在此期间,您不能处理其他传入消息。因此,本地NIC的缓冲区会被填满,任何涉及路由器的路由器都会为您缓冲数据包,等等。如果你不能读得更快,数据包会被丢弃并重新发送,你的网络也会被阻塞。这被称为上述网络拥塞
在你的代码中,另一个直接跳到我身上的东西是锁。在异步工作时,究竟为什么要锁定任何东西?您必须了解SAEA对象是处理异步方法调用的线程的stateobject。这是为了消除自己穿线的痛苦。您基本上调用sockets ReceiveAsync方法并向其抛出一个SAEA对象,如果处理完成,则从中获取缓冲区和信息并再次将其返回到ReceiveAsync方法。处理在另一个与从套接字读取无关的方法中进行。这样你可以保持你的插座快速
最后一点:我不知道应用程序的用途是什么,但通常不鼓励使用TCP处理高速传入的高负载数据。这就是为什么更快的游戏网络引擎使用UDP。即使是速度较慢的引擎,通常每秒也会发送20个数据包。如果你的代码在半秒钟内中断,你应该考虑切换到UDP。我发现这是一个关于使用UDP进行实时联网的良好信息来源。正如他解释的概念,它可能对您也有用。错误可能在您对预期长度的计算中。向我们展示codezDoubtful,因为今天下午代码可以完美地工作一个多小时,有12个客户端,数据每秒输出一次。我一提高频率,它就失败了。相同的代码,相同的计算-这只是BitConverter.ToInt32(lengthData)。这段代码非常糟糕。更多