栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

srs源码分析5-handshake

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

srs源码分析5-handshake

srs源码分析

本文分析的srs版本是0.6.0

srs源码分析1-搭建环境

srs源码分析2-浅析state_threads

srs源码分析3-srs的启动

srs源码分析4-客户端的连接

srs源码分析5-handshake

srs源码分析6-connect

以下正在写作中。。。

srs源码分析7-create stream

srs源码分析8-推流-publish

srs源码分析9-推流-unpublish

srs源码分析10-拉流-play

srs源码分析11-拉流-pause

srs源码分析12-转发-forward


使用到的网络抓包数据可以从这里下载:https://gitee.com/qiuguolu1108/blog。


要建立一个有效的RTMP Connect连接,首先需要进行握手,客户端要向服务器发送C0、C1、C2(按序)三个chunk,服务器向客户端发送S0、S1、S2(按序)三个chunk,然后才能进行有效的信息传输。

RTMP协议本身并没有规定这6个Message的具体传输顺序,但RTMP协议的实现者需要保证这几点:

  • 客户端要等收到S1之后才能发送C2
  • 客户端要等收到S2之后才能发送其他信息(控制信息和真实⾳视频等数据)
  • 服务端要等到收到C0之后发送S1
  • 服务端必须等到收到C1之后才能发送S2
  • 服务端必须等到收到C2之后才能发送其他信息(控制信息和真实⾳视频等数据)

如果每次发送⼀个握⼿chunk的话握⼿顺序会是这样:

    +-------------+                           +-------------+
    |    Client   |       TCP/IP Network      |    Server   |
    +-------------+            |              +-------------+
          |                    |                     |
    Uninitialized              |               Uninitialized
          |          C0        |                     |
          |------------------->|         C0          |
          |                    |-------------------->|
          |          C1        |                     |
          |------------------->|         S0          |
          |                    |<--------------------|
          |                    |         S1          |
     Version sent              |<--------------------|
          |          S0        |                     |
          |<-------------------|                     |
          |          S1        |                     |
          |<-------------------|                Version sent
          |                    |         C1          |
          |                    |-------------------->|
          |          C2        |                     |
          |------------------->|         S2          |
          |                    |<--------------------|
       Ack sent                |                  Ack Sent
          |          S2        |                     |
          |<-------------------|                     |
          |                    |         C2          |
          |                    |-------------------->|
     Handshake Done            |               Handshake Done
          |                    |                     |
              Pictorial Representation of Handshake

实际的发送顺序是这样的:

    +-------------+                           +-------------+
    |    Client   |       TCP/IP Network      |    Server   |
    +-------------+            |              +-------------+
          |                    |                     |
    Uninitialized              |               Uninitialized
          |      C0 + C1       |                     |
          |------------------->|        C0 + C1      |
          |                    |-------------------->|
          |                    |                     |
          |                    |     S0 + S1 + S2    |
          |    S0 + S1 + S2    |<--------------------|
          |<-------------------|                     |
          |                    |                     |
          |          C2        |                     |
          |------------------->|         C2          |
          |                    |-------------------->|
     Handshake Done            |               Handshake Done
          |                    |                     |

C0和S0的格式:

0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|     version   |
+-+-+-+-+-+-+-+-+

version:8bit,RTMP的版本,一般为3。

C1和S1的格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       time (4 bytes)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       zero (4 bytes)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random bytes                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random bytes                           |
|                           (cont)                              |
|                            ....                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

time:4个字节的时间戳,应该作为未来从这个端点发出的所有块的时间基准,可能为0,或任意值。

zero:这4个字节必须全为0。

random bytes:1528个字节的随机值。

C2和S2的格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       time (4 bytes)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       time2 (4 bytes)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random echo                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random echo                            |
|                           (cont)                              |
|                            ....                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

time:该字段必须包含对方包内的时间戳,S2对应C1,C2对应S1。

time2:该字段必须包含对方前面包(S1或C1)被读取的时间戳。

