UDP发包的延时问题

首先说明下问题描述,就是当我们在做数据转发时,一般的1对N发送数据,应为发送的时候,我们不可能确定得了这个数据是否是有效的,一般是通过心跳包来检测,但是心跳包不是实时的,这个时候如果在N个地址中出现了无效地址,就会造成延时….项目中好像不是这样的,客户端掉线后,没问题,但是客户端掉线后连接上服务器,就会出现卡顿的情况,目前还在查找中….但是想把自己这3天的学习与资料查找做一个总结.不足之处还望不吝批评指正。

测试demo的思路如下,客户端 - > 服务端 - > N个接收端. 然后N个服务端中出现部分掉线,如果客户端发送Msg的时间与最后接收端的时间相差超过10ms我们就认为造成了延时,并给出解决方案.
通过线程模拟接收端,我这里是开了30个+10个会掉线的线程
接收线程,

bool g_stop = false;
Event g_event(false);
void RecvThread(void* pData)
{
    int nPort = *(int*)pData;
    DatagramSocket recvSocket;
    SocketAddress addr("172.16.11.228", nPort);
    recvSocket.bind(addr);
    recvSocket.setBlocking(false);
    Timespan span(1000);
    char szMsg[512] = { 0 };
    while (!g_stop)
    {
        try
        {
            if (recvSocket.poll(span, Poco::Net::Socket::SELECT_READ))
            {
                SocketAddress addrfrom;
                int nlen = recvSocket.receiveFrom(szMsg, 512, addrfrom);
                if (nlen > 0)
                {
                    szMsg[nlen] = 0;
                    DateTime dt;
                    printf("ThreadID:%05d,RecvTime:%02d:%02d:%03d===Msg:%s\n", Thread::currentTid(), dt.minute(), dt.second(), dt.millisecond(), szMsg);
                }
            }
        }
        catch (Poco::Exception& e)
        {
            printf("Exception:%s\n", e.displayText());
        }
    }
    recvSocket.close();
}

发送线程

void SendThread(void* pdata)
{
    DatagramSocket cSocket;
    SocketAddress addr("172.16.11.228", 33333);
    int nIndex = 0;
    cSocket.setSendTimeout(Timespan(1000));
    cSocket.setReceiveTimeout(Timespan(1000));
    try
    {
        cSocket.connect(addr);
    }
    catch (Poco::Exception& e)
    {
        printf("err:%d,%s\n", Thread::currentTid(), e.displayText().c_str());
        return;
    }
    char szMsg[512] = { 0 };
    g_event.wait();
    while (nIndex < 1000)
    {
        try
        {
            DateTime dt;
            int nlen = sprintf_s(szMsg, "Recv ThreadID:%05d Time:%02d:%02d:%03d", Thread::currentTid(),dt.minute(),dt.second(),dt.millisecond() );
            nlen = cSocket.sendTo(szMsg, 256, addr);
            SocketAddress recvaddr;
        }
        catch (Poco::Exception& e)
        {
            printf("ThreadID:%05d,SendRecvErr:%s\n", Thread::currentTid(), e.displayText().c_str());
            ++nIndex;
            continue;
        }
        Sleep(4);
        ++nIndex;
    }
    cSocket.close();
}

main函数

int _tmain(int argc, _TCHAR* argv[])
{
    g_event.reset();
    Thread sendthread;
    sendthread.start(SendThread, NULL);
    Thread arrThread[30];
    for (int i = 0; i < 30;++i )
    {
        int nport = 60000 + i;
        arrThread[i].start(RecvThread, &nport);
        Sleep(10);
    }
    g_event.set();
    getchar();
    g_stop = true;
    getchar();
    return 0;
}

上述代码实现了..发送线程间隔4ms发送一个数据包,另外有30个线程负责接收服务器回显的数据包
发送线程,记录发送时间,然后与30个线程接收的数据进行对比,上述为了简单点,使用了poco开发库
服务端
之前使用了BSD套接字,也就是poco 封装底层的实现,但是没办法解决UDP延时的问题,再windows下不能设置socket选项,必须使用WSAIOCTL来这是,就自己封装了…看代码

#include "stdafx.h"
#include "Poco/Net/Net.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Foundation.h"
#include "Poco/Timespan.h"
#include "Poco/DateTime.h"
#include "Poco/Thread.h"
#include <WinSock2.h>
#include <vector>
#include <Windows.h>
using Poco::Thread;
using std::vector;
bool g_stopThrad = false;
#pragma comment(lib,"ws2_32.lib")
#define DEFAULTPORT 33333
vector<SOCKADDR_IN> vecaddr;
#define IOC_VENDOR 0x18000000 
#define _WSAIOW(x,y) (IOC_IN|(x)|(y)) 
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) 
void WorkThread(void* pdata)
{
    int nret = -1;
    WSADATA wsadata;
    SOCKET  serversocket = INVALID_SOCKET;
    serversocket = socket(AF_INET, SOCK_DGRAM, 0);
    SOCKADDR_IN addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.S_un.S_addr = INADDR_ANY;
    nret = bind(serversocket,(SOCKADDR*)&addr, sizeof(addr));
    ULONG block = 1;
    ioctlsocket(serversocket, FIONBIO, &block);
    DWORD dwBytesReturned = 0;
    BOOL bNewBehavior = FALSE;
    DWORD dwstatus = 0;
    ***dwstatus = WSAIoctl(serversocket, SIO_UDP_CONNRESET,
        &bNewBehavior, sizeof(bNewBehavior),
        NULL, 0, &dwBytesReturned,
        NULL, NULL);***
            fd_set fdread;
    TIMEVAL tm;
    FD_ZERO(&fdread);
    FD_SET(serversocket, &fdread);
    tm.tv_sec = 0;
    tm.tv_usec = 500000;
    char szMsg[2048] = { 0 };
    while (!g_stopThrad)
    {
        nret = select(0,&fdread, NULL, NULL, &tm);
        if (nret <= 0 )
        {
            printf("err:%d\n", WSAGetLastError());
            FD_ZERO(&fdread);
            FD_SET(serversocket, &fdread);
            continue;
        }
        nret = recvfrom(serversocket, szMsg, 2048, 0, NULL, NULL);
        if (nret > 0 )
        {
            szMsg[nret] = 0;
            Poco::DateTime tm;
            printf("Time:%d:%d:%d---info:%s\n", tm.minute(), tm.second(), tm.millisecond(), szMsg);
            for each (SOCKADDR_IN addr in vecaddr)
            {
                sendto(serversocket, szMsg, nret,0, (SOCKADDR*)&addr, sizeof(SOCKADDR));
            }
        }
    }
        closesocket(serversocket);
}

