Reliable Multicast Programming(PGM)协议
Reliable Multicast Programming (PGM)实际通用可靠多播协议,在某种程度上保证多播的可靠性。是IP上层协议,和TCP还有UDP同级,工作在传输层。
在组播传输视频项目中,发现在网络较差的时候,组播传输视频性能下降迅猛,组播的视频几乎到了无法直视的地步,已经不是马赛克什么的问题了,简直就是一张臭抹布。
但是上面的要求是让接收端达到1080p 16fps的播放效果,此时组播接收端的实时网络速率只有50KB/s左右,这种情况下要从软件上处理的话(因为路由器不好换),需要让组播的丢包率降低才行,但是使用iperf测了下当时网络的丢包率,能丢到80%,丢到他姥姥家?
但是此时带宽利用率却很低,赶紧换成udp单播试了一下,速度能上去,也不怎么花屏了,不清楚是不是确认机制的问题。
但是总不能说把组播换成单播,当接入的接收端变多的时候,不清楚单播效果会不会也变差。
这个时候发现了PGM,“可靠”多播协议,有不少基于PGM实现的库,打算先用windows上的写个demo出来。
想要使用PGM需要先在网络适配器上安装协议,安装完成后会在属性中出现可靠多播协议

然后就是开发了,官网文档提供的demo很棒,copy下来几乎就能跑起来。但是除了官网文档,相关资料就比较少了,头文件我还找了半天,环境上坑不少,记录一下
wsrm.h头文件
首先是windows sdk,我试了一下如果是8.1的sdk的话,是找不到wsrm.h头文件的,我有装10.0.17134.0,8.1还有10.0.15063.0三个版本的windows sdk,用everything找了一下这个头文件,得到了下面图示结果

8.1应该是没有,剩下两个版本均可以使用,更新的版本应该也行。

