Udp 用.NET缓冲和处理实时多播数据

Udp 用.NET缓冲和处理实时多播数据,udp,buffer,real-time,multicast,mpeg2-ts,Udp,Buffer,Real Time,Multicast,Mpeg2 Ts,我试图解析通过多播接收的MPEG2-TS数据。问题是接收方法有时会跳过数据包,我相信这在很大程度上取决于连续的Receive()方法之间的额外处理[根据我做的研究,如果CPU不在Receive()上]方法时,数据包将丢失,因此缓冲将是不立即处理的最快选择,并将此操作留给另一个线程完成……我说得对吗?] 我目前正在使用一个队列来保存接收到的数据报,以便以后使用dequeue方法从另一个线程进行处理。我还转而使用在新线程上初始化的阻塞多播接收器,而不是异步接收器,以确保从一个线程向另一个线程委派时不

我试图解析通过多播接收的MPEG2-TS数据。问题是接收方法有时会跳过数据包,我相信这在很大程度上取决于连续的Receive()方法之间的额外处理[根据我做的研究,如果CPU不在Receive()上]方法时,数据包将丢失,因此缓冲将是不立即处理的最快选择,并将此操作留给另一个线程完成……我说得对吗?]

我目前正在使用一个队列来保存接收到的数据报,以便以后使用dequeue方法从另一个线程进行处理。我还转而使用在新线程上初始化的阻塞多播接收器,而不是异步接收器,以确保从一个线程向另一个线程委派时不会出现延迟[例如,使用OnReceiveFrom()方法时]

多播接收器的代码如下所示:

class Multicast
{
    /// <summary>
    /// The possible Operating Modes allowed for this Multicast Class File
    /// </summary>
    public enum OperationMode
    {
        Client,
        Server
    };

    private IPAddress ipAddress;
    private int portNumber;
    private int interfaceIndex;
    private Socket socket;
    private EndPoint sourceOrDestination;
    private byte[] Data;
    private OperationMode operationMode;

    public Queue<byte[]> q = new Queue<byte[]>();

    public Multicast(string ipAddress, int portNumber, int interfaceIndex, OperationMode operationMode)
    {
        if (!IPAddress.TryParse(ipAddress, out this.ipAddress))
        {
            throw new Exception("Incorrect Argument Data. Unable to parse IP Address!");
        }
        this.portNumber = portNumber;
        this.interfaceIndex = interfaceIndex;
        this.operationMode = operationMode;
    }

    public void Start()
    {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, portNumber); // Local IP and Port (if more than one NIC interface is available, this command must be altered!