random echo:该字段必须包含对方发送的包(S1或C1)的随机数字段,S2对应C1,C2对应S1。

从抓包数据来看:

C0: 03
S0: 03

C1: 007a983b000000002900000023480000be18000084670000e14a00006c3d0000d6...
S2: 007a983b000000002900000023480000be18000084670000e14a00006c3d0000d6...

S1: 614e1a6b007a983b89eaa922767a1ee526d695a3db939c84a8b33aaa6fdd618d4b...
C2: 614e1a6b007a983b89eaa922767a1ee526d695a3db939c84a8b33aaa6fdd618d4b...
int SrsClient::do_cycle()
{
...
	
	if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
		srs_error("rtmp handshake failed. ret=%d", ret);
		return ret;
	}
	srs_verbose("rtmp handshake success");
    
...
}

int SrsRtmp::handshake()
{
	int ret = ERROR_SUCCESS;
	
    SrsSocket skt(stfd);
    
    SrsComplexHandshake complex_hs;      
    SrsSimpleHandshake simple_hs;        

    if ((ret = simple_hs.handshake_with_client(skt, complex_hs)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

客户端连接srs后,经过TCP三次握手后,srs会执行到SrsRtmp::handshake函数,等待和客户端进行RTMP握手。

int SrsSimpleHandshake::handshake_with_client(SrsSocket& skt, SrsComplexHandshake& complex_hs)
{
	int ret = ERROR_SUCCESS;
	
    ssize_t nsize;
    
    char* c0c1 = new char[1537];
    SrsAutoFree(char, c0c1, true);   

	
    if ((ret = skt.read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) {    
        srs_warn("read c0c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("read c0c1 success.");

	// plain text required.    版本必须是3
	if (c0c1[0] != 0x03) {
		ret = ERROR_RTMP_PLAIN_REQUIRED;
		srs_warn("only support rtmp plain text. ret=%d", ret);
		return ret;
	}
    srs_verbose("check c0 success, required plain text.");
   
    
    ret = complex_hs.handshake_with_client(skt, c0c1 + 1);
    if (ret == ERROR_SUCCESS) {
	    srs_trace("complex handshake success.");
	    return ret;         
    }
    if (ret != ERROR_RTMP_TRY_SIMPLE_HS) {
	    srs_error("complex handshake failed. ret=%d", ret);
    	return ret;
    }
   
    
    srs_info("rollback complex to simple handshake. ret=%d", ret);
	
	char* s0s1s2 = new char[3073];
	srs_random_generate(s0s1s2, 3073);     
    SrsAutoFree(char, s0s1s2, true);
	
    s0s1s2[0] = 0x03;     
    
    
    if ((ret = skt.write(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) {
        srs_warn("simple handshake send s0s1s2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("simple handshake send s0s1s2 success.");
    
    char* c2 = new char[1536];
    SrsAutoFree(char, c2, true);
    
    
    if ((ret = skt.read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) {
        srs_warn("simple handshake read c2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("simple handshake read c2 success.");
    
    srs_trace("simple handshake success.");
    
	return ret;
}

RTMP中的握手分为:simple handshake和complex handshake。complex handshake的相关知识,可以参考srs作者的这篇文章rtmp complex handshake,变更的握手,支持h264/aac。

#define SrsAutoFree(className, instance, is_array) 
	__SrsAutoFree _auto_free_##instance(&instance, is_array)
    
template
class __SrsAutoFree
{
private:
    T** ptr;        
    bool is_array;  
public:
    
    __SrsAutoFree(T** _ptr, bool _is_array){
        ptr = _ptr;
        is_array = _is_array;
    }
    
    
    virtual ~__SrsAutoFree(){
        if (ptr == NULL || *ptr == NULL) {
            return;
        }
        
        if (is_array) {  
            delete[] *ptr;     
        } else {
            delete *ptr;       
        }
        
        *ptr = NULL;
    }
};

使用RAII的思想,实现了对资源的托管。核心是:托管对象在离开其作用域时,其析构器会被调用,在析构器释放被托管的资源

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/289135.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号