天天看点

Qt调用jrtplib实现单播、多播和广播

jrtplib简介可参考:http://blog.csdn.net/caoshangpa/article/details/51151942

RTP协议分析可参考:http://blog.csdn.net/caoshangpa/article/details/51149007

windows下编译jrtplib可参考:http://blog.csdn.net/caoshangpa/article/details/51152541

linux下编译jrtplib可参考:http://blog.csdn.net/caoshangpa/article/details/51416822

linux下交叉编译jrtplib可参考:http://blog.csdn.net/caoshangpa/article/details/51416997

1.单播

Qt调用jrtplib实现单播、多播和广播

单播是一种“一对一”模式,在单播通信方式下,当一端发送数据报到一个指定的主机时,首先可能会引发ARP把目的IP地址映射为MAC地址,然而ARP就是基于广播模式的实现。当以太网帧到达一个主机时,以太网接口便会把自己的MAC地址与帧中的MAC地址相比较,如果相符,以太网接口便会接收该帧,并按协议栈向上递交,如果不想符,该帧将会忽略该帧。其他的以太网接口做同样的操作,最后接收该帧的只有一个主机。在此之后,然后以太网帧向上递交到IP层目的主机会核对IP分组中的目的IP与本机IP(多宿主机),符合,根据协议类型向上递交到传输层相应的协议处理这即时单播通信的过程。典型的TCP通信就是单播模式的。

单播在网络中得到了广泛的应用,网络上绝大部分的数据都是以单播的形式传输的,只是一般网络用户不知道而已。例如,你在收发电子邮件、浏览网页时,必须与邮件服务器、Web服务器建立连接,此时使用的就是单播数据传输方式。但是通常使用“点对点通信”(Point to Point)代替“单播”,因为“单播”一般与“多播”和“广播”相对应使用。

#include "crtpthread.h"
#include <QDebug>                                                                                                                                                                                 
CRtpThread::CRtpThread():
    m_runFlag(true)
{
#ifdef RECEIVER
    uint8_t destIP[]={192,168,1,168};
#else
    uint8_t destIP[]={192,168,1,183};
#endif
    initialRTP(destIP);
}

CRtpThread::~CRtpThread()
{
    m_session.BYEDestroy(RTPTime(10,0),0,0);
    m_runFlag=false;
#ifdef WIN32
    WSACleanup();
#endif // WIN32
}

void CRtpThread::run()
{
    while(m_runFlag)
    {
        usleep(1);
        m_session.BeginDataAccess();
        if (m_session.GotoFirstSourceWithData())
        {
            do
            {
                RTPPacket *pack;
                while ((pack = m_session.GetNextPacket()) != NULL)
                {
                    int recvSize = pack->GetPayloadLength();
                    uint8_t * recvData=pack->GetPayloadData();
                    dispatch(recvData, recvSize);
                    m_session.DeletePacket(pack);
                }
            } while (m_session.GotoNextSourceWithData());
        }

        m_session.EndDataAccess();

#ifndef RTP_SUPPORT_THREAD
        int status = m_session.Poll();
        if (status < 0)
        {
            qDebug()<< RTPGetErrorString(status).c_str();
        }
#endif
    }
}

void CRtpThread::dispatch(uchar *dataBuf, int dataLen)
{
   emit signalRtpData(dataBuf,dataLen);
#ifdef RECEIVER
   //将收到的数据转发
   rtpSendData(dataBuf, dataLen);
#endif
}

void::CRtpThread::initialRTP(uint8_t *ip)
{
#ifdef WIN32
    WSADATA dat;
    WSAStartup(MAKEWORD(2,2),&dat);
#endif // WIN32
    //每秒发送10个样本
    m_sessionparams.SetOwnTimestampUnit(1.0/10.0);
    //设置本地端口
    m_transparams.SetPortbase(27000);

    int status = m_session.Create(m_sessionparams,&m_transparams);

    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
    //设置目的IP和端口
    RTPIPv4Address addr(ip,27000);
    status = m_session.AddDestination(addr);
    if (status < 0)
    {
       qDebug()<< RTPGetErrorString(status).c_str();
    }
    //这里设置了默认值,发送的时候只用指定数据和长度SendPacket((void *)dataBuf,dataLen)。
    //否则发送时需指定后三个参数,例如SendPacket((void *)dataBuf,dataLen,0,false,10)。
    m_session.SetDefaultPayloadType(0);
    m_session.SetDefaultMark(false);
    m_session.SetDefaultTimestampIncrement(10.0);
    m_session.SetMaximumPacketSize(65535);
}

void CRtpThread::rtpSendData(uchar *dataBuf, int dataLen)
{
    int status = m_session.SendPacket((void *)dataBuf,dataLen);
    if (status < 0)
    {
        qDebug() << RTPGetErrorString(status).c_str();
    }
}
           

