精华内容
下载资源
问答
  • 组播原理详解及在Android4 组播原理详解及在Android4
  • Android视频直播原理详解

    千次阅读 2018-01-13 11:42:27
    将图像采集的图片结果组合成一连续播放的动画,即构成视频中可肉眼观看的内容。图像的采集过程主要由摄像头等设备拍摄成 YUV 编码的原始数据,然后经过编码压缩成 H.264 等格式的数据分发出去。常见的视频封装格式...

    最近一段时间,视频直播可谓大火。在视频直播领域,有不同的商家提供各种的商业解决方案,包括软硬件设备,摄像机,编码器,流媒体服务器等。本文要讲解的是如何使用一系列免费工具,打造一套视频直播方案。

    视频直播流程

    视频直播的流程可以分为如下几步: 
    采集 —>处理—>编码和封装—>推流到服务器—>服务器流分发—>播放器流播放

    这里写图片描述

    1.采集

    采集是整个视频推流过程中的第一个环节,它从系统的采集设备中获取原始视频数据,将其输出到下一个环节。视频的采集涉及两方面数据的采集:音频采集和图像采集,它们分别对应两种完全不同的输入源和数据格式。

    这里写图片描述

    • 音频采集 
      音频数据既能与图像结合组合成视频数据,也能以纯音频的方式采集播放,后者在很多成熟的应用场景如在线电台和语音电台等起着非常重要的作用。音频的采集过程主要通过设备将环境中的模拟信号采集成 PCM 编码的原始数据,然后编码压缩成 MP3 等格式的数据分发出去。常见的音频压缩格式有:MP3,AAC,HE-AAC,Opus,FLAC,Vorbis (Ogg),Speex 和 AMR等。 
      音频采集和编码主要面临的挑战在于:延时敏感、卡顿敏感、噪声消除(Denoise)、回声消除(AEC)、静音检测(VAD)和各种混音算法等。

    • 图像采集 
      将图像采集的图片结果组合成一组连续播放的动画,即构成视频中可肉眼观看的内容。图像的采集过程主要由摄像头等设备拍摄成 YUV 编码的原始数据,然后经过编码压缩成 H.264 等格式的数据分发出去。常见的视频封装格式有:MP4、3GP、AVI、MKV、WMV、MPG、VOB、FLV、SWF、MOV、RMVB 和 WebM 等。 
      图像由于其直观感受最强并且体积也比较大,构成了一个视频内容的主要部分。图像采集和编码面临的主要挑战在于:设备兼容性差、延时敏感、卡顿敏感以及各种对图像的处理操作如美颜和水印等。

    视频采集的采集源主要有 摄像头采集、屏幕录制和从视频文件推流。

    2.处理

    视频或者音频完成采集之后得到原始数据,为了增强一些现场效果或者加上一些额外的效果,我们一般会在将其编码压缩前进行处理,比如打上时间戳或者公司 Logo 的水印,祛斑美颜和声音混淆等处理。在主播和观众连麦场景中,主播需要和某个或者多个观众进行对话,并将对话结果实时分享给其他所有观众,连麦的处理也有部分工作在推流端完成。

    这里写图片描述

    如上图所示,处理环节中分为音频和视频处理,音频处理中具体包含混音、降噪和声音特效等处理,视频处理中包含美颜、水印、以及各种自定义滤镜等处理。

    3.编码和封装

    (1)编码

    如果把整个流媒体比喻成一个物流系统,那么编解码就是其中配货和装货的过程,这个过程非常重要,它的速度和压缩比对物流系统的意义非常大,影响物流系统的整体速度和成本。同样,对流媒体传输来说,编码也非常重要,它的编码性能、编码速度和编码压缩比会直接影响整个流媒体传输的用户体验和传输成本。

    • 视频编码的意义 
      原始视频数据存储空间大,一个 1080P 的 7 s 视频需要 817 MB 
      原始视频数据传输占用带宽大,10 Mbps 的带宽传输上述 7 s 视频需要 11 分钟 
      而经过 H.264 编码压缩之后,视频大小只有 708 k ,10 Mbps 的带宽仅仅需要 500 ms ,可以满足实时传输的需求,所以从视频采集传感器采集来的原始视频势必要经过视频编码。

    • 基本原理 
      为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?核心思想就是去除冗余信息: 
      1)空间冗余:图像相邻像素之间有较强的相关性 
      2)时间冗余:视频序列的相邻图像之间内容相似 
      3)编码冗余:不同像素值出现的概率不同 
      4)视觉冗余:人的视觉系统对某些细节不敏感 
      5)知识冗余:规律性的结构可由先验知识和背景知识得到

    • 编码器的选择 
      视频编码器经历了数十年的发展,已经从开始的只支持帧内编码演进到现如今的 H.265 和 VP9 为代表的新一代编码器,下面是一些常见的视频编码器: 
      1)H.264/AVC 
      2)HEVC/H.265 
      3)VP8 
      4)VP9 
      5)FFmpeg 
      注:音频编码器有Mp3, AAC等。

    (2)封装 
    沿用前面的比喻,封装可以理解为采用哪种货车去运输,也就是媒体的容器。 
    所谓容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准。容器使得不同多媒体内容同步播放变得很简单,而容器的另一个作用就是为多媒体内容提供索引,也就是说如果没有容器存在的话一部影片你只能从一开始看到最后,不能拖动进度条,而且如果你不自己去手动另外载入音频就没有声音。下面是几种常见的封装格式: 
    1)AVI 格式(后缀为 .avi) 
    2)DV-AVI 格式(后缀为 .avi) 
    3)QuickTime File Format 格式(后缀为 .mov) 
    4)MPEG 格式(文件后缀可以是 .mpg .mpeg .mpe .dat .vob .asf .3gp .mp4等) 
    5)WMV 格式(后缀为.wmv .asf) 
    6)Real Video 格式(后缀为 .rm .rmvb) 
    7)Flash Video 格式(后缀为 .flv) 
    8)Matroska 格式(后缀为 .mkv) 
    9)MPEG2-TS 格式 (后缀为 .ts) 
    目前,我们在流媒体传输,尤其是直播中主要采用的就是 FLV 和 MPEG2-TS 格式,分别用于 RTMP/HTTP-FLV 和 HLS 协议。

    4.推流到服务器

    推流是直播的第一公里,直播的推流对这个直播链路影响非常大,如果推流的网络不稳定,无论我们如何做优化,观众的体验都会很糟糕。所以也是我们排查问题的第一步,如何系统地解决这类问题需要我们对相关理论有基础的认识。 
    推送协议主要有三种:

    • RTSP(Real Time Streaming Protocol):实时流传送协议,是用来控制声音或影像的多媒体串流协议, 由Real Networks和Netscape共同提出的;
    • RTMP(Real Time Messaging Protocol):实时消息传送协议,是Adobe公司为Flash播放器和服务器之间音频、视频和数据传输 开发的开放协议;
    • HLS(HTTP Live Streaming):是苹果公司(Apple Inc.)实现的基于HTTP的流媒体传输协议;

    RTMP协议基于 TCP,是一种设计用来进行实时数据通信的网络协议,主要用来在 flash/AIR 平台和支持 RTMP 协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括 Adobe Media Server/Ultrant Media Server/red5 等。 
    它有三种变种:

    • RTMP工作在TCP之上的明文协议,使用端口1935;
    • RTMPT封装在HTTP请求之中,可穿越防火墙;
    • RTMPS类似RTMPT,但使用的是HTTPS连接;

    RTMP 是目前主流的流媒体传输协议,广泛用于直播领域,可以说市面上绝大多数的直播产品都采用了这个协议。 
    RTMP协议就像一个用来装数据包的容器,这些数据可以是AMF格式的数据,也可以是FLV中的视/音频数据。一个单一的连接可以通过不同的通道传输多路网络流。这些通道中的包都是按照固定大小的包传输的。 
    这里写图片描述

    5.服务器流分发

    流媒体服务器的作用是负责直播流的发布和转播分发功能。 
    流媒体服务器有诸多选择,如商业版的Wowza。但我选择的是Nginx,它是一款优秀的免费Web服务器,后面我会详细介绍如何搭建Nginx服务器。

    6.播放器流播放

    主要是实现直播节目在终端上的展现。因为我这里使用的传输协议是RTMP, 所以只要支持 RTMP 流协议的播放器都可以使用,譬如:

    • 电脑端:VLC等
    • 手机端:Vitamio以及ijkplayer等

    一般情况下我们把上面流程的前四步称为第一部分,即视频主播端的操作。视频采集处理后推流到流媒体服务器,第一部分功能完成。第二部分就是流媒体服务器,负责把从第一部分接收到的流进行处理并分发给观众。第三部分就是观众啦,只需要拥有支持流传输协议的播放器即可。 
    这里写图片描述

    第一部分:采集推流SDK

    目前市面上集视频采集、编码、封装和推流于一体的SDK已经有很多了,例如商业版的NodeMedia,但NodeMedia SDK按包名授权,未授权包名应用使用有版权提示信息。 
    我这里使用的是别人分享在github上的一个免费SDK,下载地址

    下面我就代码分析一下直播推流的过程吧: 
    先看入口界面: 
    这里写图片描述 
    很简单,一个输入框让你填写服务器的推流地址,另外一个按钮开启推流。

    public class StartActivity extends Activity {
        public static final String RTMPURL_MESSAGE = "rtmppush.hx.com.rtmppush.rtmpurl";
        private Button _startRtmpPushButton = null;
        private EditText _rtmpUrlEditText = null;
    
        private View.OnClickListener _startRtmpPushOnClickedEvent = new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                Intent i = new Intent(StartActivity.this, MainActivity.class);
                String rtmpUrl = _rtmpUrlEditText.getText().toString();
                i.putExtra(StartActivity.RTMPURL_MESSAGE, rtmpUrl);
                StartActivity.this.startActivity(i);
            }
        };
    
        private void InitUI(){
            _rtmpUrlEditText = (EditText)findViewById(R.id.rtmpUrleditText);
            _startRtmpPushButton = (Button)findViewById(R.id.startRtmpButton);
            _rtmpUrlEditText.setText("rtmp://192.168.1.104:1935/live/12345");
            _startRtmpPushButton.setOnClickListener(_startRtmpPushOnClickedEvent);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_start);
            InitUI();
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    主要的推流过程在MainActivity里面,同样,先看界面: 
    这里写图片描述 
    布局文件:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/cameraRelative"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
    
    <SurfaceView
            android:id="@+id/surfaceViewEx"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
        <Button
            android:id="@+id/SwitchCamerabutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/surfaceViewEx"
            android:text="@string/SwitchCamera" />
    </RelativeLayout>
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    其实就是用一个SurfaceView显示摄像头拍摄画面,并提供了一个按钮切换前置和后置摄像头。从入口函数看起:

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    
            Intent intent = getIntent();
            _rtmpUrl = intent.getStringExtra(StartActivity.RTMPURL_MESSAGE);
    
            InitAll();
    
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            _wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    首先设置全屏显示,常亮,竖屏,获取服务器的推流url,再初始化所有东西。

        private void InitAll() {
            WindowManager wm = this.getWindowManager();
    
            int width = wm.getDefaultDisplay().getWidth();
            int height = wm.getDefaultDisplay().getHeight();
            int iNewWidth = (int) (height * 3.0 / 4.0);
    
            RelativeLayout rCameraLayout = (RelativeLayout) findViewById(R.id.cameraRelative);
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.MATCH_PARENT);
            int iPos = width - iNewWidth;
            layoutParams.setMargins(iPos, 0, 0, 0);
    
            _mSurfaceView = (SurfaceView) this.findViewById(R.id.surfaceViewEx);
            _mSurfaceView.getHolder().setFixedSize(HEIGHT_DEF, WIDTH_DEF);
            _mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            _mSurfaceView.getHolder().setKeepScreenOn(true);
            _mSurfaceView.getHolder().addCallback(new SurceCallBack());
            _mSurfaceView.setLayoutParams(layoutParams);
    
            InitAudioRecord();
    
            _SwitchCameraBtn = (Button) findViewById(R.id.SwitchCamerabutton);
            _SwitchCameraBtn.setOnClickListener(_switchCameraOnClickedEvent);
    
            RtmpStartMessage();//开始推流
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    首先设置屏幕比例3:4显示,给SurfaceView设置一些参数并添加回调,再初始化AudioRecord,最后执行开始推流。音频在这里初始化了,那么相机在哪里初始化呢?其实在SurfaceView的回调函数里。

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                _iDegrees = getDisplayOritation(getDispalyRotation(), 0);
                if (_mCamera != null) {
                    InitCamera(); //初始化相机
                    return;
                }
                //华为i7前后共用摄像头
                if (Camera.getNumberOfCameras() == 1) {
                    _bIsFront = false;
                    _mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                } else {
                    _mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
                }
                InitCamera();
            }
    
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    相机的初始化就在这里啦:

        public void InitCamera() {
            Camera.Parameters p = _mCamera.getParameters();
    
            Size prevewSize = p.getPreviewSize();
            showlog("Original Width:" + prevewSize.width + ", height:" + prevewSize.height);
    
            List<Size> PreviewSizeList = p.getSupportedPreviewSizes();
            List<Integer> PreviewFormats = p.getSupportedPreviewFormats();
            showlog("Listing all supported preview sizes");
            for (Camera.Size size : PreviewSizeList) {
                showlog("  w: " + size.width + ", h: " + size.height);
            }
    
            showlog("Listing all supported preview formats");
            Integer iNV21Flag = 0;
            Integer iYV12Flag = 0;
            for (Integer yuvFormat : PreviewFormats) {
                showlog("preview formats:" + yuvFormat);
                if (yuvFormat == android.graphics.ImageFormat.YV12) {
                    iYV12Flag = android.graphics.ImageFormat.YV12;
                }
                if (yuvFormat == android.graphics.ImageFormat.NV21) {
                    iNV21Flag = android.graphics.ImageFormat.NV21;
                }
            }
    
            if (iNV21Flag != 0) {
                _iCameraCodecType = iNV21Flag;
            } else if (iYV12Flag != 0) {
                _iCameraCodecType = iYV12Flag;
            }
            p.setPreviewSize(HEIGHT_DEF, WIDTH_DEF);
            p.setPreviewFormat(_iCameraCodecType);
            p.setPreviewFrameRate(FRAMERATE_DEF);
    
            showlog("_iDegrees="+_iDegrees);
            _mCamera.setDisplayOrientation(_iDegrees);
            p.setRotation(_iDegrees);
            _mCamera.setPreviewCallback(_previewCallback);
            _mCamera.setParameters(p);
            try {
                _mCamera.setPreviewDisplay(_mSurfaceView.getHolder());
            } catch (Exception e) {
                return;
            }
            _mCamera.cancelAutoFocus();//只有加上了这一句,才会自动对焦。
            _mCamera.startPreview();
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    还记得之前初始化完成之后开始推流函数吗?

        private void RtmpStartMessage() {
            Message msg = new Message();
            msg.what = ID_RTMP_PUSH_START;
            Bundle b = new Bundle();
            b.putInt("ret", 0);
            msg.setData(b);
            mHandler.sendMessage(msg);
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Handler处理:

        public Handler mHandler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                Bundle b = msg.getData();
                int ret;
                switch (msg.what) {
                    case ID_RTMP_PUSH_START: {
                        Start();
                        break;
                    }
                }
            }
        };
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    真正的推流实现原来在这里:

        private void Start() {
            if (DEBUG_ENABLE) {
                File saveDir = Environment.getExternalStorageDirectory();
                String strFilename = saveDir + "/aaa.h264";
                try {
                    if (!new File(strFilename).exists()) {
                        new File(strFilename).createNewFile();
                    }
                    _outputStream = new DataOutputStream(new FileOutputStream(strFilename));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //_rtmpSessionMgr.Start("rtmp://192.168.0.110/live/12345678");
            _rtmpSessionMgr = new RtmpSessionManager();
            _rtmpSessionMgr.Start(_rtmpUrl); //------point 1
    
            int iFormat = _iCameraCodecType;
            _swEncH264 = new SWVideoEncoder(WIDTH_DEF, HEIGHT_DEF, FRAMERATE_DEF, BITRATE_DEF);
            _swEncH264.start(iFormat); //------point 2
    
            _bStartFlag = true;
    
            _h264EncoderThread = new Thread(_h264Runnable);
            _h264EncoderThread.setPriority(Thread.MAX_PRIORITY);
            _h264EncoderThread.start(); //------point 3
    
            _AudioRecorder.startRecording();
            _AacEncoderThread = new Thread(_aacEncoderRunnable);
            _AacEncoderThread.setPriority(Thread.MAX_PRIORITY);
            _AacEncoderThread.start(); //------point 4
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    里面主要的函数有四个,我分别标出来了,现在我们逐一看一下。首先是point 1,这已经走到SDK里面了

        public int Start(String rtmpUrl){
            int iRet = 0;
    
            _rtmpUrl = rtmpUrl;
            _rtmpSession = new RtmpSession();
    
            _bStartFlag = true;
            _h264EncoderThread.setPriority(Thread.MAX_PRIORITY);
            _h264EncoderThread.start();
    
            return iRet;
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其实就是启动了一个线程,这个线程稍微有点复杂

        private Thread _h264EncoderThread = new Thread(new Runnable() {
    
            private Boolean WaitforReConnect(){
                for(int i=0; i < 500; i++){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(_h264EncoderThread.interrupted() || (!_bStartFlag)){
                        return false;
                    }
                }
                return true;
            }
            @Override
            public void run() {
                while (!_h264EncoderThread.interrupted() && (_bStartFlag)) {
                    if(_rtmpHandle == 0) {
                        _rtmpHandle = _rtmpSession.RtmpConnect(_rtmpUrl);
                        if(_rtmpHandle == 0){
                            if(!WaitforReConnect()){
                                break;
                            }
                            continue;
                        }
                    }else{
                        if(_rtmpSession.RtmpIsConnect(_rtmpHandle) == 0){
                            _rtmpHandle = _rtmpSession.RtmpConnect(_rtmpUrl);
                            if(_rtmpHandle == 0){
                                if(!WaitforReConnect()){
                                    break;
                                }
                                continue;
                            }
                        }
                    }
    
                    if((_videoDataQueue.size() == 0) && (_audioDataQueue.size()==0)){
                        try {
                            Thread.sleep(30);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                    //Log.i(TAG, "VideoQueue length="+_videoDataQueue.size()+", AudioQueue length="+_audioDataQueue.size());
                    for(int i = 0; i < 100; i++){
                        byte[] audioData = GetAndReleaseAudioQueue();
                        if(audioData == null){
                            break;
                        }
                        //Log.i(TAG, "###RtmpSendAudioData:"+audioData.length);
                        _rtmpSession.RtmpSendAudioData(_rtmpHandle, audioData, audioData.length);
                    }
    
                    byte[] videoData = GetAndReleaseVideoQueue();
                    if(videoData != null){
                        //Log.i(TAG, "$$$RtmpSendVideoData:"+videoData.length);
                        _rtmpSession.RtmpSendVideoData(_rtmpHandle, videoData, videoData.length);
                    }
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                _videoDataQueueLock.lock();
                _videoDataQueue.clear();
                _videoDataQueueLock.unlock();
                _audioDataQueueLock.lock();
                _audioDataQueue.clear();
                _audioDataQueueLock.unlock();
    
                if((_rtmpHandle != 0) && (_rtmpSession != null)){
                    _rtmpSession.RtmpDisconnect(_rtmpHandle);
                }
                _rtmpHandle  = 0;
                _rtmpSession = null;
            }
        });
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    看18行,主要就是一个while循环,每隔一段时间去_audioDataQueue和_videoDataQueue两个缓冲数组中取数据发送给服务器,发送方法_rtmpSession.RtmpSendAudioData和_rtmpSession.RtmpSendVideoData都是Native方法,通过jni调用so库文件的内容,每隔一段时间,这个时间是多少呢?看第4行,原来是5秒钟,也就是说我们的视频数据会在缓冲中存放5秒才被取出来发给服务器,所有直播会有5秒的延时,我们可以修改这块来控制直播延时。 
    上面说了我们会从_audioDataQueue和_videoDataQueue两个Buffer里面取数据,那么数据是何时放进去的呢?看上面的point 2,3,4。首先是point 2,同样走进了SDK:

        public boolean start(int iFormateType){
            int iType = OpenH264Encoder.YUV420_TYPE;
    
            if(iFormateType == android.graphics.ImageFormat.YV12){
                iType = OpenH264Encoder.YUV12_TYPE;
            }else{
                iType = OpenH264Encoder.YUV420_TYPE;
            }
            _OpenH264Encoder = new OpenH264Encoder();
            _iHandle = _OpenH264Encoder.InitEncode(_iWidth, _iHeight, _iBitRate, _iFrameRate, iType);
            if(_iHandle == 0){
                return false;
            }
    
            _iFormatType = iFormateType;
            return true;
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    其实这是初始化编码器,具体的初始化过程也在so文件,jni调用。point 3,4其实就是开启两个线程,那我们看看线程中具体实现吧。

        private Thread _h264EncoderThread = null;
        private Runnable _h264Runnable = new Runnable() {
            @Override
            public void run() {
                while (!_h264EncoderThread.interrupted() && _bStartFlag) {
                    int iSize = _YUVQueue.size();
                    if (iSize > 0) {
                        _yuvQueueLock.lock();
                        byte[] yuvData = _YUVQueue.poll();
                        if (iSize > 9) {
                            Log.i(LOG_TAG, "###YUV Queue len=" + _YUVQueue.size() + ", YUV length=" + yuvData.length);
                        }
    
                        _yuvQueueLock.unlock();
                        if (yuvData == null) {
                            continue;
                        }
    
                        if (_bIsFront) {
                            _yuvEdit = _swEncH264.YUV420pRotate270(yuvData, HEIGHT_DEF, WIDTH_DEF);
                        } else {
                            _yuvEdit = _swEncH264.YUV420pRotate90(yuvData, HEIGHT_DEF, WIDTH_DEF);
                        }
                        byte[] h264Data = _swEncH264.EncoderH264(_yuvEdit);
                        if (h264Data != null) {
                            _rtmpSessionMgr.InsertVideoData(h264Data);
                            if (DEBUG_ENABLE) {
                                try {
                                    _outputStream.write(h264Data);
                                    int iH264Len = h264Data.length;
                                    //Log.i(LOG_TAG, "Encode H264 len="+iH264Len);
                                } catch (IOException e1) {
                                    e1.printStackTrace();
                                }
                            }
                        }
                    }
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                _YUVQueue.clear();
            }
        };
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    也是一个循环线程,第9行,从_YUVQueue中取出摄像头获取的数据,然后进行视频旋转,第24行,对数据进行编码,然后执行26行,InsertVideoData:

        public void InsertVideoData(byte[] videoData){
            if(!_bStartFlag){
                return;
            }
            _videoDataQueueLock.lock();
            if(_videoDataQueue.size() > 50){
                _videoDataQueue.clear();
            }
            _videoDataQueue.offer(videoData);
            _videoDataQueueLock.unlock();
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    果然就是插入之前提到的_videoDataQueue的Buffer。这里插入的是视频数据,那么音频数据呢?在另外一个线程,内容大致相同

    private Runnable _aacEncoderRunnable = new Runnable() {
            @Override
            public void run() {
                DataOutputStream outputStream = null;
                if (DEBUG_ENABLE) {
                    File saveDir = Environment.getExternalStorageDirectory();
                    String strFilename = saveDir + "/aaa.aac";
                    try {
                        if (!new File(strFilename).exists()) {
                            new File(strFilename).createNewFile();
                        }
                        outputStream = new DataOutputStream(new FileOutputStream(strFilename));
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
    
                long lSleepTime = SAMPLE_RATE_DEF * 16 * 2 / _RecorderBuffer.length;
    
                while (!_AacEncoderThread.interrupted() && _bStartFlag) {
                    int iPCMLen = _AudioRecorder.read(_RecorderBuffer, 0, _RecorderBuffer.length); // Fill buffer
                    if ((iPCMLen != _AudioRecorder.ERROR_BAD_VALUE) && (iPCMLen != 0)) {
                        if (_fdkaacHandle != 0) {
                            byte[] aacBuffer = _fdkaacEnc.FdkAacEncode(_fdkaacHandle, _RecorderBuffer);
                            if (aacBuffer != null) {
                                long lLen = aacBuffer.length;
    
                                _rtmpSessionMgr.InsertAudioData(aacBuffer);
                                //Log.i(LOG_TAG, "fdk aac length="+lLen+" from pcm="+iPCMLen);
                                if (DEBUG_ENABLE) {
                                    try {
                                        outputStream.write(aacBuffer);
                                    } catch (IOException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    } else {
                        Log.i(LOG_TAG, "######fail to get PCM data");
                    }
                    try {
                        Thread.sleep(lSleepTime / 10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Log.i(LOG_TAG, "AAC Encoder Thread ended ......");
            }
        };
        private Thread _AacEncoderThread = null;
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    这就是通过循环将音频数据插入_audioDataQueue这个Buffer。 
    以上就是视频采集和推流的代码分析,Demo中并没有对视频进行任何处理,只是摄像头采集,编码后推流到服务器端。

    第二部分:Nginx服务器搭建

    流媒体服务器有诸多选择,如商业版的Wowza。但我选择的是免费的Nginx(nginx-rtmp-module)。Nginx本身是一个非常出色的HTTP服务器,它通过nginx的模块nginx-rtmp-module可以搭建一个功能相对比较完善的流媒体服务器。这个流媒体服务器可以支持RTMPHLS。 
    Nginx配合SDK做流媒体服务器的原理是: Nginx通过rtmp模块提供rtmp服务, SDK推送一个rtmp流到Nginx, 然后客户端通过访问Nginx来收看实时视频流。 HLS也是差不多的原理,只是最终客户端是通过HTTP协议来访问的,但是SDK推送流仍然是rtmp的。 
    下面是一款已经集成rtmp模块的windows版本的Nginx。下载后,即可直接使用 
    下载链接:https://github.com/illuspas/nginx-rtmp-win32

    1、rtmp端口配置 
    配置文件在/conf/nginx.conf 
    RTMP监听 1935 端口,启用live 和hls 两个application 
    这里写图片描述 
    所以你的流媒体服务器url可以写成:rtmp://(服务器IP地址):1935/live/xxx 或 rtmp://(服务器IP地址):1935/hls/xxx 
    例如我们上面写的 rtmp://192.168.1.104:1935/live/12345

    HTTP监听 8080 端口,

    • :8080/stat 查看stream状态
    • :8080/index.html 为一个直播播放与直播发布测试器
    • :8080/vod.html 为一个支持RTMP和HLS点播的测试器

    2、启动nginx服务 
    双击nginx.exe文件或者在dos窗口下运行nginx.exe,即可启动nginx服务: 
    这里写图片描述

    1)启动任务管理器,可以看到nginx.exe进程 
    这里写图片描述

    2)打开网页输入http://localhot:8080,出现如下画面: 
    这里写图片描述 
    显示以上界面说明启动成功。

    第三部分:直播流的播放

    主播界面: 
    这里写图片描述

    上面说过了只要支持RTMP流传输协议的播放器都可以收看到我们的直播。下面举两个例子吧: 
    (1)window端播放器VLC 
    这里写图片描述

    这里写图片描述

    (2)Android端播放器ijkplayer 
    ijkplayer的使用请参考Android ijkplayer的使用解析

        private void initPlayer() {
            player = new PlayerManager(this);
            player.setFullScreenOnly(true);
            player.setScaleType(PlayerManager.SCALETYPE_FILLPARENT);
            player.playInFullScreen(true);
            player.setPlayerStateListener(this);
            player.play("rtmp://192.168.1.104:1935/live/12345");
        }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里写图片描述

    Rtmp Push Demo下载地址

    展开全文
  • 组播协议详解

    2021-04-19 10:01:52
    组播基础(1)组播简介(2)组播的地址(3)组播的MAC地址(4)组播的MAC地址(5)反向转发路径—RPF2.IGMP(1)简介(2)原理3.IGMP版本(1)IGMPV1(2)IGMPV2 1.组播基础 (1)组播简介 组播技术是指单个发送者...

    1.组播基础

    (1)组播简介

    组播技术是指单个发送者对应多个接收者的一种网络通信。通过项多个接收方传送单信息流的方式,可以减少具有多个接收方同时收听或者查看相同资源情况下的网络通信流量。

    (2)组播的地址

    组播地址的范围:D类地址,前四个bit总是1110.(225.0.0.0-239.0.0.0)
    保留的组播地址:
    224.0.0.1 子网中的所有系统
    224.0.0.2 子网中所有的路由器
    224.0.0.4 DVMRp路由器
    224.0.0.5 所有OSPF路由器
    224.0.0.6 OSPF指定路由器
    224.0.0.9 Rip-2路由器
    224.0.0.10 Eigrp路由器
    224.0.0.13 PIm路由器
    224.0.0.0-224.0.0.255(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用。
    224.0.1.0-224.0.1.255是公用组播地址,可以用于INternet;
    224.0.2.0-238.255.255.255 为用户可用的组播地址(临时组地址),全网范围有效。
    239.0.0.0-239.255.255.255 为本地管理组播地址,仅仅在特定的本地范围内有效

    (3)组播的MAC地址

    V4组播的MAC地址供48位
    协议规定,V4组播MAC地址的高24位为0X01005E,第25位为0,低23位为IPv4组播地址的低23位,IPV4组播地址与MAC地址的映射关系如下图所示。
    在这里插入图片描述

    (4)组播的MAC地址

    一般来说,在一个组播网络中,其基本网络结构中使用的协议可以分为两个部分。组播路由器与路由器之间部分运行 PIM。组播路由器与组播接收者行 IGMP 协议。其中 PIM 又细分为,PIM-DM(协议无关组播的密集式)。PIM-SM(协议无关组播稀疏模式)两种模式。IGMP 则又有 V1/V2/V3。
    PIM-DM适用于小型网络,它的特点是:假设网络中每个子网都存在至少一个对组播感兴趣的接收站点。因此,组播数据报被扩散到(“推”)到网络中的所有店。DM使用的源树(source tree),树的根是组播心愿,分支形成了通过网络达到接收站点的分布树,有缘书以最短的路径关窗网络,因此它也被称为SPT(最短路径树),中间的转发设备采用(S,G)的方法记录表,S表示多播原地址,G表示多播组地址。
    PIM-SM使用于大型网络,它的特点是:嘉定所有机器都不需要接收组播数据包,只有明确指定需要的才转发,接收站点为接受到特定组的数据流,必须向该组对应的汇聚(RP)发送加入消息(“拉”),加入消息所经过的路径就变成了共享树(shared tree)的分支。所有的多播源先将信息发送到RP,再有RP转发到各个接收点的SP相当于树根,到达R之后的多播信息转发时和源无关,故采用(,G)的方式记录转发表,表示通配符。

    (5)反向转发路径—RPF

    一台路由器收到了一个组播组信源发送的组播流量,当路由器收到了该组 播流量会提取其 3 层报头源 IP 地址,并且在其单播路由表内查找是否拥有去往信源所在网段的单播路由条目,如果没有,则该路由器对于该组播组信源没有RPF 接口,报文直接在接收接口被丢弃。如果该路由器路由表内拥有去往信源所在网段的单播路由,则查看该路由对应的出站接口和接收该报文的接口是否为同一个接口,如果是,则转发该报文,如果不是,则丢弃该报文。在负载均衡中,出接口 IP 地址越大,该接口越有可能成为 RPF 接口。
    在这里插入图片描述

    2.IGMP

    (1)简介

    IGMP 协议的目的是实现主机和路由器之间组成员关系的建立与维护。IGMP 协议运行于主机和与主机直接相连的组播路由器之间,主机与本地路由器之间使用 IGMP 来进行组播组成员信息的交互。

    (2)原理

    IGMPv1,由 RFC1112 定义,定义了基本的组成员查询和报告过程;
    IGMPv2,由 RFC2236 定义,在 IGMPv1 的基础上添加了组成员快速离开的机制。
    IGMPv3 ,由 RFC3376 定义,增加的主要功能是,成员可以指定接收或指定不接收某些组播源的报文。
    主要作用:一方面接收端通知本地的路由器希望加入或者接收某个组播组的信息,另一方面,发送端周期性通过此协议局域网内某个已知组的成员是否处于活动状态(组内是否存在接受者)。实现组播成员关系的收集与维护。
    PS:当网段内存在多个组播路由器时,IGMP要通过选举查询者的方式维护组播表项。
    查询者的作用:周期性的发送通用组查询信息进行成员关系查询。主机发送报告消息进行相应,主机发送报告消息的时间具有随机性。在V2版本中,主机检测到同一网段其他成员发送相同相应消息后会抑制自己的响应报文。若有新设备需要加组,需要主动发送报告消息,不需要等待查询者的查询消息。当要离开组播组的时候,主机放松离开组消息,收到离开消息后,查询者发送特定的组查询消息来确定是否所有组成员都已经离开,对于作为组成员的路由器而言,其行为和普通的主机一样,响应其他路由器查询。
    上述机制的作用是为了在组播路由器中建立一张表格,记录存在活动成员的组播组,并针对相应的组播组创建相应的定时器,记录该组播组的最后一个成员(无需记录所有成员),当收到特定组播组的数据报文后,只向那些有G的成员借口上转发数据,至于在路由器中如何转发则由组播路由协议决定,此功能与IGMP无关。

    3.IGMP版本

    (1)IGMPV1

    IGMP报文封装在IP报文中,IP的协议号为2,IGMP报文均使用TTL为1进行传递,IP头中包括了IP路由检测选项。
    在这里插入图片描述
    版本号 version: 1
    类型 type:为 1 时是成员关系查询报文,为 2 时是成员关系报告报文
    未用 unused:发送时设为 0,接收时忽略 校验和 checksum:对 8 字节的 IGMP 消息补码之和进行求 16 位补码运算
    组地址 group address:对成员关系查询报文为 0;对成员关系报告报文等于所报告的组的 IP 组 播地址。
    Type 字段: 成员关系查询(0x11):路由器周期发送(125s),查看是否有组播成员,组地址为 0
    成员关系报告(0x12),主机想加入某个组播组,组地址为某个组播地址。

    Hostname#show ip igmp interface
    Interface vlan2 (Index 50331693)
     IGMP Enabled, Active, Querier (30.0.0.1) 
     Configured for version 3 //(Default version 2)
     IP router alert option in IGMP V2 msgs: EXCLUDE
     Internet address is 30.0.0.1
     IGMP query interval is 125 seconds
     IGMP querier timeout is 255 seconds
     IGMP max query response time is 10 seconds
     Last member query response interval is 1 seconds
     Last member query count is 2 
     Group Membership interval is 260 seconds
     IGMP robustness variable is 2
    

     主动加入:主机第一次加入某组播组时,可以在未被查询的情况下,主动通告一个 IGMP 成员关系报告报文, 以及时加入组播组。
     查询响应过程:
    •路由器向 224.0.0.1(网内所有主机)发查询报文;
    •接收到该报文的主机将自己加入的组播地址填入报告报文,向该组播地址多播;
    •其他加入该组播组的主机收到该组播报文后,抑制自己报告报文的发送;
    •由此,IGMP 查询者路由器只记录本机某接口加入了哪些组播组,它无须记录具体哪些主机加入了组播组。
     相应抑制机制:主机收到查询报文后,并不立刻响应,而是延迟 0 到 10 秒的一个随机时间后才响应。这样,一方面可以避免响应风暴,另一方面又可以有机会收到别的主机通告的响应报文从而抑制本报文的发送。
     默默离开:IGMPv1 没有专门的离开组播组报文。路由器在查询间隔的 3 倍时间内仍未收到响应报文时,才将该组播组删除。

    在IGMPV1中,没有提供选举查询路由器的机制,而是把这个任务留给了组播路由协议,不同的协议会使用不停的选举机制,会造成在一个子网中出现多个查询路由器,这是IGMPV1的缺点之一,由于 IGMPv1 没有查询路由器选举机制,因此 PIM-DR 与 IGMP 查询路由器的身份重叠,也就是说 IGMPv1 的查询路由器选举机制需依赖于 PIM 的 DR 选举。

    IGMPV1的缺点:
    1>没有查询路由器的选举机制,导致PIM-DR与IGMP查询路由器的身份重叠,也就是说IGMPV1的查询路由器选举机制需依赖于PIM的DR选举
    2>缺乏显式的离开方式,路由器在查询间隔的三倍时间内认为收到相应报文,才将该组播组删除。

    (2)IGMPV2

    IGMPv2中,增加了离开组的报文格式,当主机想要离开组播组是,不需要等待路由器发出查询报文,他可以直接像路由器发送成员关系报告报文,缩短离开时间延迟,明确了查询路由器的选举机制,除此之外,IGMPV2的工作原理与IGMPV1基本一致。
    在这里插入图片描述
    PS:
    普通查询时间:125s 组成员超时:260s
    组成员超时时间 = 健壮性系数(2)*普通组查询时间+最大响应时间(10s)=260s;
    特定组查询时间:1s 默认查询次数:2
    查询器超时:255s 查询器超时时间 = 健壮性系数(默认 2) * 普通组查询时间 + 最大响应时间/2 =255s;
    IGMPV2的报文格式:
    在这里插入图片描述
    0x11(成员关系查询):IGMPv2 的查询分为两种类型:
    (1) 通用查询,组地址字段置为全 0,对所有的组进行组成员查询;
    (2) 特定组查询,针对特定组进行组成员查询,组地址字段置为特定组的地址。
    0x12:IGMPv2 成员关系报告(为了向后兼容 IGMPv1)。
    0x16:IGMPv2 成员关系报告。
    0x17:离开组报告

    • IGMPV2与V1的区别*:
      (1)增加了特定组查询,特定组查询的目的是为了让路由器知道一个特定组在子网内是否还有组 成员,以便判断是否还需要转发该组的数据报文;
      (2)IGMPv2 的成员关系报告的类型代码不一样。
      当路由器接收到成员关系报告,它就会把该组加入到组播组成员列表中,并且为其设一个值为组成员生存周期(GMI)的定时器。收到该组的报告会导致对应定时器的刷新。如果定时器超时,路由器则认为没有本地组成员,它也不再需要在邻接的网络上为该组转发组播报文了。
      IGMPv2的组成员加入与IGMPv1中相同,但是IGmpv2的离开过程与V1相比不同,主机离开组的时候,需要显式的发送离开报文给路由器(高调离开),基本过程:当网络中有想离开的主机的时候,他会发送一个离开报文给子网上的所有路由器,(224.0.0.2),查询路由器收到离开报文后,会立即发送一个特定组查询到子网上,如果子网上还有该组成员,则会发送一个相应报文,若无回应,则删除组播组,停止转发该组的数据。
       主动加入:当一个主机加入了一个组播组,则应该立即发送一个或多个版本 2 的成员关系报告给组播组
      在这里插入图片描述
       查询与响应的过程:与 IGMPv1 相同,都存在抑制机制。增加了最大响应时间来指定延时值来规定所有主机的查询响应延时的上限,此外最大响应时间在 IGMP 查询器上配置,只应用在成员关系查询信息。
       主机向 224.0.0.2 发送离开组消息(报文中含有要离开的组地址)。路由器向这个组发送特定组查询。 1 秒钟内没有收到该组的报告,发送第二个特定组查询。 二个查询信息后未收到主机响应,组 224.1.1.1 超时,离开组。
      查询器选举
      (1) 所有IGMPv2 路由器在初始都认为自己是查询器,并向本网段所有路由器(224.0.0.1)发送 IGMPv2常规查询报文。
      (2) 然后本网段所有 IGMPv2 路由器比较优先级,越大越优先;若优先级相同比较接口IP,越小越优先。
      (3) 非查询路由器启动一个计时器,收到查询器的常规查询报文就把计时器复位。如果计时器超时,就认为查询路由器发生故障,开始重新选择。

    (3)IGMPV3

    IGMPv3 的提出,主要是为了配合源特定组播的实现,即组播组成员可以指定接收或指定不接收某些组播源的报文。这样主机就可以有选择性接收来自某个特定组播源的数据包,而不是被动接收该组中所有组播源的数据包。IGMPv3 的这一特性,可以实现源特定组播 SSM 技术
     IGMPV3在V1V2的基础上提供了额外的源过滤组播功能(SFM),主机只根据组地址来决定加入某个组,并从任何一个源接收发给该组地址的报文。具有源过滤组播功能(SFM)的主机使用 IGMPv3 来表示主机所希望加入的组播组,同时还表示该主机所希望接收的组播源的地址。限制的方式主要是:包括列表(Inclusion List)或一个排除列表(Exclusion List)来表示对源地址的限制。

    1>IGMPV3的报文类型

    0x11:成员关系查询报文;
    (1) 普通查询:用于获知邻接接口(即查询所传输的网络中所相连的接口)的完整的多播接收状态。 组地址字段和源数量(N)字段都为 0。
    (2) 指定组查询:用于获知邻接接口中跟某一个 IP 地址相关的多播接收状态。组地址字段含有需 要查询的那个组地址,源数量(N)字段为 0。
    (3) 指定组和源查询:用于获知邻接接口是否需要接收来自指定的这些源发往指定组的多播数据报。 组地址字段含有要查询的多播地址,源地址[i]字段含有相关的源地址。
    0x22:版本 3 成员关系报告报文;
    0x12:版本 1 成员关系报告报文;
    0x16:版本 2 成员关系报告报文;
    0x17:版本 2 离开报文。

    2>查询报文格式:

    在这里插入图片描述
    Number of Sources 字段:实际上受限于数据链路层的 MTU,例如在以太网上,数据报最长为 1500 字节,除去 IP 报头的 24 字节和 IGMP 报头的 12 字节,剩余 1464 字节,所以最多包含 366(1464/4) 个源地址。

    3>Group Record 字段格式:

    在这里插入图片描述
    Group Record 消息的类型。
    (1) MODE_IS_INCLUDE:接收源地址列表包含的源发往该组的组播数据;
    (2) MODE_IS_EXCLUDE:不接收源地址列表包含的源发往该组的组播数据; (3) CHANGE_TO_INCLUDE_MODE:过滤模式由 EXCLUDE 转换到 INCLUDE,接收源地址列表包含 的新组播源发往该组播组的数据;
    (4) CHANGE_TO_EXCLUDE_MODE:过滤模式由 INCLUDE 转换到 EXCLUDE,拒绝源地址列表中新 组播源发往该组的组播数据;
    (5) ALLOW_NEW_SOURCES:表示在现有的基础上,需要接收源地址列表包含的源发往该组播组 的组播数据;
    (6) BLOCK_OLD_SOURCES:表示在现有的基础上,不再接收从源地址列表包含的源组播源发往 该组播组的组播数据。

    成员报告消息格式
    在这里插入图片描述

    • 查询响应过程
      路由器周期性的发送一般查询来获取本地网络的 IGMP 成员信息。主机收到一般查询之后,收集自己的组信息,包括感兴趣或不感兴趣的源列表,填入当前状态组记录中,向路由器回复 IGMP 版本 3 成员报告(发向所有 IGMPv3 路由器组 224.0.0.22)。
      当主机一方的组信息或者源信息发生改变的时候(可能是过滤模式改变,或者是源列表改变),主机会把这些改变信息填入到状态改变组记录中,然后主动向路由器发送 IGMP 版本 3 的成员报告。当路由器收到了成员报告后,会刷新本地的组和源的状态;当路由器一方维护的组的过滤模式要从 EXCLUDE 变成 INCLUDE 前,会发送指定组查询,反映在 IGMPv2 上就是对于本地不再感兴趣的组被删除之前,会发送指定组查询;对于本地不再感兴趣的源,则在删除源之前要发送指定源查询。一般,只在收到了状态改变的组记录后才会发送指定组或者指定源 查询。对于当前状态记录,不会发送指定组或者指定源查询。

     路由器上的 IGMP 状态信息
    路由器上的每个接口每个组都有一个组状态,组状态由组地址、过滤模式(INCLUDE / EXCLUDE)、源列表、组定时器组成。

    在这里插入图片描述
     IGMPV3的主要改进
    (1) 支持源特定组播 SSM;
    (2) 向后兼容 IGMPv1 和 IGMPv2;
    (3) 主机可以定义要接收的组播源地址;
    (4) 非查询路由器可以与查询路由器保持参数值同步;
    (5) 最大响应时间从 255s 增加到 53min,适合于较大的网络;
    (6) 辅助数据字段为将来的应用预留了空间;
    (7) 关系成员报告报文发送给目的地址 224.0.0.22,可以帮助二层交换机更有效地实现 IGMP 监听 (IGMPSnooping)功能;
    (8) 报告报文中可以包含多个组记录,可以有效地减少网络通信量;
    (9) 在 IGMPv3 中,取消了前面版本中的响应抑制功能

     IGMPv1/v2/v3 比较
    在这里插入图片描述
    普遍组查询报文中,既不携带组地址,也不携带源地址;
    特定组查询报文中,携带组地址,但不携带源地址;
    特定源组查询报文中,既携带组地址,还携带一个或多个源地址。

     快速离开
    在网络中的末梢网段只连接了一台主机,主机频繁的进行组播组的切换动作,为了减少离开延迟可以在设备上配置组播组快速离开。
    如果末梢网段中连接了多台主机,不建议配置快速离开。这样会导致一台主机离开组播组后, 其他主机也都被迫离开组播组。

    4.PIM-SM

    (1)PIM-SM基础

    1>基本概念
    PIM,协议无关组播,不依赖于某一特定单播路由协议,可利用各种单播路 由协议建立的单播路由表完成 RPF 检查功能,而不是维护一个分离的组播路由表 实现组播转发。由于 PIM 无需收发组播路由更新,所以与其它组播协议相比,PIM 开销降低了许多。PIM 的设计出发点是在 Internet 范围内同时支持 SPT 和 RPT, 并使两者之间灵活转换,因而集中了它们的优点提高了组播效率。PIM 定义了两种模式:密集模式(Dense-Mode)和稀疏模式(Sparse-Mode)。
    PIM-DM 属于密集模式协议,采用了“扩散/剪枝”机制。同时,假定带宽不受限制,每个路由器下的终端都想接收组播数据包,PIM-DM 将组播数据包“推” 到网络中所有的路由器上。这样,无需终端上进行任何操作,即可收到组播报文, 适用于网络规模较小,且终端大多需要组播报文的场景。
    PIM-SM 属于稀疏模式协议,与 PIM-DM 不同之处在于,其不会将组播报文主 动“推”向网络中的路由器,而是当某个路由器下的终端有接收某个特定组播组的报文需求时,采用“拉”的方式,将组播报文从源拉到目的地。这种方式相比 DM 而言更节约带宽,同时能实现精准推送。适用范围也较 DM 更广,可以应用在大型网络中。
    PIM-SM 协议处于 IP 协议的上层,通过原始 socket 和 IP 通信。PIM-SM 在 IP 报文中的协议号为 103,组播地址为 224.0.0.13。
    2>PIM的角色
    BSR:自举路由器,发送 bootstrap 消息,用于确认 RP 路由器并向全网通告。如果在组播网络中静态指定了 RP,网络中也可以不用设置 BSR 路由器。
    选举规则:优先级【默认为 0】和 IP 地址比较,值越大越优先。如果没 有比其优先的设备,则其成为当选 BSR,反之则进入候选 BSR 状态(C-BSR)。
    RP:汇聚点,即共享树 shared tree 的汇聚点,在组播共享树中,会以该汇聚点 为根进行转发,组播流量从源发出时,需要经过 RP,再转向相应的接收者。RP 可以分为动态和静态两种,静态即全网配置某台设备为 RP。动态模式下,在网络中配置候选 RP(C-RP)。
    选举规则:当 BSR 选定后,C-RP 通过单播向 BSR 发送 candidate-RP-advertisement 信息。包含优先级、地址、组播组信息【优先级默认 192,越小越优先】。
    DR: 指定路由器,用于接入组播源和接收者,发送注册、join 报文等。DR 的选 举:如果两边均有优先级参数【默认为 1】,则先比较优先级,再比较地址,如 不全有优先级,则只比较 IP 地址,越大越优先。在组播网络中每个网段都会选举 DR。
    当两台开启了 PIM-SM 协议的路由器接入到同一个 LAN 中时,均会向组播地 址224.0.0.13发送组播hello报文,hello报文中会携带如DR优先级、hold-time、 LAN Prune delay(剪枝延迟)、propagation delay(传送延迟)、override interval (J/P 覆盖时间)等,
    在这里插入图片描述hold time:邻居保持时间,即在hold time时间范围内没有收到hello 消息的话,则认为该邻居失效,
    DR:优先级主要用于 DR 的选举,默认为 1,数值 越大越优先,如 DR 优先级数值相同,或一方不含有 DR 优先级信息,则比较接口 IP 地址,IP 地址大优先。

    (2)PIM的工作机制

    1>PIM邻居建立完成,BSR,RP选举完成,所有的路由器都了解RP的组信息。
    2>组播组成员所在的DR向RP发起接入,然后组播源所在的DR也会去Rp注册,这样接受者可以通过这一路径收到组播源的组播报文。
    3>RPT-SPR切换,寻找最优转发路径
    4>剪枝切换的内容:
    在这里插入图片描述
    在上图所示的组网环境中,R2 被设置为 RP,但通过拓扑图示来看,显然 R1 直接到 R3 的路径要优于 R1—R2—R3 的路径。因此会进行 RPT—SPT 剪枝切换。 切换的动作由 R1 主动发起,其收到第一个组播报文后就会向 R3 发起显式(S, G)加入,加入成功以后,R3 进行剪枝,将组播报文通过 R3-R1 的路径直接发送 到接收者,不再通过 R3-R2-R1 这条路径。此时show ip pim mroute 或者debug ip pim event都可以观察到切换过程:
    在这里插入图片描述
    PS:若网络中存在负载路由,单播会存在负载均衡的情况,但是组播不会;组播会选择下一跳地址大的进行路由转发
    PIM-SSm在查看表项的时候,只会发现(S,G)表项,没有(*,G)表项,因为SSm不进行剪枝切换
    配置区别:ip pim ssm default/range:
    default 参数代表 默认的 232.0.0.0/8 组播组,也可以使用 range+ACL 的方式重新自定义组播地址

    5.IGMP snooping

    (1)基本概念:

    IGMP Snooping 是用来监听主机与路由器之间的 IGMP 报文,设备可以根据组 IGMP 报告报文、IGMP 离开报文而动态地创建、维护和删除组播地址表,从而使得设备接收到组播业务报文后依据各自的组播地 址表项进行转发,减少风暴,提高带宽利用率。

    (2)主要功能:

    侦听 IGMP 报文动态地创建、维护和删除组播地址表,按照组播地址表实现组播 业务报文在指定 成员端口转发;
    协助上游组播路由器正确维护 IGMP 成员关系表;在无三层组播设备环境下实现 IGMP 查询器的功能。

    (3)角色

    1>最大响应时间:IGMPv2 查询者发送的普通组查询报文中含有最大响应时间字段,组播接收者应 在最大响应时间间隔内发送成员报告报文。如果组播接收者没有在最大响应时间间隔内发送成员 报告报文,设备就认为该子网内没有该组播组的接受者,立即删除组播组信息
    在这里插入图片描述
    2>组播组成员端口老化时间:当一个端口加入到组播组中的时候会同时启动该端口的老化定时器。 如果在此定时器超时的时候还没有收到 IGMP 成员报告报文,则老化该组播成员端口。此定时器 默认为 260 秒,不能配置
    在这里插入图片描述
    3>路由器端口老化时间:路由器端口老化定时器设置的时间。如果在此定时器超时的时候还没有收 到 IGMP 查询报文(可以是通用查询报文,也可以是特定组查询报文),则老化此路由器端口。 此定时器默认为 255 秒,即 4 分 15 秒,倒计时方式;该参数可配置
    在这里插入图片描述
    4>组播成员端口(Member port):与组播组成员主机相连的端口。组播组成员指加入某个组播组的主机。可以是物理上相连,也可以是逻辑上相连。设备接收到组播业务报文,将根据组播地址表 向对应组播成员端口转发
    在这里插入图片描述 5>路由器端口(Router port):收到 IGMP 组查询报文或者组播路由协议报文的端口。可以是物理 上相连路由器,也可以是逻辑上相连路由器。当设备收到 IGMP 成员报告或离开报文时,会将报文通过路由器端口转发出去,这样上联路由器能正确维护 IGMP 成员关系表
    在这里插入图片描述

    (4)协议流程:

    1、收到 IGMP 通用查询报文时,如果是已有的路由器端口(即原来这个端口就和组播路由器相连), 则更新路由器端口老化定时器;如果是新路由器端口(即原来这个端口没有和组播路由器相连), 则设备把该端口学习为路由器端口,同时启动对该路由器端口的老化定时器。无论何种情况, 设备收到通用查询报文后,均需向对应 VLAN 中 flood;
    2、 收到 IGMP 特定组查询报文时,设备需学习新的路由器端口或更新路由器端口老化时间,然后 向被查询的组播组成员端口转发该特定组查询报文;
    3、 收到 IGMP 报告报文时,判断该报文要加入的 IP 组播组对应的 MAC 组播组是否已经存在。如 果对应的 MAC 组播组不存在,则新建 MAC 组播组,将接收报告报文的端口加入该 MAC 组播 组中,并启动该端口的老化定时器;如果该报文对应的 MAC 组播组已经存在,但是接收报告 报文的端口不在该 MAC 组播组中,则将接收报告报文的端口加入 MAC 组播组中并启动该端 口的老化定时器;如果该报文对应的 MAC 组播组已存在,并且接收报告报文的端口也已经存 在于该 MAC 组播组,则仅更新接收报告报文的端口上的老化定时器。无论何种情况,设备收 到 IGMP 报告报文后,都只向路由器端口转发,如果路由器端口不存在,则只学习/更新组播组 表项,不转发此报文;
    4、 收到对某 IP 组播组的 Leave 报文,如果开启了快速老化功能,则立即老化该组播成员端口;如果没有开启快速老化功能,则会启动端口的响应查询定时器,如果在该定时器超时的时候还没 有收到该组播组的报告报文,则将该端口从相应 MAC 组播组中删去。当成员端口被老化时, 如果 MAC 组播组没有组播成员端口了,设备应删除该组播组表项。无论何种情况,设备收到 Leave 报文后,都只向路由器端口转发。

    PS
    IGMP查询器:在网络中如果没有三层组播设备,将无法实现 IGMP 查询器的相关功能。为了解决这个问题,可以通过在二层组播设备上配置 IGMP snooping 查询器来实现 IGMP 查询器的功能。使二层组播设备能够建立并维护组播转发表项,从而正常转发组播业务报文。
    配置为查询器后,必须指定设备的一个地址为查询器地址,否则由于缺省源地址为 0.0.0.0 故无法发出 IGMP 组查询报文
    配置了 IP 地址的查询器会参加 VLAN 内 IGMP 查询者的选举,当选查询者负责维护 VLAN 内 IGMP 成员关系表

    (5)IGMP snooping TCN 事件

    当网络拓扑发生变化时,会产生 TCN 事件,生成树根端口会主动发送全局 IGMP 离开报文(组地址:0.0.0.0)来请求 IGMP查询者发送普通组查询报文,达到快速收敛目的;
    使能 IGMP snooping TCN 事件快速收敛后,非生成树根端口也能主动发送全局 IGMP 离开报文 (组地址:0.0.0.0),达到快速收敛目的。

    (6)IGMP Proxying

    当网络中有较多组播组的接收者,为了减少上游组播设备收到的 IGMP 成员报告和离开报文的 数量,有效减小系统开销,可以在设备上配置 IGMP Proxying
    IGMP Proxying 能够代理下游接收者向上游设备发送的 IGMP 成员报告报文和离开报文;也能 够响应上游组播设备发送的 IGMP 组查询报文,再向下游设备发送 IGMP 组查询报文。

    (7)组播vlan

    传统二层组播点播方式,当处于不同的 VLAN 的用户点播时,每个 VLAN 会在本 VLAN 内复 制一份组播流。这种组播点播方式浪费了大量的带宽
    为了解决这个问题,可以通过配置组播 VLAN 的方式,使不同 VLAN 内的用户共用一个组播 VLAN。组播 VLAN 功能开启后,并且组播 VLAN 与用户 VLAN 完全隔离,这样不仅节约了 带宽而且保证了安全性
    组播 VLAN 有两种:分别是 MVR(Multicast VLAN Registration,组播 VLAN 注册)和 MVP (Multicast VLAN Plus,组播 VLAN 增强版)
    1>MVR:
     当多个用户端口属于不同 VLAN 时,可以通过将这些端口添加到 MVR 组播 VLAN 内,实现不 同 VLAN 内的用户共用一个组播 VLAN
     MVR 组播 VLAN 成员端口只能接用户,不能再串接设备,从 VLAN 成员端口发送出去组播报 文不能带 VLAN Tag
     用户端口配置为 hybrid 模式,同时属于用户 VLAN 和 MVR VLAN
    2>MVP:
     用户端口无需添加到组播 VLAN,只需将用户 VLAN 设置为组播 VLAN 的子 VLAN;MVP 将 完成组播 VLAN 和子 VLAN 之间的组播报文复制
     子 VLAN 成员端口可接设备,也可以直接接用户。当接设备时,子 VLAN 成员端口发送组播 报文可以带 VLAN Tag;当连接用户时,子 VLAN 成员端口发送组播报文不带 VLAN Tag
     下游设备无需关注 MVP 设备的配置,正常配置即可

    出现问题以及解决方法:

    1.组播不通
    1>.排查单播是否通
    2>单播通的情况下,排查设备配置是否正常,端口下是否使能PIM-Sm,接入设备上是否使能iGMP,
    PS:vlan下如果配置了未知二层组播丢弃以及组播流量上游转发,l2-multicast drop-unknown ; multicast mrouter-forwarding,那么所有的接入设备上此vlan都必须配置,不然可能出现组播不通的情况。
    2.组播到特定时间表项消失show ip igmp snooping groups,发现表项消失
    1>查询组播相应时间的表格
    PS:如果网络结果中没有组播路由,只是一个单纯的二层组播,则需要配置组播查询器功能,不然到了40分20S左右组播表项会消失,因为无人维护这个表项。
    3.组播丢包
    使用ACL的count动作组,在组播数据的路径上,看看报文丢在哪里
    4.在不同厂商设备对接的时候,组播的RP最好设备成为环回口的地址,尽量避免设置成为组播vlan的地址

    1.sh ip pim mroute 看核心有没有组播路由表,(下面的过程首先是接收端去注册形成(*,G)表项
    然后源去注册形成(S,G)表项,最终RPT-SPR切换,转发数据)
    
    Hostname#show ip pim mroute
    
    IP Multicast Routing Table:
    
    PIM VRF Name: Default
    
    Total 0 (*,*,RP) entry
    
    Total 1 (*,G) entry
    
    Total 1 (S,G) entry
    
    Total 1 (S,G,rpt) entry
    
    Total 0 FCR entry
    
    Up timer/Expiry timer
    
    
    
    (*, 225.1.1.1)
    
    Up time: 00:08:12
    
    RP: 0.0.0.0
    
    RPF nbr: 0.0.0.0
    
    RPF idx: None
    
    Flags:
    
      JOIN DESIRED
    
    Upstream State: JOINED
    
      Local interface list:
    
        vlan3
    
      Joined interface list:
    
      Asserted interface list:
    
     
    
    (138.255.1.134, 225.1.1.1)
    
    Up time: 00:07:24
    
    KAT time: 00:02:22
    
    RPF nbr: 0.0.0.0
    
    RPF idx: None
    
    SPT bit: TRUE
    
    Flags:
    
      JOIN DESIRED
    
      COULD REGISTER
    
    Upstream State: JOINED
    
      Local interface list:
    
      Joined interface list:
    
        register_vif0
    
      Asserted interface list:
    
      Outgoing interface list:
    
        register_vif0
    
        vlan3
    
      Packet count 8646421
    
     
    
    (138.255.1.134, 225.1.1.1, rpt)
    
    Up time: 00:07:24
    
    RP: 0.0.0.0
    
    Flags:
    
      RPT JOIN DESIRED
    
      RPF SGRPT XG EQUAL
    
    Upstream State: NOT PRUNED
    
      Local interface list:
    
      Pruned interface list:
    
      Outgoing interface list:
    
    vlan3
    
    若看到上述状态,说明转发组播数据无问题
    
    
    2.show ip mcache 查询组播的核心转发表
    router#sh ip mcache
    
    Multicast Forward Cache table:
    Total 1 MFC entry
    Total 0 stall upcall packet
    MFC (138.255.1.134/32,  224.123.234.2/32)
      Incoming interface: fastethernet0.1(1)
      Flags: PROTOCOL MFS IPMC 
      Output interface list: 
        fastethernet1(0)
      Waiting packets: NULL
      Uptime: 00:05:36, Timeout in 23 sec
    只要可以看到这个表里面的‘输入输出接口’,那么组播一般工作都是没有问题。
    
    3、sh ip igmp groups 
    
    
    Hostname#show ip igmp groups
    
    IGMP Static Group Membership
    
    Total 1 Static Groups
    
    Group Address    Source Address   Interface            
    
    225.0.0.1        0.0.0.0          vlan2        
    
    IGMP Connected Group Membership
    
    Total 1 groups
    
     
    Group Address  Interface  Uptime    Expires   Last Reporter    V1 Expires  V2 Expires
    
    226.0.0.1      vlan2      00:00:44  00:04:14  128.255.17.66    stopped     stopped
    
    查看组播的接收者是否已经加入了组播组,如果这里已经可以看到该组,就证明igmp处理应该没有问题。
    
    
    
    4,接入交换机show ip igmp snooping group有没有表项
    有的话,证明接入交换机转发无问题
    Hostname#show ip igmp snooping groups
    
    IGMP Snooping Group Membership
    
    Total 1 group
    
     
    
    VLAN ID  Interface Name       Group Address    Expires   Last Reporter    V1 Expires  V2 Expires  Uptime  
    
    _______  ___________________  _______________  ________  _______________  __________  __________  __________  
    
    20       gi0/21               229.55.66.1      00:04:05  128.255.27.89    stopped     stopped     00:00:16
    
    
    展开全文
  • 组播原理

    千次阅读 2018-12-11 14:38:52
    一,组播原理与配置 当网络中部署点到多点通信应用时,若采用单播方式,网络中传输的信息量与需要该信息的用 户量成正比。多份内容相同的信息发送给不同用户,对信息源及网络带宽都将造成巨大压力。若采 用广播方式,...

    一,组播原理与配置
    当网络中部署点到多点通信应用时,若采用单播方式,网络中传输的信息量与需要该信息的用
    户量成正比。多份内容相同的信息发送给不同用户,对信息源及网络带宽都将造成巨大压力。若采
    用广播方式,无需接收信息的主机也将收到该信息,这样不仅信息安全得不到保障且会造成同一网
    段中信息泛滥。
    P组播技术有效地解决了单播和广播在点到多点应用中的问题。组播源只发送一份数据,数据
    在网络节点间被复制、分发且只发送给需要该信息的接收者
    本章将依次介绍组播工作原理、组播路由协议、组播基础配置命令。
    1.组播介绍
    组播作为|P传输的3种方式之一,P组播通信指的是P报文从一个源发出,被转发到一组特定的接收者
    相较于传统的单播和广播,IP组播可以有效地节约网络带宽、降低网络负载,所以被广泛应用于IP四
    数据传送和多媒体会议等网络业务中。
    传统的|P通信有单播( Unicast)和广播( Broadcast)两种方式。
    1.单播方式部署点到多点应用
    采用单播方式时,网络中传输的信息量与需要该信息的用户量成正比。如图6.1所示,当需要该信息的用
    户数量较大时,信息源需要将多份内容相同的信息发送给不同的用户,这对信息源以及网络带宽都将造成巨大
    的压力。因此,该传输方式不利于信息的批量发送,只适用于用户稀少的网络。

    2.广播方式部署点到多点应用
    采用广播方式时,不需要接收信息的主机也将收到该信息,这样不仅信息的安全性得不到
    保障,而且会造成同一网段中信息泛滥。因此,该传输方式不利于与特定对象进行数据交互,同时会浪费大量的带宽。
    由上述可知,传统的单播和广播通信方式不能有效地解决单点发送、多点接收的问题。组播( Multicast)
    可以很好地解决点到多点的数据传输,源只发送一份数据,网络中只有需要该数据的主机(目标主机 HostA和 Hostc)可以接收该数据,其他主机( HostB)不能收到该数据。
    组播的优势:相比单播,由于被传递的信息在距信息源尽可能远的网络节点才开始被复制和分发,所以用户的增加不会导致信息源负载的加重以及网络资源消耗的显著增加。相比广播,由于被传递的信息只会发送给需要该信息的接收者,所以不会造成网络资源的浪费并能提高信息传输的安全性。
    2.组播基本概念
    组播传输的特点是单点发送,多点接收。
    组播组:用|P组播地址进行标识的一个集合。任何用户主机(或其他接收设备),加入一个组播组,就
    成为了该组成员,可以识别并接收发往该组播组的组播数据
    个组播源可以同时向多个组播组发送数据,多个组播源也
    组播源:信息的发送者称为“组播源
    以同时向一个组播组发送报文。组播源通常不需要加入组播组,由源端DR负责管理组播源的注册和SPT( Shortest Path Tree)的建立。
    组播组成员:所有加入某组播组的主机便成为该组播组的成员。组播组中的成员是动态的,主机可以在任何时刻加入或离开组播组。组播组成员可以广泛地分布在网络中的任何地方。
    组播路由器:支持三层组播功能的路由器或交换机。组播路由器不仅能够提供组播路由功能,也能够在与用户连接的末梢网段上提供组播组成员的管理功能。

    展开全文
  • 组播详解

    千次阅读 2020-08-03 09:29:47
    组播协议允许将一台主机发送的数据通过网络路由器和交换机复制到多个加入此组播的主机,是一种一对多的通讯方式。 IP组播的好处、优势 组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向n个...

    本文转自:http://liuqz926.blog.163.com/blog/static/13448936220091121104233491/

    组播协议允许将一台主机发送的数据通过网络路由器和交换机复制到多个加入此组播的主机,是一种一对多的通讯方式。

    IP组播的好处、优势

    组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向n个主机发送相同的数据时,发送主机需要分别向n个主机发送,共发送n次。一个主机用组播协议向n个主机发送相同的数据时,只要发送1次,其数据由网络中的路由器和交换机逐级进行复制并发送给各个接收方,这样既节省服务器资源也节省网络主干的带宽资源。

    与广播协议相比,只有组播接收方向路由器发出请求后,网络路由器才复制一份数据给接收方,从而节省接收方的带宽。而广播方式无论接收方是否需要,网络设备都将所有广播信息向所有设备发送,从而大量占据接收方的接入带宽。

    IP组播历史

    在1980年代初斯坦福大学的一位博士生叫Steve Deering,在为其导师David Cheriton工作,设计一种叫做Vsystem的分布式操作系统。此操作系统允许一台计算机使用MAC层组播向在本地Ethernet段的一组其他计算机传递信息。

    随着工作的扩展组播必须跨越路由器,所以必须将组播扩展到OSI模型的第三层,此历史重任落到了Steve Deering身上,他总结了组播路由的通信协议基础,并最终在1991年12月发表的博士论文中进行了详细的阐述。

    组播协议的优势:

    组播协议的优势在于当需要将大量相同的数据传输到不通主机时,
    

    1.能节省发送数据的主机的系统资源和带宽;

    2.组播是有选择地复制给又要求的主机;

    1. 组播可以穿越公网广泛传播,而广播则只能在局域网或专门的广播网内部传播;

    2. 组播能节省网络主干的带宽;

    组播协议的缺点:

    与单播协议相比,组播没有补包机制,因为组播采用的是UTP的传输方式,并且不是针对一个接受者,所以无法有针对的进行补包。所以直接组播协议传输的数据是不可靠的。
    

    二、为什么宽带网必须使用组播协议

    自从上世纪末长城宽带壮烈的宽带推广运动以来,宽带网一直面临种种问题,但这些问题归结起来就是一个问题,那就是客户端得不到与其接入带宽相称的足够的数据流。

    最早的长城宽带面临的是“宽带无内容”的问题,客户得不到其承诺的视频点播等宽带娱乐,于是投诉、退户甚至诉诸法律。

    电信凭借其雄厚的财力和电话线资源后来居上,但很快又面临网速慢、缺内容的投诉,电信网站上的视频点播似乎总是无尽的等待和缓冲。后来P2P软件的出现使得某些比较专业的用户似乎看到了希望,他们用BT、电驴等软件互传电影等娱乐信息也凑合了。没多久电信和网通就高举着和他们没什么关系的版权大旗封杀了BT、电驴等软件。什么是组播 - Liuqz - liuqz926 的博客

    所有这些都是源于现在宽带网的“上下非对称”的金字塔结构,也就是网络主干的带宽远远小于所有用户带宽之和,但现在网络使用的单播通讯协议却要求网络主干的带宽等于或接近所有用户带宽之和。现在的状况是一个城市或省的网络出口主干的带宽大约相当于其所有客户带宽之和的5%,也就是说假如有5%的客户用BT软件通过网络全速传输数据,那其余95%的客户就不要玩了。现在电信主干上的流量的75%都是P2P应用的流量,已经超过了电信所能承受的极限。

    那么采用CDN技术,将网络内容在城域网内就近缓冲行不行呢?答案是:技术上可行经济上行不通。其需要的服务器是一个巨大的天文数字。现在的大中城市的宽带网用户数量都在20万以上,以此数量来计算光购置CDN服务器就需要2亿元左右!这就是为什么电信不用CDN技术来满足客户需求的原因。所以在服务器的服务能力和客户机的需求上也存在着严重的上下非对称结构。

    那么这个死结是不是没法解开呢?当然不是,组播协议的数据流特点就是“上下非对称”的,也就是说,在网络主干上的一条数据流通过每层交换机的复制可以变成无数客户端的数据流,形成客户端数据流之和远大于主干数据流的金字塔结构。这一特点正好与现在的网络结构相符。所以说,基于组播协议的流媒体宽带娱乐可以解决这一问题。

    举例来说,使用基于组播协议的直播系统可以用一台服务器支持数万客户收看一个或几个频道的网上电视直播。假设一共提供100个频道的电视节目,每个频道是1M的MPEG4高清晰码流,则无论有1万客户还是100万客户,其占用的网络主干都是100M,而3~5台服务器硬件的投资不到100万。

    如果采用我们专利技术的基于组播的VOD系统,客户还可以享受到廉价的点播服务。由于其采用的是组播协议,无论对于网络主干还是VOD服务器的压力都很小。
    

    三、单播/组播/广播 通讯协议的特点及应用对比

    当前的网络中有三种通讯模式:单播、广播、组播,其中的组播出现时间最晚但同时具备单播和广播的优点,最具有发展前景。

    一、单播:

      主机之间“一对一”的通讯模式,网络中的交换机和路由器对数据只进行转发不进行复制。如果10个客户机需要相同的数据,则服务器需要逐一传送,重复10次相同的工作。但由于其能够针对每个客户的及时响应,所以现在的网页浏览全部都是采用IP单播协议。网络中的路由器和交换机根据其目标地址选择传输路径,将IP单播数据传送到其指定的目的地。
    

    什么是组播 - Liuqz - liuqz926 的博客

    单播的优点:

    1. 服务器及时响应客户机的请求

    2. 服务器针对每个客户不通的请求发送不通的数据,容易实现个性化服务。

    单播的缺点:

    1. 服务器针对每个客户机发送数据流,服务器流量=客户机数量×客户机流量;在客户数量大、每个客户机流量大的流媒体应用中服务器不堪重负。

    2. 现有的网络带宽是金字塔结构,城际省际主干带宽仅仅相当于其所有用户带宽之和的5%。如果全部使用单播协议,将造成网络主干不堪重负。现在的P2P应用就已经使主干经常阻塞,只要有5%的客户在全速使用网络,其他人就不要玩了。而将主干扩展20倍几乎是不可能。

    二、 广播:

      主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。有线电视网就是典型的广播型网络,我们的电视机实际上是接受到所有频道的信号,但只将一个频道的信号还原成画面。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。
    

    什么是组播 - Liuqz - liuqz926 的博客

    广播的优点:

    1. 网络设备简单,维护简单,布网成本低廉

    2. 由于服务器不用向每个客户机单独发送数据,所以服务器流量负载极低。

    广播的缺点:

    1.无法针对每个客户的要求和时间及时提供个性化服务。

    1. 网络允许服务器提供数据的带宽有限,客户端的最大带宽=服务总带宽。例如有线电视的客户端的线路支持100个频道(如果采用数字压缩技术,理论上可以提供500个频道),即使服务商有更大的财力配置更多的发送设备、改成光纤主干,也无法超过此极限。也就是说无法向众多客户提供更多样化、更加个性化的服务。

    2. 广播禁止在Internet宽带网上传输。

    三、组播:

       主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要(未加入组)的主机的其他通讯。
    

    什么是组播 - Liuqz - liuqz926 的博客

    组播的优点:

    1. 需要相同数据流的客户端加入相同的组共享一条数据流,节省了服务器的负载。具备广播所具备的优点。

    2. 由于组播协议是根据接受者的需要对数据流进行复制转发,所以服务端的服务总带宽不受客户接入端带宽的限制。IP协议允许有2亿6千多万个(268435456)组播,所以其提供的服务可以非常丰富。

    3. 此协议和单播协议一样允许在Internet宽带网上传输。

    组播的缺点:

    1.与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和QOS加以弥补。

    2.现行网络虽然都支持组播的传输,但在客户认证、QOS等方面还需要完善,这些缺点在理论上都有成熟的解决方案,只是需要逐步推广应用到现存网络当中。

    四、IP组播路由协议详细介绍

    一、概述

    1、组播技术引入的必要性
      随着宽带多媒体网络的不断发展,各种宽带网络应用层出不穷。IP TV、视频会议、数据和资料分发、网络音频应用、网络视频应用、多媒体远程教育等宽带应用都对现有宽带多媒体网络的承载能力提出了挑战。采用单播技术构建的传统网络已经无法满足新兴宽带网络应用在带宽和网络服务质量方面的要求,随之而来的是网络延时、数据丢失等等问题。此时通过引入IP组播技术,有助于解决以上问题。组播网络中,即使组播用户数量成倍增长,骨干网络中网络带宽也无需增加。简单来说,成百上千的组播应用用户和一个组播应用用户消耗的骨干网带宽是一样的,从而最大限度的解决目前宽带应用对带宽和网络服务质量的要求。

    2、IP网络数据传输方式
      组播技术是IP网络数据传输三种方式之一,在介绍IP组播技术之前,先对IP网络数据传输的单播、组播和广播方式做一个简单的介绍:

    单播(Unicast)传输:在发送者和每一接收者之间实现点对点网络连接。 如果一台发送者同时给多个的接收者传输相同的数据,也必须相应的复制多份的相同数据包。如果有大量主机希望获得数据包的同一份拷贝时, 将导致发送者负担沉重、延迟长、网络拥塞;为保证一定的服务质量需增加硬件和带宽。

    组播(Multicast)传输:在发送者和每一接收者之间实现点对多点网络连接。 如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率。减少了骨干网络出现拥塞的可能性。

    广播(Broadcast)传输:是指在IP子网内广播数据包,所有在子网内部的主机都将收到这些数据包。 广播意味着网络向子网每一个主机都投递一份数据包,不论这些主机是否乐于接收该数据包。所以广播的使用范围非常小, 只在本地子网内有效,通过路由器和交换机网络设备控制广播传输。

    二、组播技术

    1、 IP组播技术体系结构
      组播协议分为主机-路由器之间的组成员关系协议和路由器-路由器之间的组播路由协议。组成员关系协议包括IGMP(互连网组管理协议)。组播路由协议分为域内组播路由协议及域间组播路由协议。域内组播路由协议包括PIM-SM、PIM-DM、DVMRP等协议,域间组播路由协议包括MBGP、MSDP等协议。同时为了有效抑制组播数据在链路层的扩散,引入了IGMP Snooping、CGMP等二层组播协议。
      IGMP建立并且维护路由器直联网段的组成员关系信息。域内组播路由协议根据IGMP维护的这些组播组成员关系信息,运用一定的组播路由算法构造组播分发树进行组播数据包转发。域间组播路由协议在各自治域间发布具有组播能力的路由信息以及组播源信息,以使组播数据在域间进行转发。

    2、 组播IP地址
      组播IP地址用于标识一个IP组播组。IANA把D类地址空间分配给IP组播,其范围是从224.0.0.0到239.255.255.255。如下图所示(二进制表示),IP组播地址前四位均为1110。
    八位组(1) 八位组(2) 八位组(3) 八位组(4)
    1110XXXX XXXXXXXX XXXXXXXX XXXXXXXX

    3、 组成员关系协议 (IGMP)
      IGMP协议运行于主机和与主机直接相连的组播路由器之间,主机通过此协议告诉本地路由器希望加入并接受某个特定组播组的信息,同时路由器通过此协议周期性地查询局域网内某个已知组的成员是否处于活动状态(即该网段是否仍有属于某个组播组的成员),实现所连网络组成员关系的收集与维护。
      IGMP有三个版本,IGMPv1由RFC1112定义,目前通用的是IGMPv2,由RFC2236定义。IGMPv3目前仍然是一个草案。IGMPv1中定义了基本的组成员查询和报告过程,IGMPv2在此基础上添加了组成员快速离开的机制,IGMPv3中增加的主要功能是成员可以指定接收或指定不接收某些组播源的报文。这里着重介绍IGMPv2协议的功能。
      IGMPv2通过查询器选举机制为所连网段选举唯一的查询器。查询器周期性的发送普遍组查询消息进行成员关系查询;主机发送报告消息来应答查询。当要加入组播组时,主机不必等待查询消息,主动发送报告消息。当要离开组播组时,主机发送离开组消息;收到离开组消息后,查询器发送特定组查询消息来确定是否所有组成员都已离开。
      通过上述IGMP机制,在组播路由器里建立起一张表,其中包含路由器的各个端口以及在端口所对应的子网上都有哪些组的成员。当路由器接收到某个组G的数据报文后,只向那些有G的成员的端口上转发数据报文。至于数据报文在路由器之间如何转发则由路由协议决定,IGMP协议并不负责。

    4、 网络二层组播相关协议
      网络二层组播相关协议包括IGMP Snooping ,IGMP Proxy和CGMP协议。
      IGMP Snooping的实现机理是:交换机通过侦听主机发向路由器的IGMP

    成员报告消息的方式,形成组成员和交换机接口的对应关系;交换机根据该对应关系将收到组播数据包只转给具有组成员的接口。
      IGMP Proxy与IGMP Snooping实现功能相同但机理相异:IGMP snooping只是通过侦听IGMP的消息来获取有关信息,而IGMP Proxy则拦截了终端用户的IGMP请求并进行相关处理后,再将它转发给上层路由器。
      CGMP(Cisco Group Management Protocol)是Cisco基于客户机/服务器模型开发的私有协议,在CGMP的支持下,组播路由器能够根据接收到的IGMP数据包通知交换机哪些主机何时加入和脱离组播组,交换机利用由这些信息所构建的转发表来确定将组播数据包向哪些接口转发。GMRP是主机到以太网交换机的标准协议,它使组播用户可以在第二层交换机上对组播成员进行注册。

    5、 组播路由协议 (PIM-SM)
      众多的组播路由协议中,目前应用最多的协议是 PIM-SM稀疏模式协议无关组播。
      在PIM-SM域中,运行PIM-SM协议的路由器周期性的发送Hello消息,用以发现邻接的PIM路由器,并且负责在多路访问网络中进行指定路由器(DR)的选举。这里,DR负责为其直连组成员朝着组播分发树根节点的方向发送"加入/剪枝"消息,或是将直连组播源的数据发向组播分发树。
    什么是组播 - Liuqz - liuqz926 的博客

    PIM-SM显式的加入机制

    PIM-SM通过建立组播分发树来进行组播数据包的转发。组播分发树分为两种:以组G的RP为根的共享树(Shared Tree)和以组播源为根的最短路径树(Shortest Path Tree)。

    PIM-SM通过显式的加入/剪枝机制来完成组播分发树的建立与维护。如上图所示:

    当DR收到一个发自接收端的加入(Join),它就会向着组G的RP方向逐跳组播发出一个(*,G)加入信息用以加入共享树;

    源主机向组发送组播数据时,源的数据被封装在注册消息内,并由其DR 单播至RP,RP再将源的解封装数据包沿着共享树转发到各个组成员;

    RP朝着源方向向第一跳路由器发送(S,G)加入信息,用以加入此源的最短路径树,这样源的数据包将沿着其最短路径树不加封装地发送到RP;

    当第一个组播数据沿此树到达时,RP向源的DR发送注册-停止消息,以使DR停止注册封装过程。此后,这个源的组播数据不再注册封装,而是先沿着源的最短路径树发送到RP,再由RP 将其沿着共享树转发到各个组成员。

    当不再需要组播数据时,DR向着组G的RP逐跳组播剪枝消息用以剪枝共享树。
      PIM-SM中还涉及到其根节点RP的选择机制。PIM-SM域内配置了一个或多个候选自举路由器(Candidate-BSR)。应用一定的规则从中选出自举路由器(BSR)。PIM-SM域中还配置了候选 RP路由器(Candidate-RP),这些候选 RP将包含了它们地址及可以服务的组播组等信息的包单播至自举路由器。BSR 定期生成包括一系列候选 RP以及相应的组地址的"自举"消息。"自举"消息在整个域中逐跳发送。路由器接收并保存这些"自举"消息。若 DR 从直连主机收到了某组的成员关系报告后,如果它没有这个组的路由项,DR 将使用一个hash算法将组地址映射至一个可以为该组服务的候选 RP。然后 DR 将朝RP方向逐跳组播"加入/剪枝"消息。若 DR从直连主机收到组播数据包,如果它没有这个组的路由项,DR 将使用hash算法将组地址映射至一个可以为该组服务的候选 RP。然后 DR将组播数据封装在注册消息中单播到RP。

    五、IP组播地址

    组播协议的地址在IP协议中属于D类地址。
    D类地址是从224.0.0.0到239.255.255.255之间的IP地址其中224.0.0.0到224.0.0.255是被保留的地址。

    组播协议的地址范围类似于一般的单播地址,被划分为两个大的地址范围,

    239.0.0.0—239.255.255.255是私有地址,供各个内部网在内部使用,这个地址的组播不能上公网,类似于单播协议使用的192.168.X.X和10.X.X.X。

    224.0.1.0—238.255.255.255是公用的组播地址,可以用于Internet上。

    下面是一些常见的有特殊用途的IP组播地址

    224.0.0.0 - Base address

    224.0.0.1 - 网段中所有支持多播的主机

    224.0.0.2 - 网段中所有支持多播的路由器

    224.0.0.4 - 网段中所有的DVMRP路由器

    224.0.0.5 - 所有的OSPF路由器

    224.0.0.6 - 所有的OSPF指派路由器

    224.0.0.7 - 所有的ST路由器

    224.0.0.8 - 所有的ST主机

    224.0.0.9 - 所有RIPv2路由器

    224.0.0.10 - 网段中所有支的路由器

    224.0.0.11 - Mobile-Agents

    224.0.0.12 - DHCP server / relay agent服务专用地址

    224.0.0.13 - 所有的PIM路由器

    224.0.0.22 - 所有的IGMP路由器

    224.0.0.251 - 所有的支持组播的DNS服务器

    224.0.0.9 RIPv2支持组播更新。

    224.0.0.22 IGMPv2使用此地址,这个协议的本意是减少广播,让组员以组播形式通信。

    224.0.0.5 224.0.0.6这两个是ospf协议使用的组播地址。

    在broadcast network不论是DR,BDR,DRother,大家发送hello packet的时候目标地址都是AllSPFRouter(224.0.0.5);DRother向DR,BDR发送DD,LSA request或者LSA UPdate时目标地址是AllDRouter(224.0.0.6);DR,BDR向DRother发送DD,LSA Request或者LSA Update时目标地址是AllSPFRouter(224.0.0.5);retransmit的LSA都是unicast,LSA ACK要看是explicit ack(unicast)还是implicit ack(multicast 224.0.0.6);

    组播IP地址与以太网二层MAC地址的映射:

    IP组播地址用于标识一个IP组播组。IANA把D类地址空间分配给IP组播,范围从224.0.0.0到239.255.255.255,IP组播地址前四位均为1110。

    从224.0.0.0至224.0.0.255被IANA保留为网络协议使用。例如:244.0.0.1 全主机组244.0.0.2 全多播路由器组244.0.0.3全DVMRP路由器组244.0.0.5 全OSPF路由器组。在这一范围的多播包不会被转发出本地网络,也不会考虑多播包的TTL值。

    地址从239.0.0.0至239.255.255.255作为管理范围地址,保留为私有内部域使用。

    如下图所示,以太网和FDDI的MAC地址01:00:5E:00:00:00到01:00:5E:7F:FF:FF用于将三层IP组播地址映射为二层地址,即IP组播地址中的低23位放入IEEE MAC地址的低23位。IP组播地址有28位地址空间,但只有23位被映射到IEEE MAC地址,这样会有32个IP组播地址映射到同一MAC地址上。

    什么是组播 - Liuqz - liuqz926 的博客

    六、如何构建支持组播协议的校园网

    一、 在校园网中构建支持组播协议的网络需要注意几点

    1. 中心的三层交换机要支持组播协议(建议采用PIM稀疏SM模式)。

    2. 直接连接三层交换机的汇聚层的二层交换机需要支持IGMP SNOOPING协议,一般的智能或带网管的交换机都具有此功能。需要注意的是二层交换机的处理能力,IGMP侦听需要消耗交换机的处理能力,所以有些处理能力较差的二层交换机在数据流量较小的时候IGMP侦听能够做的很好的,一但数据流量较大时就侦听不到的情况,导致用户无法正常加入组播。

    3. 最下级的接入层交换机最好用智能交换机,出于经济上的考虑也可以用一般的傻交换机。但决不能用古老的共享式HUB集线器,因为共享式HUB会严重影响其他客户机的通讯。

    4. 组播视频服务器最好直接连接核心的三层交换机,中间不要通过二层交换机级连。

    1. 用我们下载页面提供的组播测试工具测试组播协议是否连通。

    6. 具体的组播原理和配置,请到下载页面下载组播原理和配置的资料包

    1.    一个简化的拓扑结构如下图所示:
      

    什么是组播 - Liuqz - liuqz926 的博客

    二、 关于防火墙问题

    我们的建议是将组播协议的数据流旁路绕过防火墙,即连接一条不经过防火墙的链路,并在端口地址列表中只允许组播地址的数据包通过;或在防火墙内部设定透明穿透bypass,即对于组播地址的数据包不作分析处理,直接转发。其考虑基于如下几点:

    1. 现在网络中使用的防火墙种类繁多、性能各异。但总体上来说对于处理视频信息这样的巨大流量都是力不从心的。

    2. 组播数据流是非连接的UTP,而且需要客户机自己加入组播才能收到组播,所以发送数据者无通过组播定位攻击,目前为止还未出现以组播为载体的病毒和黑客程序。

    三、关于收费网关问题

    收费网关一般位于最后的组播复制节点的更上层,所以通过原有的BAS收费网关系统对组播数据流进行复制是不现实的,一般是将组播数据流绕过现有的BAS收费网关,用另外的方式收费,解决方式有几种:

    1. 采用可控组播协议的方式,现在的华为、中兴的交换机都支持此技术。

    2. 采用包月计费的方式。

    3. 采用免费收看用广告赚取收入的方式。

    七 如何构建支持组播协议的城域网

    城域网的主干一般采用Ethernet over SDH,或直接采用以太网,他们支持IP组播问题不大。难点在于最后一公里的接入网部分,接入网有两种主流技术,以太网和ADSL,其中以太网接入很容易开通组播。

    但是现在电信公网的接入以ADSL为主,ADSL的物理基础是ATM,所以其原本是不支持IP组播的,通过后来的一些技术改进才使其支持IP组播,其中的主流是DSLAM技术。

    一、 ADSL承载组播的特殊性

    由于ADSL的底层不同于一般的以太网帧结构,因此在DSLAM上实现IP组播可以采用IGMP Proxy和IGMP Snooping两种方式。
      IGMP Proxy的实现机理:DSLAM靠拦截用户和路由器之间的IGMP报文建立组播表,Proxy设备的上联端口执行主机的角色,下联端口执行路由器的角色。
      IGMP Snooping的实现机理:DSLAM以侦听主机发向路由器IGMP成员报告消息的方式,形成组成员和交换机端口的对应关系,DSLAM则根据该对应关系,将收到的组播数据包转发到组成员的端口。
      早期的基于纯ATM交换内核的DSLAM,由于PVC的终结是在BAS上,DSLAM支持数据的透传,不能对数据进行任何的处理,所以只能实现IGMP Snooping功能,而不能支持IGMP Proxy功能。现在基于纯IP交换的第三代DSLAM,可以同时支持IGMP Proxy和IGMP Snooping功能。

    二、 DSLAM中IP组播性能的衡量

    DSLAM中实现IP组播性能的衡量主要分为功能和性能两部分。
    1.功能主要包括:
      (1)DSLAM组播流的转发。DSLAM能按组播转发表正确转发,也就是说只有被授权的用户板端口才能收到组播流,而其他非授权端口不能收到组播流;
      (2)多个组成员的加入不会影响到组内其他成员收看组播节目;同样,单个组成员的离开和单个组的离开也不会影响到组内其他成员和其他组的成员观看组播节目;
      (3)对组播权限的控制。对权限的控制应该是基于端口号、MAC地址或IP地址的灵活控制,而且控制的颗粒度应能控制某个用户可以加入此组播组但是不能加入彼组播组;
      (4)IP TV频道切换时延。时延在用户能接收的范围内与电视频道切换时间比较理想。
    2.其性能主要包括:
      (1)成员Join/Leave的时延。Join时延是指从待测路由器收到指定组播组的Join消息到它开始向该指定组播组转发组播流的时延;Leave时延是指从待测路由器收到组的Leave消息到它停止向该指定组转发组播流的时延。如果Join/Leave时延短则意味着在相同时间里加入/离开的组播组全面改造。其间对内容的分发是一个要重点考虑的问题,因为视频业务对带宽的占用是相当大的,如果分发不当不仅会影响到视频业务的质量,而且还会对原有业务产生负面的影响。建议内容分发网采用分级的分布式结构,这可以在一定程度上缓解IP城域网的压力;
      (2)单/多PVC下多业务的QoS。保证在同一条PVC或多条PVC上能传送不同的业务;
      (3)单板组播性能。该性能用于表征单用户板满情况下最大无差错转发时的组播流速率,速率的大小关系到组播源可以向用户提供的业务类型,如果速率不够大就有可能会限制某些业务的开展。

    展开全文
  • 组播原理介绍

    2014-01-04 12:06:44
    介绍组播的使用场景和应用设置;介绍了个各个设置的含义、影响
  • 组播地址基础详解

    万次阅读 2016-05-23 14:25:27
    组播地址基础详解   IANA已经把D类地址空间分配给了IP组播地址. D类空间的地址在其第一个字节的前4位,用二进制值1110来识别. 所以组播地址的范围是: 224.0.0.0到239.255.255.255. D类地址: ...
  • 组播详解【转】

    千次阅读 2019-10-06 08:55:14
    组播协议允许将一台主机发送的数据通过网络路由器和交换机复制到多个加入此组播的主机,是一种一对多的通讯方式。 IP组播的好处、优势 组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向n个...
  • 单播、广播、组播详解

    万次阅读 2014-11-07 12:48:58
    组播协议允许将一台主机发送的数据通过网络路由器和交换机复制到多个加入此组播的主机,是一种一对多的通讯方式。 IP组播的好处、优势 组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向...
  • 介绍TCP/IP协议栈中关于组播原理解释及应用
  • VLAN原理详解

    万次阅读 多人点赞 2019-05-31 09:47:54
    严格地说,并不仅仅是广播帧,多帧(Multicast Frame)和目标不明的单帧(Unknown Unicast Frame)也能在同一个广播域中畅行无阻。 本来,二层交换机只能构建单一的广播域,不过使用VLAN功能后,它能够 将...
  • 组播协议原理讲解

    万次阅读 2014-09-11 21:40:17
    单播、组播、广播是计算机网络上三种基本的通信方式。  单播是相互感兴趣的主机双方进行通信的方式,主机不能接收对其不感兴趣的其它主机发送的信息,属于点对点通信。  广播是主机向子网内所有主机发送信息,...
  • 多播(组播原理分析

    千次阅读 2018-05-24 18:55:27
    为什么要使用多: 网卡从网络上接收到目标物理地址对应的所有bit位都为1的数据报时,会收到这条消息并将其上传给驱动程序,网卡的这种工作模式称为广播模式,网卡的缺省工作模式包含直接模式和广播模式。...
  • OSPF协议原理详解

    千次阅读 多人点赞 2020-04-21 23:17:13
    OSPF协议原理详解 基本概念 OSPF网络类型 OSPF报文类型 OSPF邻居建立过程 DR和BDR的选举 LSA OSPF区域类型和结构 OSPF路径选择 OSPF虚链路 一.链路状态路由协议 (Link Status Protocol)基本概念 1.OSPF的概念 ...
  • NDP原理详解

    千次阅读 2021-01-25 12:29:52
    报文如图所示: PC1的NS报文是发送到一个组播IPv6地址,这个地址我们称为请求节点组播地址。一个IPv6接口会通过自动映射技术为自己的每个单播地址(包括链路本地地址)创建一个请求节点组播地址。该地址由固定的前缀...
  • 一、寻址模式 ①寻址模式—概述 在计算机网络中,寻址模式是指我们如何寻址网络上的主机的机制。 IPv4和IPv6提供几种类型的模式,...网络交换机或路由器在接收到指定到单个主机的单IP分组时,发送到其连接到...
  • Redis原理详解

    2020-08-11 15:40:30
    Redis 基础详解 一、Redis 是什么 Redis 是一个使用 C 语言写成的,开源的、key-value 结构的、非关系型数据库。它支持存储的 value 类型相对更多,包括 String(字符串)、List(列表)、Set(集合)、Sorted Set(有序...
  • IGMP Snooping配置在二层组播设备上,通过对上游三层设备和下游用户之间的IGMP报文进行分析,建立和维护二层组播转发表,实现组播数据报文在数据链路层的按需分发。IGMP Snooping (Internet Group Management ...
  • 组播及igmp/mld协议详解(二)

    万次阅读 2018-05-10 09:59:01
    主机向本地的组播路由器发送IGMP消息来表明自己所属的组播组。在IGMP协议中,路由器侦听IGMP消息并周期的发出查询,以发现某个子网上哪些组是活动的,哪些是不活动的。 IGMP消息在IP数据报内发送,用IP协议号2来...
  • MPLS原理详解

    2016-05-12 10:57:50
    3、LDP:首先设备上开启ldp功能后,设备就会发送ldp的播报文,来获取各自邻居的信息进而建立tcp和udp会话。之后再自动或手动分配标签。 有关LDP会话建立过程,请等待。。。。。。 4、mpls数据转发分析  首先ip...
  • Service原理详解

    2015-12-14 21:35:55
    Service原理详解 版本:2018/9/1-1(18:00) Service原理详解 基础(6) 生命周期(4) 通信(4) 启动模式(21) startService流程 源码解析 bindService流程 ServiceDispatcher 源码解析...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,776
精华内容 3,910
关键字:

组播原理详解