vs2017以上的话在visual studio installer里面修改多装个sdk就行了
传输速度
PGM本身也有发送窗口的概念,如果使用默认设置,窗口小,发送速度非常慢,每秒最多只有70KB左右,这时候需要设置socket选项
RateKbitsPerSec 的单位是kilobits/s,是一个上限
RM_SEND_WINDOW send_window;
send_window.WindowSizeInBytes = 8000 * 1000;
send_window.WindowSizeInMSecs = 1;
send_window.RateKbitsPerSec = (send_window.WindowSizeInBytes/send_window.WindowSizeInMSecs)*8;
int rc = setsockopt(s, IPPROTO_RM, RM_RATE_WINDOW_SIZE, (char *)&send_window, sizeof(send_window));
if (rc == SOCKET_ERROR)
{
cout << "setsockopt(): RM_RATE_WINDOW_SIZE failed with error code " << WSAGetLastError() << endl;
}
RM_SEND_WINDOW结构体就这么三个成员,第一个是每秒速度了,第二个是发送窗口的大小,第三个是窗口大小毫秒,其中windows会强制让
RateKbitsPerSec/8 = WindowSizeInBytes * WindowSizeInMSecs
PS:WindowSzieInMSecs的值需要调整,当WindowSizeInBytes=8000并且WindowSzieInMSecs=1时,发送端较大概率会阻塞,原因未知,可能是发包速度过快导致
真的可靠么?
在文章一开始的时候可靠被加上了双引号,为的是表明这个协议并不是想象中的那么可靠。
发送窗口大小有限,如果需要恢复重传的数据在发送窗口之外了,那数据就是不可恢复的,一般当发送端速率过快接收端接收速度明显跟不上时,就会出现不可恢复现象。一旦出现不可恢复数据时,windows就会让接收端的连接重置,此时就不能继续接收。
源码
因为找不到对应的头文件让我着实头疼了很久,相关文档少,还不告诉我头文件是什么,这太不爽了,就好比让你看着门后面有啥,就是不给你钥匙。
pgm分为server端和client端,功能是发送文件,根据msdn的文档编写的
下面是server端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include<winsock2.h>
#include<WS2tcpip.h> //ip_mreqͷ
#include <wsrm.h>
#include <stdio.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main() {
WSADATA WSAData;
WORD sockVersion = MAKEWORD(2, 2);
if (WSAStartup(sockVersion, &WSAData) != 0)
return 0;
FILE *fp;
fopen_s(&fp, "test.webm", "rb+");
SOCKET s;
SOCKADDR_IN salocal, sasession;
int dwSessionPort;
s = socket(AF_INET, SOCK_RDM, IPPROTO_RM);
salocal.sin_family = AF_INET;
salocal.sin_port = htons(0); // Port is ignored here
salocal.sin_addr.s_addr = htonl(INADDR_ANY);
bind(s, (SOCKADDR *)&salocal, sizeof(salocal));
//
// Set all relevant sender socket options here
//
//
// Now, connect <entity type="hellip"/>
// Setting the connection port (dwSessionPort) has relevance, and
// can be used to multiplex multiple sessions to the same
// multicast group address over different ports
//
dwSessionPort = 1234;
sasession.sin_family = AF_INET;
sasession.sin_port = htons(dwSessionPort);
sasession.sin_addr.s_addr = inet_addr("224.4.5.6");
RM_SEND_WINDOW send_window;
send_window.WindowSizeInBytes = 8000;
send_window.WindowSizeInMSecs = 1;
send_window.RateKbitsPerSec = (send_window.WindowSizeInBytes/send_window.WindowSizeInMSecs)*8;
int rc = setsockopt(s, IPPROTO_RM, RM_RATE_WINDOW_SIZE, (char *)&send_window, sizeof(send_window));
if (rc == SOCKET_ERROR)
{
cout << "setsockopt(): RM_RATE_WINDOW_SIZE failed with error code " << WSAGetLastError() << endl;
}
connect(s, (SOCKADDR *)&sasession, sizeof(sasession));
//
// We're now ready to send data!
//
char pSendBuffer[1400];
sockaddr_in serverAddr;
int iAddrlen = sizeof(serverAddr);
while (1) {
if (feof(fp))
break;
memset(pSendBuffer, 0, 1400);
int data_size = fread(pSendBuffer, 1, 1400, fp);
LONG error;
error = sendto(s, pSendBuffer, data_size, 0, (sockaddr*)&serverAddr,iAddrlen);
if (error == SOCKET_ERROR)
{
fprintf(stderr, "send() failed: Error = %d\n",
WSAGetLastError());
}
}
WSACleanup();
return 0;
}
下面是client端代码
#include <iostream>
#include<winsock2.h>
#include<WS2tcpip.h> //ip_mreqͷ
#include <wsrm.h>
#include <stdio.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main() {
WSADATA WSAData;
WORD sockVersion = MAKEWORD(2, 2);
if (WSAStartup(sockVersion, &WSAData) != 0)
return 0;
SOCKET s,
sclient;
SOCKADDR_IN salocal,
sasession;
int sasessionsz, dwSessionPort;
FILE * fp;
fopen_s(&fp, "aaatest.webm", "wb+");
s = socket(AF_INET, SOCK_RDM, IPPROTO_RM);
//
// The bind port (dwSessionPort) specified should match that
// which the sender specified in the connect call
//
dwSessionPort = 1234;
salocal.sin_family = AF_INET;
salocal.sin_port = htons(dwSessionPort);
salocal.sin_addr.s_addr = inet_addr("224.4.5.6");
int receive_buf_size = 65536 * 10;
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&receive_buf_size, sizeof(receive_buf_size)) < 0)
{
std::cout << "setsockopt():SO_RCVBUF failed with error code" << WSAGetLastError() << std::endl;
}
bind(s, (SOCKADDR *)&salocal, sizeof(salocal));
//
// Set all relevant receiver socket options here
//
listen(s, 10);
sasessionsz = sizeof(sasession);
sclient = accept(s, (SOCKADDR *)&sasession, &sasessionsz);
if (setsockopt(sclient, SOL_SOCKET, SO_RCVBUF, (char*)&receive_buf_size, sizeof(receive_buf_size)) < 0)
{
std::cout << "setsockopt():SO_RCVBUF failed with error code" << WSAGetLastError() << std::endl;
}
//
// accept will return the client socket and we are now ready
// to receive data on the new socket!
//
LONG BytesRead;
char pTestBuffer[1400];
sockaddr_in clientAddr;
int iAddrlen = sizeof(clientAddr);
while (1)
{
memset(pTestBuffer, 0, 1400);
cout << "start" << endl;
BytesRead = recvfrom(sclient, pTestBuffer, 1400, 0, (sockaddr*)&clientAddr, &iAddrlen);
cout << "end" << endl;
if (BytesRead == 0)
{
fprintf(stdout, "Session was terminated\n");
}
else if (BytesRead == -1)
{
std::cout << "no data?!" << std::endl;
}
if (BytesRead > 0)
{
fwrite(pTestBuffer, 1, BytesRead, fp);
std::cout << BytesRead << std::endl;
}
}
fclose(fp);
WSACleanup();
return 0;
}
Reliable Multicast Programming(PGM)协议的更多相关文章
- RSVP协议的基本概念介绍
2010-06-12 14:12 佚名 互联网 字号:T | T 对于RSVP协议的简单介绍和图解.通过文章,我们将对这个含义的基本概念和结构,以及工作方式等方面的知识有所了解.希望对大家有所帮助. ...
- 云计算(9)--Gossip:multicast problem
Gossip/Epidemic ptotocol 解决的问题是multicast problem Gossip 协议是电脑之间的通信协议,受启发与现实社会的流言蜚语.现代分布式系统通常用gossip协 ...
- JMS学习十(ActiveMQ支持的传输协议)
ActiveMQ提供了一种连接机制,这种连接机制使用传输连接器(TransportConnector)实现客户端与代理(client - to - broker)之间的通信. 网络连接器(networ ...
- 111个知名Java项目集锦,包括url和描述
转:http://www.cnblogs.com/wangs/p/3282183.html 项目名称 项目描述 ASM Java bytecode manipulation framework A ...
- 2015-07学习总结——网络编程(TCP/IP)
之前学习的主要内容是单机上的处理,比如编程语言.游戏编程.数据库.多媒体编解码.其实对网络也有些接触,比如WWW.HTTP.UDP/TCP.RTP.RTMP.SNMP.FTP.单播组播.Telnet. ...
- 分布式系统一致性问题和Raft一致性算法
一致性问题 一致性算法是用来解决一致性问题的,那么什么是一致性问题呢? 在分布式系统中,一致性问题(consensus problem)是指对于一组服务器,给定一组操作,我们需要一个协议使得最后它们的 ...
- [转载] 一致性问题和Raft一致性算法
原文: http://daizuozhuo.github.io/consensus-algorithm/ raft 协议确实比 paxos 协议好懂太多了. 一致性问题 一致性算法是用来解决一致性问题 ...
- IP组播技术介绍及实现例子
引 言 近年来,随着Internet的迅速普及和爆炸性发展,在Internet上产生了许多新的应用,其中不少是高带宽的多媒体应用,譬如网 络视频会议.网络音频/视频广播.AOD/VOD.股市行情发布. ...
- ZeroMQ 教程 002 : 高级技巧
本文主要译自 zguide - chapter two. 但并不是照本翻译. 上一章我们简单的介绍了一个ZMQ, 并给出了三个套路的例子: 请求-回应, 订阅-发布, 流水线(分治). 这一章, 我们 ...
随机推荐
- 解决IIS Web部署 svg/woff/woff2字体找不到问题(vue部署后找不到)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/smartsmile2012/articl ...
- js执行上下文栈和变量对象
JavaScript执行上下文栈和变量对象 JS是单线程的语言,执行顺序肯定是顺序执行,但是JS 引擎并不是一行一行地分析和执行程序,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段. 例子一 ...
- 8. Android加载流程(打包与启动)
移动安全的学习离不开对Android加载流程的分析,包括Android虚拟机,Android打包,启动流程等... 这篇文章 就对Android的一些基本加载进行学习. Android虚拟机 And ...
- Python之路(第四十五篇)线程Event事件、 条件Condition、定时器Timer、线程queue
一.事件Event Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么 ...
- 如果再聘请一位会css的美工,那要你还有什么用?
老板正为网站不好看而烦恼. 会议结束后. 我对我的前辈说:要是能找个会css美工就好了! 前辈说:如果再聘请一位会css的美工,那要你还有什么用? 我一时语塞. 后来回答:我可以专心后端代码,可以写 ...
- python之pip安装软件包常用命令
# pip版本号查询 pip -V # 安装软件包.格式:pip install 软件包名 pip install pygame # 安装指定版本号的软件包.格式:pip install 软件包==软 ...
- JS数据打印的几种方式
/** * 在页面输出内容! */ document.write("This's Great!"); /** * 控制台打印: */ console.log("This ...
- 【Spring Boot】Spring Boot之使用AOP实现数据库多数据源自动切换
一.添加maven坐标 <!-- aop --> <dependency> <groupId>org.springframework.boot</groupI ...
- 海康SDK JAVA版本调用步骤及问题介绍
一.前言 本文为海康SDK JAVA版本Demo的介绍,采用Eclipse运行,以及一些问题记录. 海康SDK版本:SDK_Win32 Eclipse版本:Mars2.0 JDK版本:1.8.0_15 ...
- 12-C#笔记-可空类型
区别于C++,C#定义的NULL和0不同.更严格. C#的基本类型,区分为可空和不可空 主要涉及两个符号 单问号? 双问号?? 示例如下: using System; namespace Calcul ...