Reliable Multicast Programming(PGM)协议

Reliable Multicast Programming (PGM)实际通用可靠多播协议,在某种程度上保证多播的可靠性。是IP上层协议,和TCP还有UDP同级,工作在传输层。

在组播传输视频项目中,发现在网络较差的时候,组播传输视频性能下降迅猛,组播的视频几乎到了无法直视的地步,已经不是马赛克什么的问题了,简直就是一张臭抹布。

但是上面的要求是让接收端达到1080p 16fps的播放效果,此时组播接收端的实时网络速率只有50KB/s左右,这种情况下要从软件上处理的话(因为路由器不好换),需要让组播的丢包率降低才行,但是使用iperf测了下当时网络的丢包率,能丢到80%,丢到他姥姥家?

但是此时带宽利用率却很低,赶紧换成udp单播试了一下,速度能上去,也不怎么花屏了,不清楚是不是确认机制的问题。
但是总不能说把组播换成单播,当接入的接收端变多的时候,不清楚单播效果会不会也变差。

这个时候发现了PGM,“可靠”多播协议,有不少基于PGM实现的库,打算先用windows上的写个demo出来。

想要使用PGM需要先在网络适配器上安装协议,安装完成后会在属性中出现可靠多播协议
20190808160252.png

然后就是开发了,官网文档提供的demo很棒,copy下来几乎就能跑起来。但是除了官网文档,相关资料就比较少了,头文件我还找了半天,环境上坑不少,记录一下

wsrm.h头文件

首先是windows sdk,我试了一下如果是8.1的sdk的话,是找不到wsrm.h头文件的,我有装10.0.17134.0,8.1还有10.0.15063.0三个版本的windows sdk,用everything找了一下这个头文件,得到了下面图示结果
20190808161825.png

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

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 heresalocal.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;
}

转载于:https://www.cnblogs.com/lenomirei/p/11324394.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1488 肥猫的游戏

P1488 肥猫的游戏https://www.luogu.org/problem/P1488 博弈论一个三角形&#xff0c;只有三种情况&#xff1a;1邻接&#xff0c;2邻接&#xff0c;3邻接主要是2邻接的情况比较麻烦边<0,n-1>需要特判一下 #include <iostream> #include <cstdio> #include &…

使用OAuth2令牌的安全REST服务

1.简介 在本教程中&#xff0c;我们将介绍如何将Spring Security与OAuth结合使用以保护REST服务。 在演示应用程序中&#xff0c;可以使用路径模式&#xff08; / api / ** &#xff09;访问服务器上受保护的REST资源&#xff0c;以便基于该路径的请求URL映射到不同的控制器方法…

java 内联调用深度_Java中内联虚拟方法调用的性能

java 内联调用深度总览 动态编译的好处之一是它能够支持在虚拟方法代码上的广泛方法内联。 内联代码可提高性能时&#xff0c;代码仍必须检查类型&#xff08;以防由于优化而更改了类型&#xff09;或在多个可能的实现之间进行选择。 这导致了问题。 通过接口调用的方法的多个…

组件切换方式(Vue.js)

这里&#xff0c;我用一个注册登录两组件的切换实例来演示&#xff1a; 切换方式一 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><title>独秀不爱秀</title> </head><body><div …

在GWT中序列化/反序列化Json

JSON和GWT 最近&#xff0c; GWT用户小组中进行了有趣的讨论 &#xff0c;涉及在客户端对JSON进行序列化/反序列化的最佳实践。 这篇文章旨在突出其重点。 到目前为止&#xff0c;在GWT中有三种将对象转换为JSON并从客户端转换回JSON的方法&#xff1a; gwt-jackson框架&#…

NOIP模拟测试15「建造城市city(插板法)·轰炸·石头剪刀布」

建造城市 题解 先思考一个简单问题 10个$toot$ 放进5间房屋&#xff0c;每个房屋至少有1个$toot$&#xff0c;方案数 思考&#xff1a;插板法&#xff0c;$10$个$toot$有$9$个缝隙&#xff0c;$5$间房屋转化为$4$个挡板&#xff0c;放在toot缝隙之间得到$C_{9}^{4}$ 10个$toot$…

弹簧和线程:TaskExecutor

在Web应用程序中使用线程并不常见&#xff0c;尤其是当您必须开发长期运行的任务时。 考虑到spring&#xff0c;我们必须格外注意并使用它已经提供的工具&#xff0c;而不是生成我们自己的线程。 我们希望线程由spring管理&#xff0c;因此能够在不产生任何影响的情况下使用应…

数位dp从会打模板到不会打模板

打了几个数位$dp$&#xff0c;发现自己除了会打模板之外没有任何长进&#xff0c;遇到非模板题依然什么都不会 那么接下来这篇文章将介绍如何打模板&#xff08;滑稽&#xff09; 假设我们要处理$l----r$ 采用记忆化搜索的方式&#xff0c;枚举$<r$每一种情况&#xff0c;枚…