通过注释或放开.pro中的DEFINES +=RECEIVER来控制是发送端还是接收端,接收端会把收到的数据\n转发给发送端。因为是单播,所以在构造函数中需要指定两个IP地址,然后调用initialRTP函数进行RTP的初始化。

发送端和接收端分别如下图所示:

Qt调用jrtplib实现单播、多播和广播
Qt调用jrtplib实现单播、多播和广播

2.多播

Qt调用jrtplib实现单播、多播和广播

多播是一种“一对一组”的模式,也就时加入同一个组的主机才会接收到数据,他综合了单播和广播的优点,可以只对特定的主机进行通信,其他的主机通信不受影响。多播通常被指IP多播,IP多播是一种通过使用一个多播地址将数据在同一时间以高效的方式发往处于TCP/IP网络上的多个接收者的协议。但是对于IP多播,扩容不容易。

“多播”也可以称为“组播”,在网络技术的应用并不是很多,网上视频会议、网上视频点播特别适合采用多播方式。因为如果采用单播方式,逐个节点传输,有多少个目标节点,就会有多少次传送过程,这种方式显然效率极低,是不可取的;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。IP网络的多播一般通过多播IP地址来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。Windows 2000中的DHCP管理器支持多播IP地址的自动分配。

#include "crtpthread.h"
#include <QDebug>                                                                                                                                                                                 
CRtpThread::CRtpThread():
    m_runFlag(true)
{
    uint8_t destIP[]={224,212,118,99};
    initialRTP(destIP);
}

CRtpThread::~CRtpThread()
{
    m_session.BYEDestroy(RTPTime(10,0),0,0);
    m_runFlag=false;
#ifdef WIN32
    WSACleanup();
#endif // WIN32
}

void CRtpThread::run()
{
    while(m_runFlag)
    {
        usleep(1);
        m_session.BeginDataAccess();
        if (m_session.GotoFirstSourceWithData())
        {
            do
            {
                RTPPacket *pack;
                while ((pack = m_session.GetNextPacket()) != NULL)
                {
                    int recvSize = pack->GetPayloadLength();
                    uint8_t * recvData=pack->GetPayloadData();
                    dispatch(recvData, recvSize);
                    m_session.DeletePacket(pack);
                }
            } while (m_session.GotoNextSourceWithData());
        }

        m_session.EndDataAccess();

#ifndef RTP_SUPPORT_THREAD
        int status = m_session.Poll();
        if (status < 0)
        {
            qDebug()<< RTPGetErrorString(status).c_str();
        }
#endif
    }
}

void CRtpThread::dispatch(uchar *dataBuf, int dataLen)
{
   emit signalRtpData(dataBuf,dataLen);
}

void::CRtpThread::initialRTP(uint8_t *ip)
{
#ifdef WIN32
    WSADATA dat;
    WSAStartup(MAKEWORD(2,2),&dat);
#endif // WIN32
    //每秒发送10个样本
    m_sessionparams.SetOwnTimestampUnit(1.0/10.0);
    //设置本地端口
    m_transparams.SetPortbase(27000);
    //设置网络接口IP地址
//    uint8_t interfaceIP[]={114,212,118,99};
//    m_transparams.SetMulticastInterfaceIP(*interfaceIP);
    //设置多播组数据的TTL值,范围为0~255之间的任何值
    m_transparams.SetMulticastTTL(255);

    int status = m_session.Create(m_sessionparams,&m_transparams);
    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
    //是否支持多播
    bool iscast = m_session.SupportsMulticasting();
    if(!iscast)
    {
       m_session.Destroy();
       qDebug()<<"Do not support multicast";
       return;
    }
    //多播地址
    RTPIPv4Address addr(ip,27000);
#ifdef RECEIVER
    //加入多播
    status = m_session.JoinMulticastGroup(addr);
    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
#else
    status = m_session.AddDestination(addr);
    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
#endif
    //这里设置了默认值,发送的时候只用指定数据和长度SendPacket((void *)dataBuf,dataLen)。
    //否则发送时需指定后三个参数,例如SendPacket((void *)dataBuf,dataLen,0,false,10)。
    m_session.SetDefaultPayloadType(0);
    m_session.SetDefaultMark(false);
    m_session.SetDefaultTimestampIncrement(10.0);
    m_session.SetMaximumPacketSize(65535);  
}

void CRtpThread::rtpSendData(uchar *dataBuf, int dataLen)
{
    int status = m_session.SendPacket((void *)dataBuf,dataLen);
    if (status < 0)
    {
        qDebug() << RTPGetErrorString(status).c_str();
    }
}
           

对于发送端,需要将多播地址当做目的地址加入会话;对于接收端,需要调用函数JoinMuticastGroup加入多播组,这样所有加入到多播组的接收端都可以接收到发送端发送的数据。

