RFC5245 ICE学习笔记1

ICE学习笔记 -- RFC 5245

RFC 5245 ICE  

1, offer/answer model

2, ICE Step:

   1) 产生候选地址(1.公网 2.NAT反射 3.Relay转发地址) Generate candidates (host candidates, server reflexive candidates, peer reflexive candidates, and relayed candidates)

   2) 本地对端交换候选地址,完成连接性检查。(用RTP包,一个四次握手的过程,如下图:)

        

   3) 根据优先级进行排序                              Sorting Candidates

   4) 冻结候选地址                                    Frozen Candidates

   5) 安全性检查                                      Security for Checks

   6) 结束ICE过程                                     Concluding ICE

3. STUN  (RFC 5389)

   1) binding message format

   2) binding request/binding response

   3) 目前定义了三种STUN用途:

      Interactive Connectivity Establishment(ICE)[MMUSIC-ICE],交互式连接建立

      Client-initiated connections for SIP [SIP-OUTBOUND],用于SIP的客户端初始化连接

      NAT Behavior Discovery [BEHAVE-NAT],NAT行为发现 

   4)STUN Message Type

      - request
      - success response
      - failure response
      - indication  

4. TURN (RFC 5766) (TURN协议的缺点是服务器负载太高,容易成为性能瓶颈)

   1)Relayed Transport Address:TURN服务器上的传输地址,用于客户端和对端中继数据。

     TURN Server Transport Address:TURN服务器上的传输地址,用于客户端发送STUN消息给服务器。

     Peer Transport Address:服务器看到的对端的传输地址,当对端是在NAT后面,则是对端的服务器反射传输地址。

     Allocation:通过Allocate请求将中继传输地址提供给客户端,除了中继状态外,还有许可和超时定时器等。

     5-tuple:五元组,包括客户端IP地址和端口,服务器IP地址和端口和传输协议(包括UDP、TCP、TLS)的组合。

     Channel:通道号与对端传输地址的关联,一旦一个通道号与一个对端的传输地址绑定,客户端和服务器就能够利用带宽效应更大的通道数据消息来交换数据。

     Permission:一个对端允许使用它的IP地址和传输协议来发送数据到TURN服务器,服务器只为从对端发来的并且匹配一个已经存在的许可的流量中继到相应的客户端。

     Realm:服务器内用于描述服务器或内容的一个字符串,这个realm告诉客户端哪些用户名和密码的组合可用于认证请求。

     Nonce:服务器随机选择的一个字符串,包含在报文摘要中。为了防止中继攻击,服务器应该有规律的改变这个nonce。

    

   2)方法:

     0x003    Allocate

     0x004    Refresh

     0x006    Send

     0x007    Data

     0x008    CreatePermission

     0x009    ChannelBind  

    1、A向S发出Allocate Request,请求S在自己的IP地址上为A分配一个端口。

    2、S收到A的Allocate请求后,为A分配一个端口aport。并向A返回一个Allocate Response。

    3、A向S发出Channel Bind请求,请求将B的(IP地址UDP端口)对绑定到一个Channel号ano上。

    4、S收到Channel Bind请求后,将Channel号ano和B的(IP地址UDP端口)对绑定,并向A返回一个Channel Bind Success回复。

    5、之后A可以用Channel Data命令通过Channel号ano向B发消息。Channel Data命令实际上将消息发给了S,S再通过为A分配的端口aport向B转发。

    6、B收到的A的消息中,源地址显示的是S为A分配的中转地址(S的IP地址:aport),B可以直接向这个中转地址发消息,S会将其转发给A。

    

5. 开源实现 https://github.com/NATTools

ICELIB_INSTANCE *m_iceInst;
static ICELIB_Result OnConnectivityChecksComplete(void *pUserData, uint32_t userValue1, bool isControlling, bool iceFailed);
static ICELIB_Result OnOutgoingBindingRequest(void *pUserData, const struct sockaddr *destination,
const struct sockaddr *source, uint32_t transport, uint32_t userValue1, uint32_t userValue2,
uint32_t componentId, bool useRelay, const char *pUfragPair, const char *pPasswd,
uint32_t peerPriority, bool useCandidate, bool iceControlling, bool iceControlled,
uint64_t tieBreaker, StunMsgId transactionId, const char *szFingerPrint);
static ICELIB_Result OutgoingBindingResponse(void *pUserData, uint32_t userValue1,
uint32_t userValue2, uint32_t componentId, const struct sockaddr *source,
const struct sockaddr *destination, const struct sockaddr *MappedAddress,
uint16_t errorResponse, StunMsgId transactionId, bool useRelay, const char *pUfragPair,
const char *pPasswd);
/*
static ICELIB_Result OnSendKeepAlive(void *pUserData, uint32_t userValue1, uint32_t userValue2,
uint32_t mediaIdx);
*/

