4)STUN Message Type
- request
- success response
- failure response
- indication
1、A向S發出Allocate Request,請求S在自己的IP地址上為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);
*/