服务端main函数


列表内容
====

int _tmain(int argc, _TCHAR* argv[])
{
    for (int i = 0; i < 40; ++i)
    {
        int nPort = 60000 + i;
        SOCKADDR_IN addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(nPort);
        addr.sin_addr.S_un.S_addr = inet_addr("172.16.11.228");
        vecaddr.push_back(addr);
    }
    Thread workThread;
    workThread.start(WorkThread);
    getchar();
    g_stopThrad = true;
    getchar();
    return 0;
}

这是正确的解决方案,错误的解决方案是不使用上面加粗和斜线的那一句,原理是windows下当我们向一个不存在的地址sendto数据后,会有一个icmp包的回显,造成select时间为可读,进而会造成select阻塞或者超时,也就引起了…udp接收线程与发送线程的时间相差很大的问题…具体理论链接参考博客尾部
直接上结果…
不设置Socket的情况,如下图…
延时图片
我们可以看到发送的时间与接收到的时间相差了40ms左右,这个再实时音频传输中是不被允许的,因为标准的MP3音频是26ms…更别说其他的自定义的了..所以会有卡顿的现象出现.
设置socket的情况,如下图
这里写图片描述
基本是没有延时的,同样的测试步骤,我是这样测试的,其实接收线程就相当于远端服务器,我另外在开一个差不多类似的客户端代码,但是只是接收100条Msg就退出,相当于模拟了无效地址..测试了10几次,最大的延时是2ms内,在我们允许的范围内,但是第一张图,基本上2-3次就会出现大量的延时..根据网络上的资料,udp socket一次收发耗时在2us左右,40*2us = 800us 接近1ms了..所以基本上符合预期了.当然论证不足或者有疏漏出请大家批评指针,一起进步

//参考链接
网络参考链接,还是比较中肯

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Windows操作系统上,我们有很多UDP发包工具可供选择。以下是一些常见的UDP发包工具和它们的简要介绍: 1. Packet Sender:Packet Sender是一款简单易用的UDP/TCP网络发包工具,可以在Windows平台上发送UDP包。它提供了用户友好的界面,可以轻松设置源和目标IP地址、端口号、数据包内容等参数,并实时查看发送和接收的数据包。 2. Netcat:Netcat(也称为NC)是一个多功能的网络工具,也可以用作UDP发包工具。它是一个命令行工具,在Windows平台上提供了类Unix的shell环境,可以通过命令行参数指定要发送UDP数据包的内容和目的地。 3. Hping:Hping是一个非常强大的网络工具,也可以用来发送UDP数据包。它具有灵活的命令行接口,可以通过指定不同的选项和参数来定制要发送UDP包的各个方面,如IP地址、端口、标记等。Hping还可以用于网络安全测试和调试等方面。 4. Nping:Nping是一个网络工具,专门设计用于进行网络探测和安全测试。它支持发送各种类型的网络流量,包括UDP包。通过指定参数、设置负载和其他选项,用户可以使用Nping对目标主机进行UDP网络探测、性能测试和安全审计。 总之,在Windows系统上有许多可选择的UDP发包工具,每个工具都有其自身的特点和用途。选择适合自己需求的工具,并根据自己的需求来设置参数,以便在网络测试、调试或安全评估等场景中使用UDP发包工具。 ### 回答2: UDP发包工具是一种用于测试网络传输性能的工具,在Windows系统上有许多可用的UDP发包工具。以下是几种常见的UDP发包工具: 1. Iperf:Iperf是一个开源的网络性能测试工具,可以通过UDP或TCP发送数据包来测试网络带宽、延迟和抖动等性能指标。它可以通过命令行界面或图形界面使用,非常方便实用。 2. PackETH:PackETH是一个简单而强大的网络分组发包工具,可以用于发送自定义的UDP数据包。它具有图形用户界面,可以轻松设置数据包的源地址、目标地址、端口号以及数据负载,非常适用于网络测试和研究。 3. UDP Tester:UDP Tester是一个简单但实用的UDP数据包发包工具。它可以发送UDP数据包,并显示接收到的回应时间和丢包率。UDP Tester具有直观的用户界面,可以轻松设置发送数据包的目标地址、端口号以及发送的数据负载。 4. Nping:Nping是一个流行的网络发包工具,可以用于发送各种类型的网络数据包。它支持灵活的数据包生成和定制,可以发送UDP、TCP、ICMP等协议的数据包。Nping可以通过命令行界面来使用,非常适合用于网络测试和诊断。 这些UDP发包工具在Windows系统上都有很好的兼容性,并提供了丰富的功能和选项,可以帮助用户进行各种网络性能测试和调试。无论是简单的带宽测试还是复杂的网络协议研究,这些工具都可以满足用户的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值