6. wilson同学的分享记录

ICE

1. 什么是NAT?(网络地址转换)
2. NAT带来的问题(连通性问题,并没有定义或推荐公网或私网IP地址影射的方法,及互相通迅的问题,完全由应用自己解决)
3. STUN解决的问题(解决两个NAT之后设备的连通)
4. 源地址、目标地址、源端口,目标端口,协议 5个元素
5. NAT暴力猜测方法,两边各猜64个连接,连通的概率就很大了
6. ALG,根据SDP里的信息,帮你分配端口,路由器要支持SDP,且SDP不能被加密
7. Connectivity check,可以做很多并行的尝试,间隔一定的delay,按优先级发包
8. STUN包做了扩展。PRIORITY, USE-CANDIDATE, ICE-CONTROLLED, and ICE-CONTROLLING
9. Nominate的过程:(Nomination 一直在做,priority每过100毫秒加100,达到1000,即可以conclude.)
收到包以后,好几个都通了,就需要选择一个。Aggressive/Regular nomination, Aggressive nomination已经淘汰,因为协议有bug。
Aggressive 本来是想更快的建立连接,后来大家发现Regular完全可以做到同样的快, Early media。

Regular Nominate过程,发的STUN包有两种包,USE-CANDIDATE=0(Nominate前), USE-CANDIDATE=1(Nominate后)
两个人,不能两个人都Nominate, 要选择一个主动,一个被动。SDP里面有个属性,叫ICE-CONTROLLING(主动), ICE-CONTROLLED(被动)。
被动的一方收到response,看到USE-CANDIDATE=1,就知道了选择好了,以后用这个transport来通讯。
角色的选择,第一个STUN包的里时候,里面有个tie-breaking,是一个64bit的随机数,谁的大谁就做CONTROLLING,另一个做CONTROLLED

10. LITE实现(当做为服务器时,有公网IP的情况下,申明我是LITE,就不需要connectivity check,由client来check)
firefox 对LITE支持的不太好,chrome还可以。

11. Restart/Reconnect
12. Keep live(定时,client发一个包,server回一个包,做有效性检查)
13. Conclude:(为什么Aggressive Nominate不需要了,是因为有Early media)
Early media就是说,有一个transport连通以后,就可以发送一些数据回去,所以只要你发,对方肯定能收的到.
因为这时候还没有Conclue,一旦conclue这后,非nominate的transport会被关闭。
Conclude,是为了保证RTP的transport是symmetric的,即发过去和发过来的transport是同一个。
14. ICE, RTP, DTLS是复用在同一个transport上的。
15. AddMediaStream,一个SDP里面有多个Media(Audio/Video),要建多条连接,一个MediaStream里面有两个component(RTP1 /RTCP 2).
一个session里面有多个MediaStream(Audio/Video/Sharing)。
我们ICE session里面的实现,三个是分开的,一个Media就用了一个Session,是觉得放在一个session里,可以做优化的不多。
Audio/Video/Sharing各有一个ICEConnector,各用一个transport。

16. 优先级 0123456 Nattool里fundation有bug(不同的IP用相同的fundation).
17. OnICEComplete会来两回,第一次是Early meida(最早的连通的一个transport), 第二次是Conclude. bUpdate=0 bUpdate=1。


ICE

//
//----- ICE configuration data
//
typedef struct {
unsigned int tickIntervalMS;
unsigned int keepAliveIntervalS;
unsigned int maxCheckListPairs;
bool aggressiveNomination;
bool iceLite;
ICELIB_logLevel logLevel;
} ICELIB_CONFIGURATION;


//
//----- ICE instance data
//
typedef struct tag_ICELIB_INSTANCE {
ICELIB_STATE iceState;
ICELIB_CONFIGURATION iceConfiguration;
ICELIB_CALLBACKS callbacks;
ICE_MEDIA localIceMedia;
ICE_MEDIA remoteIceMedia;
bool iceControlling;
bool iceControlled;
bool iceSupportVerified;
uint64_t tieBreaker;
ICELIB_STREAM_CONTROLLER streamControllers[ ICE_MAX_MEDIALINES];
unsigned int numberOfMediaStreams;
unsigned int roundRobinStreamControllerIndex;
uint32_t tickCount;
uint32_t keepAliveTickCount;
} ICELIB_INSTANCE;