发送端和接收端分别如下图所示:

Qt调用jrtplib实现单播、多播和广播
Qt调用jrtplib实现单播、多播和广播

3.广播

Qt调用jrtplib实现单播、多播和广播

广播时一种“一对所有”模式,在广播模式下,该以太网帧被局域网中所有的以太网接口接收,并向上递交到传输层,如果指定的端口开启并绑定相应的应用进程时,应用进程就会处理该数据报,如果端口没有任何进程绑定,传输层就会丢弃该数据报。该主机并不会发送一个ICMP数据不可达的消息,否则会导致广播风暴。

广播风暴就是网络长时间被大量的广播数据包所占用,正常的点对点通信无法正常进行,外在表现为网络速度奇慢无比。出现广播风暴的原因有很多,一块有故障的网卡,就可能长时间向网络上发送广播包而导致广播风暴。“广播”在网络中的应用较多,如客户机通过DHCP自动获得IP地址的过程就是通过广播来实现的。

#include "crtpthread.h"
#include <QDebug>                                                                                                                                                                                 
CRtpThread::CRtpThread():
    m_runFlag(true)
{
#ifdef RECEIVER
    uint8_t destIP[]={192,168,1,168};
#else
    uint8_t destIP[]={192,168,1,255};
#endif
    initialRTP(destIP);
}

CRtpThread::~CRtpThread()
{
    m_session.BYEDestroy(RTPTime(10,0),0,0);
    m_runFlag=false;
#ifdef WIN32
    WSACleanup();
#endif // WIN32
}

void CRtpThread::run()
{
    while(m_runFlag)
    {
        usleep(1);
        m_session.BeginDataAccess();
        if (m_session.GotoFirstSourceWithData())
        {
            do
            {
                RTPPacket *pack;
                while ((pack = m_session.GetNextPacket()) != NULL)
                {
                    int recvSize = pack->GetPayloadLength();
                    uint8_t * recvData=pack->GetPayloadData();
                    dispatch(recvData, recvSize);
                    m_session.DeletePacket(pack);
                }
            } while (m_session.GotoNextSourceWithData());
        }

        m_session.EndDataAccess();

#ifndef RTP_SUPPORT_THREAD
        int status = m_session.Poll();
        if (status < 0)
        {
            qDebug()<< RTPGetErrorString(status).c_str();
        }
#endif
    }
}

void CRtpThread::dispatch(uchar *dataBuf, int dataLen)
{
   emit signalRtpData(dataBuf,dataLen);
}

void::CRtpThread::initialRTP(uint8_t *ip)
{
#ifdef WIN32
    WSADATA dat;
    WSAStartup(MAKEWORD(2,2),&dat);
#endif // WIN32
    //每秒发送10个样本
    m_sessionparams.SetOwnTimestampUnit(1.0/10.0);
    //设置本地端口
    m_transparams.SetPortbase(27000);

    int status = m_session.Create(m_sessionparams,&m_transparams);
    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
    //设置目的IP和端口
    RTPIPv4Address addr(ip,27000);
    status = m_session.AddDestination(addr);
    if (status < 0)
    {
        qDebug()<< RTPGetErrorString(status).c_str();
    }
    //这里设置了默认值,发送的时候只用指定数据和长度SendPacket((void *)dataBuf,dataLen)。
    //否则发送时需指定后三个参数,例如SendPacket((void *)dataBuf,dataLen,0,false,10)。
    m_session.SetDefaultPayloadType(0);
    m_session.SetDefaultMark(false);
    m_session.SetDefaultTimestampIncrement(10.0);
    m_session.SetMaximumPacketSize(65535);  
}

void CRtpThread::rtpSendData(uchar *dataBuf, int dataLen)
{
    int status = m_session.SendPacket((void *)dataBuf,dataLen);
    if (status < 0)
    {
        qDebug() << RTPGetErrorString(status).c_str();
    }
}
           

RTP协议一般用于单播和多播,如果开发中有广播的需求,广播与单播的唯一区别就是将发送端目标IP的最后字段设置为255,比如本例中的192.168.1.255,这样的话该网段的接收端都能收到发送端发送的数据。

发送端和接收端运行效果与多播相同。

使用jrtplib的一个具体例子: 基于RTP协议的H.264视频传输系统:实现

参考链接:http://blog.csdn.net/wye213/article/details/9896547

参考链接:http://blog.csdn.net/maopig/article/details/6863253

参考链接:https://docs.oracle.com/cd/E38902_01/html/E38880/sockets-137.html

源码链接:见http://blog.csdn.net/caoshangpa/article/details/52571183的评论

原创不易,转载请标明出处:https://blog.csdn.net/caoshangpa/article/details/52571183

继续阅读