        //socket.SetSocketOption(SocketOptionLevel.Udp,SocketOptionName.Broadcast,true);
        socket.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.NoDelay, 1);
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); // Allow for loopback testing 
        socket.Bind(localEndPoint); // Extremly important to bind the Socket before joining multicast groups 
        socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, Properties.Settings.Default.Network_MulticastTTL); // Set TTL (e.g.: 1 for one router hop)
        try
        {
            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, interfaceIndex)); // Join Multicast
        }
        catch (Exception) { }
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, Properties.Settings.Default.Network_FramePayloadSize);
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, Properties.Settings.Default.Network_FramePayloadSize);
        //socket.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.DontFragment, 1);
        socket.DontFragment = true;



        while (true)
        {
            Data = new byte[socket.ReceiveBufferSize];
            socket.Receive(Data);

            q.Enqueue(Data);
        }
    }

    /// <summary>
    /// This function is used to stop the socket from sending/receiving further data. The socket is therefore closed.
    /// </summary>
    public void Stop()
    {
        socket.Close();
    }
}
类多播
{
/// 
///此多播类文件允许的可能操作模式
/// 
公共枚举操作模式
{
客户
服务器
};
私人IP地址;
专用int端口号;
专用int接口索引;
专用插座;
私有端点源或目标;
专用字节[]数据;
私人运营模式运营模式;
公共队列q=新队列();
公共多播(字符串IP地址、int端口号、int接口索引、操作模式操作模式)
{
if(!IPAddress.TryParse(IPAddress,out this.IPAddress))
{
抛出新异常(“参数数据不正确。无法分析IP地址!”);
}
this.portNumber=portNumber;
this.interface索引=interface索引;
this.operationMode=operationMode;
}
公开作废开始()
{
套接字=新套接字(AddressFamily.InterNetwork、SocketType.Dgram、ProtocolType.Udp);
IPEndPoint localEndPoint=new IPEndPoint(IPAddress.Any,portNumber);//本地IP和端口(如果有多个NIC接口可用,则必须更改此命令!
//SetSocketOption(SocketOptionLevel.Udp,SocketOptionName.Broadcast,true);
socket.SetSocketOption(SocketOptionLevel.Udp,SocketOptionName.NoDelay,1);
socket.SetSocketOption(SocketOptionLevel.socket,SocketOptionName.ReuseAddress,1);//允许进行环回测试
socket.Bind(localEndPoint);//在加入多播组之前绑定套接字非常重要
socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.MulticastTimeToLive,Properties.Settings.Default.Network_MulticastTTL);//设置TTL(例如:1代表一个路由器跳)
尝试
{
socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.AddMembership,新的MulticastOption(ipAddress,interfaceIndex));//加入多播
}
捕获(异常){}
socket.SetSocketOption(SocketOptionLevel.socket、SocketOptionName.ReceiveBuffer、Properties.Settings.Default.Network\u FramePayloadSize);
socket.SetSocketOption(SocketOptionLevel.socket、SocketOptionName.SendBuffer、Properties.Settings.Default.Network\u FramePayloadSize);
//socket.SetSocketOption(SocketOptionLevel.Udp,SocketOptionName.DontFragment,1);
socket.DontFragment=true;
while(true)
{
数据=新字节[socket.ReceiveBufferSize];
套接字。接收(数据);
q、 排队(数据);
}
}
/// 
///此函数用于停止套接字发送/接收更多数据。因此,套接字关闭。
/// 
公共停车场()
{
socket.Close();
}
}
即使将接收到的所有数据报转储到一个.ts文件中,在VLC中播放时也可能会注意到像素化和跳过音频。实际上,由于wireshark按顺序显示所有数据包,NIC卡成功地接收到这些数据包,并且流可以在VLC中播放而不会出现问题(直接打开流时)


您建议如何改进结果?

正如我所想,您将套接字接收缓冲区限制为一个数据报。为的值应该更大,因此内核有足够的空间为您缓冲多个输入数据报,而您的应用程序不会立即从套接字读取数据报(假设正在处理当前输入)。将当前接收缓冲区大小值乘以100


那么你在回复评论中所说的字节为零的话就没有多大意义了。看看你的代码——你没有得到返回值(即收到的实际字节数)考虑到这一点,假设你每次都得到
ReceiveBufferSize
字节。

正如我所想,你将套接字接收缓冲区限制在一个数据报左右。给for的值应该更大,因此内核有足够的空间为你缓冲多个输入数据报,而你的应用程序不会立即读取数据报m套接字(比如处理当前输入)。将当前接收缓冲区大小值乘以100


那么你在回复评论中所说的字节为零的话就没有多大意义了。看看你的代码——你没有得到返回值(即收到的实际字节数)考虑到这一点,假设您每次都得到
ReceiveBufferSize
字节。

您对设置做了一些不必要的工作,但这并不重要。
Properties.settings.Default.Network\u FramePayloadSize
的值是多少?这可能会限制您在内核中缓冲一个数据包。它是1316字节,是7字节的确切大小在单个帧中传输数据包(如Wireshark中所示)…当我尝试放大此帧时,其他字节仅设置为0。在多个数据包位于此低层的情况下,以下扩展字节是否可能不是0