日期:2014-05-20  浏览次数:20881 次

RTMP协议 握手
小弟参照 rtmp协议的标准的握手部分大致这样的意思:
 .Client → Server :向服务器发出握手请求.这不属于协议包一部分,该握手请求第一个字节为(0×03),其后跟着1536个字节.
 .尽管看上去这部分的内容对于RTMP协议来说并不是至关重要的,但也不可随意对待.
 .Server → Client :服务器向客户端回应握手请求.这部分的数据仍然不属于RTMP协议的部分.该回应的其实字节仍然为(0x03),
 .但是后边跟着个长度为1536个字节(一共为3072 )的包块.第一个1536块看上去似乎可以是任意内容,甚至好像可以是Null都没有 关系.第二个1536的代码块,
  .是上一步客户端向服务器端发送的握手请求的内容.
  .Client→Server:把上一步服务器向客户端回应的第二块1536个字节的数据块.

但是小弟用red5自带的一个视频播发的例子通过wireshark捕获得到的握手的数据包不满足以上协议的说明,具体情况着这样的,
.其中c0为03;c1的time字段为 00 42 cb 18;zero 字段为 80 00 07 02;random(随机数为)为 5f 74 c4 77 ca 17 14...
.对应s0为03;s1的time字段为 00 00 00 00;zero 字段为 01 02 03 04;random(随机数据)为 9d 4e 94 d4 ca 6a 6b 63....
.s2的time为 44 b4 3a b9;time2为 94 b3 a6 c1; 随机返回为:a1 e7 ac a0 41 e5 df...
.c2的time为 00 00 00 00;time2为 00 00 00 00; 随机返回为:9d 4e 94 d4 ca 6a 6b 63...
.我发现一些问题 c2 的time对应着s1 的time, c2的随机返回对应着s1的随机数据,这个数据正常但是s2与c1几乎没有多大的联系
是不是red5的remp协议的处理有问题,还是rtmp协议更新了?
请各位前辈指点一二,谢谢!

------解决方案--------------------
给你一个建议:
(1)貌似你没有读它的协议,这东西不是猜是没用的。
(2)如果你连Handshake都看不懂的话,后面还有更复杂的概念,够你吃一壶的。所以你必须看明白它的文档,自己看明白。如果看不明白,就不要自己写这个协议了,用adobe自带的库吧。


------解决方案--------------------
RTMP这个协议虽然不能说烂,但也决非一流的协议。文档很乱,保持在draft水准,还是v1.0。

1. 你暂时不用关心这个timestamp,在写代码的时候总是设置为00 00 00 00即可
2. zero也设成为0就行了
3. 用wireshark高点的版本可以自动解析RTMP,方便跟踪,不要自己明码分析
4. 可以先用adobe的live encoder与fms交互,对其抓包分析,验证协议

你自己动手,handshake部分还很容易通过的。但是后面的事情估计对你来说有点复杂,它自己有复用层,还要编成AMF0格式的消息。从你目前的状况看,要完成一个RTMP要猴年马月了吧,呵呵

我写过一点文档,不过我不面向这么简单的问题的。
http://blog.sina.com.cn/s/articlelist_2365457685_2_1.html
------解决方案--------------------
C/C++ code

// C0, C1, C2, S0, S1, S2
int RTMP_LiveConn::Handshake()
{
    RTMP_UINT8 buf_C0[1];
    RTMP_UINT8 buf_S0[1];
    int n;

    buf_C0[0] = RTMP_VERSION;
    m_pLogger->Write(LOG_NORMAL,"Send C0.");
    if(m_iSock.Send(buf_C0, 1) <0)
    {
        printf("failed to send C0.\n");
        return -1;
    }

    // 起时时间
    RTMP_UINT32 timestamp = 0;
    m_iSysClock.Reset();

    RTMP_UINT8 buf_C1[1536];
    if(1)
    {
        RTMP_Encoder enc(buf_C1);
        enc.PutUint32(timestamp);
        enc.PutPadding(4, 0);
    }

    m_pLogger->Write(LOG_NORMAL,"Send C1.");
    if(m_iSock.Send(buf_C1, 1536) <0)
    {
        printf("failed to send C1.\n");
        return -1;
    }

    // 接收S0
    m_pLogger->Write(LOG_NORMAL,"Recv S0.");
    n = m_iSock.Recv(buf_S0,1);
    if(n != 1)
    {
        printf("failed to recv S0.\n");
        return -1;
    }
    if(buf_S0[0] != RTMP_VERSION)
    {
        printf("S0 error. \n");
        return -1;
    }

    // 接收S1
    m_pLogger->Write(LOG_NORMAL,"Recv S1.");
    RTMP_UINT8 buf_S1[1536];
    TryRecvAll(1536);
    n = m_nBytesNum;
    if(n != 1536)
    {
        printf("error: S1 length %d bytes.\n", n);
        return -1;
    }
    memcpy(buf_S1, m_pBytes, n);

    // 发送C2 (C2=S1)
    RTMP_UINT8 buf_C2[1536];
    memcpy(buf_C2, buf_S1, 1536);
    if(1)
    {
        RTMP_Encoder enc(buf_C2);
        enc.Skip(4);

        // 计算时间
        RTMP_UINT32 timestamp = m_iSysClock.Update();
        enc.PutUint32(timestamp);    
    }
    m_pLogger->Write(LOG_NORMAL,"Send C2.");
    if(m_iSock.Send(buf_C2, 1536) < 0)
    {
        printf("failed to send C2.\n");
        return -1;
    }

    // 接收S2 (SHOULD == C1)
    m_pLogger->Write(LOG_NORMAL,"Recv S2.");
    RTMP_UINT8 buf_S2[1536];
    TryRecvAll(1536);
    n = m_nBytesNum;
    if(n != 1536) 
    {
        printf("error: S2 length %d bytes.", n);
        return -1;
    }
    memcpy(buf_S2, m_pBytes, n);

    if(memcmp(buf_C1+8, buf_S2+8, 1528) != 0)
    {
        printf("S2 != C1 \n");
        return -1;
    }
    timestamp = 0;
    if(1)
    {
        RTMP_Decoder dec(buf_S2+4);
        timestamp = dec.GetUint32();
        printf("time delta = %d ms\n", timestamp);
    }

    return 0;
}