运维开发网

如何解决.NET套接字和TCP可能丢失的数据包问题?

运维开发网 https://www.qedev.com 2020-06-15 12:51 出处:网络 作者:运维开发网整理
我需要一些帮助,找出如何解决我正在使用.NET套接字通过TCP进行高容量数据馈送的问题. 简而言之,当客户端应用程序启动时,它将连接到服务器上的特定端口.连接后,服务器开始向客户端发送实时数据,以类似自动收报机的方式显示信息.服务器支持多个客户端工作站,因此数据将通过多个端口(多个套接字)发送. 一切都已实施,并且通过慢速进给和低音量工作.我正在对系统进行压力测试,以确保其可靠性和可扩展性.当我增
我需要一些帮助,找出如何解决我正在使用.NET套接字通过TCP进行高容量数据馈送的问题.

简而言之,当客户端应用程序启动时,它将连接到服务器上的特定端口.连接后,服务器开始向客户端发送实时数据,以类似自动收报机的方式显示信息.服务器支持多个客户端工作站,因此数据将通过多个端口(多个套接字)发送.

一切都已实施,并且通过慢速进给和低音量工作.我正在对系统进行压力测试,以确保其可靠性和可扩展性.当我增加频率时,服务器运行完美.但是,我看到客户端上看起来丢失的数据包.这发生在随机时间.

当前,广播的每个消息的前面都有一个4字节的值,用于标识该消息的长度.当我们在客户端接收数据时,我们将数据附加到缓冲区(Stream),直到我们收到该字节数.任何其他字节都被视为下一条消息的开头.同样,这很有效,直到我调高频率.

在我的测试中,我发送一个大约225字节的数据包,然后是大约310kB的一个,另一个大约40kb.每隔1秒发送一条消息,大约有12个客户端正在运行.将频率增加到1/2秒,我最终看到一个客户端的显示冻结.持续1/4秒,我可以在几秒钟内用少至4个客户端重现问题.

查看我的代码(我可以提供,如果需要),我看到所有客户端都在接收数据,但不知何故信息“不同步”且预期长度值很大(在1亿范围内) .因此,我们只是继续阅读数据,从不认识到消息的结束.

我要么需要更好的方法,要么确保获得我期望的数据并且不丢失数据包.你能帮我吗?

UPDATE

我做了大量额外的测试,改变了消息的大小和交付频率.肯定存在相关性.我制作消息大小越小,我可以实现的频率越高.但是,不可避免地,我总能打破它.

因此,更准确地描述我正在寻找的是:

>了解正在发生的事情.这将帮助我确定可能的解决方案,或者至少为可靠行为建立阈值.

>实现故障安全机制,以便在问题发生时,我可以处理它并可能从中恢复.也许在数据流中添加校验和或类似的东西.

这是我在客户端(接收)应用程序中运行的代码:

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是可靠的协议,因此不是由于丢包造成的.任何丢失的数据包都会导致以下两种情况之一:

>丢失的数据被重新传输,接收器会暂停一段时间,但绝不会看到丢失的数据或数据乱序.

>插座已关闭.

UPDATE

在将部分长度写入缓冲区后,IsCoComplete方法返回true.这会导致ProcessData()中的接收器代码丢弃已接收的长度缓冲区字节,然后失去同步.

0

精彩评论

暂无评论...
验证码 换一张
取 消