/*!
* ICE single candidate
*
* From draft-ietf-mmusic-ice-18:
*
* foundation = 1*32 ice-char
* componentid = 1*5 digit (0..65535)
* priority = 1*10 digit (0..2147483647)
* connectionAddr = address including port
* relAddr = host addres when sending relayed candidates (Optional, used for debugging)
*/
typedef struct {
char foundation[ ICE_MAX_FOUNDATION_LENGTH];
uint32_t componentid;
uint32_t priority;
struct sockaddr_storage connectionAddr;
ICE_CANDIDATE_TYPE type;
struct sockaddr_storage relAddr;
uint32_t userValue1;
uint32_t userValue2;
uint32_t transport;
char fingerprint[ICE_MAX_FINGERPRINT];
} ICE_CANDIDATE;

typedef struct {
uint32_t componentId;
struct sockaddr_storage connectionAddr;
ICE_CANDIDATE_TYPE type;
} ICE_REMOTE_CANDIDATE;

typedef struct {
ICE_REMOTE_CANDIDATE remoteCandidate[ICE_MAX_COMPONENTS];
uint32_t numberOfComponents;
} ICE_REMOTE_CANDIDATES;

/*!
* ICE candidates for a single media stream
*/
typedef struct {
char ufrag [ ICE_MAX_UFRAG_LENGTH];
char passwd [ ICE_MAX_PASSWD_LENGTH];
ICE_CANDIDATE candidate[ ICE_MAX_CANDIDATES];
uint32_t numberOfCandidates;
ICE_TURN_STATE turnState;
uint32_t userValue1;
uint32_t userValue2;
struct sockaddr_storage defaultAddr;
ICE_CANDIDATE_TYPE defaultCandType;
} ICE_MEDIA_STREAM;

ICELIB_INSTANCE
ICELIB_Constructor
ICELIB_Destructor
ICELIB_Start
ICELIB_Stop
ICELIB_ReStart
ICELIB_Tick

ICELIB_setCallbackLog

ICELIB_addLocalMediaStream
ICELIB_setLocalMediaStream
ICELIB_getLocalMediaStream

ICELIB_addRemoteMediaStream
ICELIB_getRemoteMediaStream

ICELIB_setCallbackConnecitivityChecksComplete
ICELIB_setCallbackOutgoingBindingRequest
ICELIB_setCallbackOutgoingBindingResponse
ICELIB_setCallbackKeepAlive
ICELIB_doKeepAlive

ICELIB_addLocalCandidate
ICELIB_getActiveCandidate
ICELIB_getActiveRemoteCandidates

ICELIB_incomingBindingRequest
ICELIB_isIceComplete
ICELIB_isRunning
ICELIB_incomingBindingResponse

ICELIB_getRemoteComponentId
ICELIB_generateTransactionId


ICELIBTYPES_ICE_CANDIDATE_TYPE_toString

Frozen已经废弃不用了


ICELIB_setCallbackConnecitivityChecksComplete(m_iceInst, OnConnectivityChecksComplete, this);
ICELIB_setCallbackOutgoingBindingRequest(m_iceInst, OnOutgoingBindingRequest, this);
ICELIB_setCallbackOutgoingBindingResponse(m_iceInst, OutgoingBindingResponse, this);

static ICELIB_Result OnConnectivityChecksComplete(void *pUserData, uint32_t userValue1, bool isControlling, bool iceFailed);
//onicecomplete, aReason

static ICELIB_Result OnOutgoingBindingRequest(void *pUserData, const struct sockaddr *destination,
const struct sockaddr *source, uint32_t transport, uint32_t userValue1, uint32_t userValue2,
uint32_t componentId, bool useRelay, const char *pUfragPair, const char *pPasswd,
uint32_t peerPriority, bool useCandidate, bool iceControlling, bool iceControlled,
uint64_t tieBreaker, StunMsgId transactionId, const char *szFingerPrint);
//start connectivity checking, a Request --->> a check.



static ICELIB_Result OutgoingBindingResponse(void *pUserData, uint32_t userValue1,
uint32_t userValue2, uint32_t componentId, const struct sockaddr *source,
const struct sockaddr *destination, const struct sockaddr *MappedAddress,
uint16_t errorResponse, StunMsgId transactionId, bool useRelay, const char *pUfragPair,
const char *pPasswd);

//有一个response回来可以做early media

conclude at last, select a transport

/*
static ICELIB_Result OnSendKeepAlive(void *pUserData, uint32_t userValue1, uint32_t userValue2,
uint32_t mediaIdx);
*/

猜你喜欢

转载自blog.csdn.net/hyl999/article/details/108993308
rfc
ice
今日推荐