javafx简单吗_JavaFX即将推出您附近的Android或iOS设备吗?

javafx简单吗已经有大新闻最近在世界上的JavaFX的关于JavaFX的是许多更多的组件开源&#xff0c;开源的广告在2012 JavaOne大会 。 在2月的开放源代码更新中 &#xff0c; Richard Bair编写了一份JavaFX项目表&#xff0c;该表在撰写本文时&#xff08;2013年2月11日&#xff0…

【webrtc】webrtc的rtp重传代码分析

pgm不太能用&#xff0c;没有想象中的可靠&#xff0c;重传机制貌似仍然使用组播重传&#xff0c;丢包率80%的网络感觉没啥改进&#xff0c;如果有所好转延迟估计也是个不小的问题。 后听说rtp也有nack机制&#xff0c;webrtc基于rtp实现了重传在一定程度上保证可靠性。 在各路…

了解java.nio.file.Path – 1

介绍 Java的最后几个发行版本&#xff0c;即Java 7&#xff0c;Java 8和即将到来的Java 9&#xff0c;具有许多功能&#xff0c;这些功能使Java开发人员的生活更加轻松。 &#xff08;我知道Java 9会使它变得更困难&#xff0c;但是只有在您采用新的范例时才可以。之后&#xf…

socket-01

对于所有的Web应用&#xff0c;本质上其实就是一个socket服务端&#xff0c;用户的浏览器其实就是一个socket客户端 转载于:https://www.cnblogs.com/yanhuaqiang/p/11329925.html

ActionScript3学习笔记2-包

在 ActionScript 3.0 中&#xff0c;包是用命名空间实现的&#xff0c;但包和命名空间并不同义。在声明包时&#xff0c; 可以隐式创建一个特殊类型的命名空间并保证它在编译时是已知的。显式创建的命名空间在 编译时不必是已知的。 下面的示例使用 package 指令来创建一个包含…

grep v grep_使用grep4j轻松测试分布式组件上的SLA

grep v grep因此&#xff0c;您的分布式体系结构如下图所示&#xff0c;您刚刚从企业那里收到了一项要求&#xff0c;以确保生产者发送并随后传输到下游系统&#xff08;消费者&#xff09;的消息的SLA必须快且永远不会慢于此。 400毫秒。 要求说&#xff1a; 从生产者发送到…

得到指定进程所有窗口。显示 影藏 置顶。

这里使用一个外挂程序测试&#xff0c;因为外挂程序没有做功能限制的处理 只是做了 窗口影藏。 全局变量 HWND hwnd[100]{0};int number0;DWORD Tpid0; 局部变量 char username[1028]; 先找到进程ID 1 HWND SelectPor() 2 { 3 bool isYesfalse; 4 string porcessName&q…

jquery部分方法

offset([coordinates]) 概述&#xff1a;获取匹配元素在当前视口的相对偏移。返回的对象包含两个整型属性&#xff1a;top 和 left。此方法只对可见元素有效。 比如&#xff0c;获取第二段的偏移&#xff1a; HTML 代码: <p>Hello</p><p>2nd Paragraph</p…

JUnit 5扩展模型的生命周期

JUnit5最终版本即将来临 &#xff08;当前是M4&#xff09;&#xff0c;我已经开始研究如何编写扩展。 在JUnit5中 &#xff0c;您没有使用Runners &#xff0c; Rules &#xff0c; ClassRules等&#xff0c;而是只有一个Extension API来实现自己的扩展。 JUnit5提供了多个接…

让IE6、IE7、IE8支持CSS3

我们都知道IE6&#xff0c;7并不支持CSS3的属性&#xff0c;IE8也不能很好的支持CSS3。但是有一个小脚本能够做到&#xff0c;它可以让IE支持 CSS3&#xff0c;包括&#xff1a;border-radius (rounded), box-shadow ( shadow), text-shadow等…… 如果你需要一个支持CSS3 的bo…

NOIP模拟测试16「Drink·blue·weed」

话说这次考试 Drink 非常棒的一道卡常练习题&#xff0c;适合练习卡常 真的很棒 前置卡常知识 1.char要比int快 char是最快的 输出putchar&#xff0c;输入getchar 在这个题快了7000豪 2.read 快读非常棒&#xff0c;让你变得更快&#xff0c;fread更棒&#xff0c;fread会爆炸…

Spring Boot Web Slice测试–示例

春天开机推出 测试切片而回&#xff0c;它已经采取了一些时间来解决它我的头&#xff0c;并探讨一些细微的差别。 背景 使用此功能的主要原因是减少样板。 考虑一个看起来像这样的控制器&#xff0c;仅适用于使用Kotlin编写的各种控制器。 RestController RequestMapping(&qu…