medooze源码分析--启动服务器
2018-11-15 本文已影响212人
音视频直播技术专家
createEndpoint
当我们打开index.js 后,看到的第一个重要的 API就是createEndpoint函数了。我们就以这个 API为开头,一步一步的分析一下medooze的运行机制。
createEndpoint
函数定义在 medooze-media-server
库中的 lib 目录下的 MediaServer.js 中。代码如下:
/**
* Create a new endpoint object
* @memberof MediaServer
* @param {String} ip - External IP address of server, to be used when announcing the local ICE candidate
* @returns {Endpoint} The new created endpoing
*/
MediaServer.createEndpoint = function(ip)
{
//Cretate new rtp endpoint
return new Endpoint(ip);
};
它的作用是公布 ICE 本地候选者,当做一个终端。
顺着这个函数我们继续往下看,在该函数中会创建一个 Endpoint 对象。
Endpoint
在 medooze-media-server
库中的 lib 目录下的 Endpoint.js 文件是 Endpoint 的类文件。该类的作用如下:
/**
* An endpoint represent an UDP server socket.
* The endpoint will process STUN requests in order to be able to associate the remote ip:port with the registered transport and forward any further data comming from that transport.
* Being a server it is ICE-lite.
*/
/**
*endpoint 表示UDP服务器套接字。
*endpoint 将处理STUN请求,以便能够将远程ip:port与注册的传输相关联,并转发来自该传输的任何进一步的通信数据。
*作为一个服务器,它是轻量级的ICE。
*/
在创建 Endpoint 对象时,会调用它的构造函数。其代码如下:
constructor(ip)
{
//Store ip address of the endpoint
this.ip = ip;
//Create native endpoint
this.bundle = new Native.RTPBundleTransport();
//Start it
this.bundle.Init();
//Store all transports
this.transports = new Set();
//Create candidate
this.candidate = new CandidateInfo("1", 1, "UDP", 33554431, ip, this.bundle.GetLocalPort(), "host");
//Get fingerprint (global at media server level currently)
this.fingerprint = Native.MediaServer.GetFingerprint().toString();
}
通过上面的代码可以看到 Endpoint 包括以下几个成员:
- ip : STUN 服务器IP地址。
- bundle:native层的Endpoint。
- transports: 这是一个集合,用于存放所有用到的传输协议
- candidate: ICE 候选者
- fingerprint: 指纹,用于网络安全。
接下来,我们分别看一下 Native.RTPBundleTransport 和 CandidateInfo。
RTPBundleTransport
RTPBundleTransport 类定义在 media-server
中的 include/RTPBundleTransport.h文件中。该类的构造函数如下:
RTPBundleTransport::RTPBundleTransport()
{
//Init values
socket = FD_INVALID;
port = 0;
//No thread
setZeroThread(&thread);
running = false;
//Mutex
pthread_mutex_init(&mutex,0);
pthread_cond_init(&cond,0);
}
在 Endpoint
类的构函数中,首先创建了 RTPBundleTransport
对象,然后又调用了该对象的 Init方法,其代码如下:
int RTPBundleTransport::Init()
{
int retries = 0;
sockaddr_in recAddr;
//Clear addr
memset(&recAddr,0,sizeof(struct sockaddr_in));
//Init ramdon
srand (time(NULL));
//Set family
recAddr.sin_family = AF_INET;
//Get two consecutive ramdom ports
while (retries++<100)
{
...
//Create new sockets
socket = ::socket(PF_INET,SOCK_DGRAM,0);
//Get random
port = (RTPTransport::GetMinPort()+(RTPTransport::GetMaxPort()-RTPTransport::GetMinPort())*double(rand()/double(RAND_MAX)));
//Make even
port &= 0xFFFFFFFE;
//Try to bind to port
recAddr.sin_port = htons(port);
//Bind the rtp socket
if(bind(socket,(struct sockaddr *)&recAddr,sizeof(struct sockaddr_in))!=0)
//Try again
continue;
...
Start();
//Done
Log("<RTPBundleTransport::Init()\n");
//Opened
return port;
}
//Error
Error("-RTPBundleTransport::Init() | too many failed attemps opening sockets\n");
//Failed
return 0;
}
通过上面的代码我们可以看到, 在Init
函数中,主要做了两件事儿:
- 绑定端口号,打开sokcet服务。
- 通过 Start() 启动了一个新的线程。对于线程的启动与所做的事情我们将在下篇文章中再做介绍。
CandidateInfo
在另外一个称为 semantic-sdp-js
的项目中的lib中可以找到 CandidateInfo 类的定义。其构造函数如下:
/**
* CanditateInfo constructor
* @constructor
* @alias CandidateInfo
* @param {String} foundation
* @param {Number} componentId
* @param {String} transport
* @param {Number} priority
* @param {String} address
* @param {Number} port
* @param {String} type
* @param {String} relAddr
* @param {String} relPort
*/
constructor(foundation, componentId, transport, priority, address, port, type, relAddr, relPort) {
this.foundation = foundation;
this.componentId = componentId;
this.transport = transport;
this.priority = priority;
this.address = address;
this.port = port;
this.type = type;
this.relAddr = relAddr;
this.relPort = relPort;
}
其代码非常简单,就是将传入的一些参数保存起来。