cdsn博客不支持word文件,所以這里顯示不完全。可到本人資源中下載word文檔:
v0.3:http://download.csdn.net/detail/kl222/6961491
v0.1:http://download.csdn.net/detail/kl222/6677635
下載完后評論,可以返還你的積分。此文檔還在完善中,歡迎大家交流,共同完善。
Webrtc 教程
|
|
版本0.3(2014年2月)
康林 (16614119@qq.com)
本文博客地址:http://blog.csdn.net/kl222/article/details/17198873
版本 |
內容 |
時間 |
作者 |
V0.1 |
初建文檔 |
2013年11月 |
康林 |
V0.2 |
增加協議文檔 |
2014年2月8日 |
康林 |
V0.3 |
細化了windows下編譯過程。 增加IDE工具用法。 重新排版整個文檔。 |
2014年2月20日 |
康林 |
2.1.3 編譯Android(只能在linux下):.20
(4) 音頻引擎(VoiceEngine)模塊 APIs.24
(5) 視頻引擎(VideoEngine)模塊 APIs.25
3.2.3.1 libjingle_peerconnection.26
3.2.3.2 libjingle_media庫:...30
4.2.2 XMPP Messaging Component.模塊...36
4.2.3 Session Logic and management commponent模塊。...37
4.2.4 Peer to peer Component模塊。...37
6.4 交互式連接建立(InteractiveConnectivity Establishment),一種綜合性的NAT穿越的技術。 62
7.8 一個開源的ICE庫——libnice介紹...111
7.9 4種利用TURN穿越對稱型NAT方案的設計與實現...113
7.10 基於ICE方式SIP信令穿透Symmetric_NAT技術研究...114
chromium自己整了一套開發工具系統,原來叫gclient(名字好像讓位給google桌面客戶端了) ,現在改名depot_tools。
Wrapper script for checking out and updating source code frommultiple SCM repository locations.
chromium使用了(目前 @159834)107個代碼倉庫的代碼,這些分散在多個代碼倉庫,chromiun不需要某些倉庫的東西,google就封裝個工具,這個工具既支持svn,也支持git,不光能down代碼,也支持了
· patch
· cpplint,pylint
· apply_issue
· junction
· codereview
· 更新chromium代碼
· 生成工程文件,windows上生產sln,mac生產xcode工程,linux生成scons或者makefile
· 其他的patch,codereview,管理分散開發人員的修改
http://www.chromium.org/developers/how-tos/depottools
sudo apt-getinstall git
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
l 已裝cygwin:
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
l 無cygwin:
https://src.chromium.org/svn/trunk/tools/depot_tools.zip
· 首先會更新 depot_tools,有兩種 bat和sh,目的都一樣
更新depot_tools,然后運行python版gclient.py,參數都傳給gclient.py
這里解決了雞生蛋還是蛋生雞的問題,更新了gclient.py
· 生成.gclient文件,gclient指定了某個版本的chromium·代碼
· 執行gclient sync,更新代碼,生成工程文件,這里使用了另一個工具 GYP
Commands are:
cleanup Cleans up allworking copies.
config Create a.gclient file in the current directory.
diff Displayslocal diff for every dependencies.
fetch Fetchesupstream commits for all modules.
help Prints listof commands or help for a specific command.
hookinfo Output the hooksthat would be run by `gclient runhooks`
pack Generate apatch which can be applied at the root of the tree.
recurse Operates on allthe entries.
revert Revert allmodifications in every dependencies.
revinfo Output revisioninfo mapping for the client and its dependencies.
runhooks Runs hooks for filesthat have been modified in the local working copy.
status Showmodification status for every dependencies.
sync Checkout/update all modules.
update Alias for thesync command. Deprecated.
Prints list of commands or help for aspecific command.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-j JOBS, --jobs=JOBS Specifyhow many SCM commands can run in parallel;
default=8
-v, --verbose Produces additional output for diagnostics. Can be
used up to three times for morelogging info.
--gclientfile=CONFIG_FILENAME
Specify an alternate .gclient file
--spec=SPEC create a gclient file containing the provided string.
Due to Cygwin/Python brokenness, itprobably can't
contain any newlines.
http://www.chromium.org/developers/how-tos/install-depot-tools
主要是寫
.gclient和DEPS python語法(精確點就是json語法+“#”型注釋,list最末元素可以有,執行時使用python的eval來解釋的)
.gclient
Gyp工具簡介見附件。Google自己搞的玩意。Webrtc不是直接用的gyp,而是又封裝了一下。
Webrtc中的gyp工具是build/ gyp_chromium
整個webrtc工程是由python程序進行維護。而整個webrtc工程現在處理一個完全不穩定的階段。所以需要了解一些基本的python語法知識。
工欲善其事,必先利其器。所以下面介紹下本地集成開發環境。
一般的在windows本地開發環境(IDE)是visualstudio。然而webrtc默認的是ninja。雖然可以手工生成vs工程,但是經常出現兼容性問題。所以下面介紹下vs加ninja開發環境。
Webrtc默認生成的工程位於源碼根目錄下的out目錄中。
打開vs2012:
官網地址:http://kdevelop.org/
它是一個跨平台開源的集成開發環境。用c++開發。所以效率比較高。本人比較喜歡用它在linux下開發,或用它開發跨平台程序。它默認支持ninja。
官網地址:http://eclipse.org/
Eclipse是一個用java寫的跨平台集成開發環境。支持java、c、c++等。它由於是用java開發的,所以效率比用c、c++開發的低。如果你的機器比較好,用這個也挻文便的。用於android開發。
原文地址:http://www.webrtc.org/reference/getting-started
注意:由於天朝的限制,所以下載是一個艱難的過程(最好翻牆)。由於google采用ninja做為默認編譯工程。所以當你用其它本地建立方式(VS、make等)時,你要自己解決兼容問題。關於建立工具、編譯工具的版本,最好采用最新的版本,否則也會出現兼容性問題。
相關工具安裝,最好采用默認值。否則也可能出現兼容問題。
1) 安裝vs2012旗艦版
http://www.microsoft.com/zh-cn/download/details.aspx?id=30678
注冊號:
Visual Studio 2012 Ultimate旗艦版序列號:
YKCW6-BPFPF-BT8C9-7DCTH-QXGWC
RBCXF-CVBGR-382MK-DFHJ4-C69G8
YQ7PR-QTHDM-HCBCV-9GKGG-TB2TM
建立用最新版本vs,vs2008以前版本不能保證百分之百兼容。因為開發者用的是最新版本。
2) 安裝依賴庫:
a) Windows SDK(主要是需要DirectX sdk,主要是捕獲、Render時用的是DirectX)
windows sdk介紹:
官網:
http://msdn.microsoft.com/zh-cn/windows/bb980924.aspx
從 Windows 7開始,DirectX SDK 就作為 Windows SDK 的一部分包含在內。
注意:然而webrtc現在用的是directx9.0 sdk。所以只要在下面地址下載:
http://www.microsoft.com/en-us/download/details.aspx?id=6812
b) Windows DDK(好象可以不用安裝)
從windows vista開始,ddk改成wdk。
官網:http://msdn.microsoft.com/zh-cn/windows/hardware/gg454513
Vista以后的版本,可以用最新的windowswdk 8.0
如果是windows xp,則需要wdk7.1.0以前的版本。
3) 下載depot_tools工具
a) 先裝cygwin:
git clonehttps://chromium.googlesource.com/chromium/tools/depot_tools.git
b) 無cygwin:
https://src.chromium.org/svn/trunk/tools/depot_tools.zip
4) 把路徑設置到環境變量PATH中。
5) 下載webrtc代碼,最好選擇穩定代碼下載,trunk是當前開發代碼庫。
6) 配置下載代碼庫:
E:\source\muli\google>gclientconfighttp://webrtc.googlecode.com/svn/trunk/
這一步主要下載git、svn、python和配置文件.gconfig。
默認配置下載與平台相應的代碼,如果要下其它平台代碼。修改.gconfig文件,加入target_os = ['windows','android','unix']
7) 設置產生者和版本號。此步可選
E:\source\muli\google\trunk>setGYP_GENERATORS=msvs #設置產生者(可選)
指定工程文件類型,如果沒有這一步,默認使用ninja
make forMakefiles
msvs forVisual Studio
msvs-ninja forVisual Studio project building with ninja
xcode for Xcode
(Windows: ninja/Visual Studio, OSX: ninja/XCode, Linux: ninja/make, Android: ninja)
E:\source\muli\google\trunk>setGYP_MSVS_VERSION=2012 #設置vs產生者版本(可選)
8) 同步源代碼。
E:\source\muli\google>gclient sync --force #同步源碼
更新depot_tools工具、git、svn、python工具、下載webrtc代碼及相關工具,有1G多大小。注意:如果下載中卡住了,需要翻牆。
這步完成時,會自動調用gyp產生工程。如果沒有設置前面兩步,則默認的為ninja工程。
(Windows: ninja/Visual Studio, OSX: ninja/XCode, Linux: ninja/make, Android: ninja)
如果用默認工程,就可以直接到第10)操作。
9) 重新產生工程
E:\source\muli\google>gclient runhooks --force #運行hooks,重新產生工程
10) 手工生成工程
E:\source\muli\google\trunk>python build\gyp_chromium.py--depth . -G msvs_version=2012 all.gyp #手工產生指定版本的工程
在trunk目錄下生all.sln
-Gmsvs_version :vs的版本號。產生者由環境變量GYP_GENERATORS設置
--generator-output:工程產生目錄,但是生成出來的工程有問題,所以不選。
Msvs版本:建議用最新的版本號,因為開發者用的是最近的版本,所以老版本的兼容性可能會有問題。
11) 編譯:
用vs2012打開工程文件all.sln 。
用ninja編譯
ninja -C out/Debug All
12) 編譯時可能出現的問題:
問題一:
________ running'C:\openmeetings\google\depot_tools\python_bin\python.exe trunk
/webrtc/build/gyp_webrtc-Dextra_gyp_flag=0' in 'C:\openmeetings\google'
ERROR at//build/config/win/visual_studio_version.gni:33:24: Script returned non
-zero exit code.
visual_studio_path =exec_script("get_visual_studio_path.py",
^----------
Current dir:C:\openmeetings\google\trunk\out\gn_build.Debug64\
Command:"C:\openmeetings\google\depot_tools\python_bin\python.exe""C:\openmeet
ings\google\trunk\build\config\win\get_visual_studio_path.py"auto
Returned 1 andprinted out:
原因是由於沒有安裝visual studio引起的。解決方法是安裝vs2012。
問題三:
E:\source\muli\google\trunk>pythonbuild\gyp_chromium.py --depth . -G msvs_versi
on=2012 all.gyp
Traceback (most recent call last):
File "build\gyp_chromium.py", line 18, in <module>
execfile(os.path.join(path, 'gyp_chromium'))
File "E:\source\muli\google\trunk\build\gyp_chromium", line42, in <module>
import find_depot_tools
ImportError: No module namedfind_depot_tools
原因是開發人員把這個模塊給刪掉了。但不知道為什么這么明顯的BUG卻沒有測試到並修復?也許是開發人員從不用第10步生成工程。Webrtc代碼處理開發階段,很不穩定。在項目管理方面比其它開源項目(例如:ffmpeg、vlc、libnice)差很多。當然相應不久將來,這個BUG也許會被修復。
解決方法:
在gyp_chromium.py42行前加入下面行。
sys.path.insert(1,os.path.join(chrome_src, 'webrtc', 'build'))
從下面地址下載find_depot_tools工具到trunk\webrtc\build 下:https://github.com/adobe/chromium/blob/master/tools/find_depot_tools.py
問題二:
用vs2008編譯,出現下面問題:
3>.\video_coding\main\source\receiver.cc(159): error C2668: “abs”:對重載函數的調用不明確
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(539):可能是“long double abs(long double)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(491):或 “float abs(float)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(487):或 “double abs(double)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(485):或 “long abs(long)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\stdlib.h(380):或 “int abs(int)”
3> 試圖匹配參數列表“(const int64_t)”時
3>.\video_coding\main\source\receiver.cc(164): error C2668: “abs”:對重載函數的調用不明確
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(539):可能是“long double abs(long double)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(491):或 “float abs(float)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(487):或 “double abs(double)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\math.h(485):或 “long abs(long)”
3> C:\Program Files\Microsoft VisualStudio 9.0\VC\include\stdlib.h(380):或 “int abs(int)”
以上問題是由於vs2008對C99支持不夠,用最新版本vs2012就可以解決。也可以修改代碼進行類型強制轉換。
1) 安裝depot_tools:
svnco http://src.chromium.org/svn/trunk/tools/depot_tools
或者:
git clonehttps://chromium.googlesource.com/chromium/tools/depot_tools.git
我的depot_tools下到了 /data/google/depot_tools 中。
2) 設置環境變量,把這個目錄加入到PATH中:
exportPATH=$PATH:/data/google/depot_tools
3) 下載webrtc代碼,最好選擇穩定代碼下載,trunk是當前開發代碼庫。
gclient config http://webrtc.googlecode.com/svn/trunk/(生成.gconfig文件)
默認配置下載與平台相應的代碼,如果要下其它平台代碼。修改.gconfig文件,加入target_os = ['windows','android','unix']
gclientsync --force(同步項目文件,要下載1個多G的文件,網速不好的,可以去玩一會再回來),注意:如果下載中卡住了,需要翻牆。
gclientrunhooks --force (重新生成相應的工程文件,Linux的MakeFile文件)
4) 安裝依賴開發庫:
apt-get installlibasound2-dev
apt-get install libpulse-dev
apt-get install libx11-dev
apt-get install libxext-dev
apt-get install libnss3-dev
5) 生成工程文件
a) 生成make工程
k@k-C410:/data/google/webrtc/trunk$export GYP_GENERATORS=make
指定工程文件類型,如果沒有這一步,默認使用ninja
make forMakefiles
msvs forVisual Studio
msvs-ninja forVisual Studio project building with ninja
xcode for Xcode
k@k-C410:/data/google/webrtc/trunk$build/gyp_chromium --depth=. all.gyp
如果你沒有安裝依賴庫,可能會出現下面錯誤:
Updating projects from gypfiles...
Package nss was not found inthe pkg-config search path.
Perhaps you should add thedirectory containing `nss.pc'
to the PKG_CONFIG_PATHenvironment variable
No package 'nss' found
gyp: Call to 'pkg-config--libs-only-L --libs-only-other nss' returned exit status 1. while loadingdependencies of all.gyp while trying to load all.gyp
安裝libnss庫:
k@k-C410:/data/google/webrtc/trunk$sudo apt-get install libnss3-dev
________ running'/usr/bin/python trunk/webrtc/build/gyp_webrtc -Dextra_gyp_flag=0' in'/home/google'
Generating gypfiles from GN...
Updating projectsfrom gyp files...
Package gconf-2.0was not found in the pkg-config search path.
Perhaps you shouldadd the directory containing `gconf-2.0.pc'
to thePKG_CONFIG_PATH environment variable
No package'gconf-2.0' found
gyp: Call to'pkg-config --cflags gconf-2.0' returned exit status 1.
Error: Command/usr/bin/python trunk/webrtc/build/gyp_webrtc -Dextra_gyp_flag=0 returnednon-zero exit status 1 in /home/google
安裝gconf-2.0庫:
k@k-C410:/home/google$ sudoapt-get install gconf-2.0
然后再生成編譯工程:
k@k-C410:/data/google/webrtc/trunk$build/gyp_chromium --depth=. all.gyp
在當前目錄下產生Makefile 文件。
編譯:
k@k-C410:/data/google/webrtc/trunk$make peerconnection_server
k@k-C410:/data/google/webrtc/trunk$make peerconnection_client
生成的文件放在out目錄下。
k@k-C410:/data/google/webrtc/trunk/out/Debug$ls
genmacro libvpx_obj_int_extract obj.target re2c
genmodule libyuv.a peerconnection_client yasm
genperf linker.lock peerconnection_server
genstring obj protoc
genversion obj.host pyproto
你可以看到:
peerconnection_client、 peerconnection_server兩個應用程序。
運行:
啟動服務器:
k@k-C410:/data/google/webrtc/trunk/out/Debug$./peerconnection_server
Server listening on port 8888
啟動客戶端:
k@k-C410:/data/google/webrtc/trunk/out/Debug$./peerconnection_client
也可以用trunk/talk/examples/peerconnection/server/server_test.html目錄下的頁面進行測試。
a) 生成默認工程,默認為ninja
k@k-C410:/data/google/webrtc/trunk$build/gyp_chromium --depth=. all.gyp
如果你沒有安裝依賴庫,可能會出現下面錯誤:
Updating projectsfrom gyp files...
Package nss wasnot found in the pkg-config search path.
Perhaps you shouldadd the directory containing `nss.pc'
to thePKG_CONFIG_PATH environment variable
No package 'nss'found
gyp: Call to'pkg-config --libs-only-L --libs-only-other nss' returned exit status 1. whileloading dependencies of all.gyp while trying to load all.gyp
安裝libnss庫:
k@k-C410:/data/google/webrtc/trunk$sudo apt-get install libnss3-dev
然后再生成編譯工程:
k@k-C410:/data/google/webrtc/trunk$build/gyp_chromium --depth=. all.gyp
在當前目錄下產生out目錄,在out目錄中有Debug、Release兩個子目錄,在子目錄中有ninja工程文件build.ninja
編譯指定的目標:
k@k-C410:/data/google/webrtc/trunk$ninja -C out peerconnection_server
k@k-C410:/data/google/webrtc/trunk$ ninja -C outpeerconnection_client
編譯所有工程目標:
k@k-C410:/data/google/webrtc/trunk$ ninja -C out All
在Debug下可以看到許多的測試程序。
設置環境變量:ANDROID_NDK_ROOT、ANDROID_SDK_ROOT、JAVA_HOME
k@k-C410:/data/google /webrtc/trunk$export JAVA_HOME=/data/jdk1.7.0_45
k@k-C410:/data/google/webrtc/trunk$exportANDROID_SDK_ROOT=/data/adt-bundle-linux-x86_64-20130917/sdk
k@k-C410:/data/google/webrtc/trunk$export ANDROID_NDK_ROOT=/data/android-ndk-r9
k@k-C410:/data/google/webrtc/trunk$source build/android/envsetup.sh
k@k-C410:/data/google/webrtc/trunk$ build/gyp_chromium --depth=. all.gyp#此步可省
k@k-C410:/data/google/webrtc/trunk$ ninja -C out/Debug All #此步可省
在編譯過程中會有些類實例調用靜態方法的警告錯誤。需要提示修改代碼。
生成所有程序。
k@k-C410:/data/google/webrtc/trunk/out/Debug$ls *.apk
AppRTCDemo-debug.apk OpenSlDemo-debug.apk WebRTCDemo-debug.apk
在Debug下可以看到許多的測試程序。
在編譯環境搭建完成后,可以開始干活了。我們用webrtc的主要目的是應用它的音/視頻捕獲、視頻顯示、音頻播放、音視頻壓縮、網絡通信。如果你只是想在HTML5中用音/視頻解決方案,可以跳過本教程,因為webrtc的最終目標是實現HTML5的音視頻解決方案。本教程主要講解在應用程序中如何使用webrtc庫。
XMPP協議主要用於解決獲取用戶列表、交換用戶數、信令交換。
現實網絡環境有三種情況:
1. 公共網絡:這類網絡IP之間可以不受限制的進行直接訪問。
2. NAT網絡:這類網絡主機在私有內網中,沒有單獨的公網IP。但可以通過打洞來找到它在公網中固定的網絡地址。STUN協議就是解決些網絡問題。
3. 嚴格的受限NAT網絡:這類網絡中的主機在私網內,只能單向訪問外網。外網不能直接訪問它。所以這類網絡需要通過在公共網絡上的TURN服務器來進行數據中轉。TRUN協議就是解決此網絡問題的。
為了解決地址轉換、防火牆限制訪問等問題。所以提供了ICE協議,ICE協議是一個框架,它依賴STUN協議解決完全錐形NAT、以及受限錐形NAT。TURN協議用於解決嚴格受限NAT網絡的問題。
(1)紫色部分是Web開發者API層;
(2)藍色實線部分是面向瀏覽器廠商的API層(本教程主要講解的部分)
(3)藍色虛線部分瀏覽器廠商可以自定義實現
Web開發者開發的程序,Web開發者可以基於集成WebRTC的瀏覽器提供的web API開發基於視頻、音頻的實時通信應用。
面向第三方開發者的WebRTC標准API(Javascript),使開發者能夠容易地開發出類似於網絡視頻聊天的web應用,最新的標准化進程可以查看這里。
本地C++ API層,使瀏覽器廠商容易實現WebRTC標准的Web API,抽象地對數字信號過程進行處理。主要是 PeerConnection 對象。
傳輸/會話層
會話層組件采用了libjingle庫的部分組件實現,無須使用xmpp/jingle協議。
參見:https://developers.google.com/talk/talk_developers_home
現在libjingle已經移到webrtc項目中維護了。位於源碼根目錄的 talk 目錄下。
a. RTPStack協議棧
Real Time Protocol
b. STUN/ICE
可以通過STUN和ICE組件來建立不同類型網絡間的呼叫連接。現實網絡環境有三種情況:
1. 公共網絡:這類網絡IP之間可以不受限制的進行直接訪問。
2. NAT網絡:這類網絡主機在私有內網中,沒有單獨的公網IP。但可以通過打洞來找到它在公網中固定的網絡地址。STUN協議就是解決些網絡問題
3. 嚴格的受限NAT網絡:這類網絡中的主機在私網內,只能單向訪問外網。外網不能直接訪問它。所以這類網絡需要通過在公共網絡上的服務器來進行數據中轉。TRUN協議就是解決此網絡問題的。
ICE是一個框架,在這個框架中,它會判斷主機是上面三種類型之一,並用相應的辦法來建立主機之間的連接。
c. Session Management
一個抽象的會話層,提供會話建立和管理功能。該層協議留給應用開發者自定義實現。
音頻引擎是包含一系列音頻多媒體處理的框架,包括從視頻采集卡到網絡傳輸端等整個解決方案。
PS:VoiceEngine是WebRTC極具價值的技術之一,是Google收購GIPS公司后開源的。在VoIP上,技術業界領先,后面的文章會詳細了解
a. iSAC
InternetSpeech Audio Codec
針對VoIP和音頻流的寬帶和超寬帶音頻編解碼器,是WebRTC音頻引擎的默認的編解碼器
采樣頻率:16khz,24khz,32khz;(默認為16khz)
自適應速率為10kbit/s ~ 52kbit/;
自適應包大小:30~60ms;
算法延時:frame + 3ms
b. iLBC
Internet Low Bitrate Codec
VoIP音頻流的窄帶語音編解碼器
采樣頻率:8khz;
20ms幀比特率為15.2kbps
30ms幀比特率為13.33kbps
標准由IETF RFC3951和RFC3952定義
c. NetEQ for Voice
針對音頻軟件實現的語音信號處理元件
NetEQ算法:自適應抖動控制算法以及語音包丟失隱藏算法。使其能夠快速且高解析度地適應不斷變化的網絡環境,確保音質優美且緩沖延遲最小。
是GIPS公司獨步天下的技術,能夠有效的處理由於網絡抖動和語音包丟失時候對語音質量產生的影響。
PS:NetEQ 也是WebRTC中一個極具價值的技術,對於提高VoIP質量有明顯效果,加以AEC\NR\AGC等模塊集成使用,效果更好。
d. Acoustic Echo Canceler (AEC)
回聲消除器是一個基於軟件的信號處理元件,能實時的去除mic采集到的回聲。
e. Noise Reduction (NR)
噪聲抑制也是一個基於軟件的信號處理元件,用於消除與相關VoIP的某些類型的背景噪聲(嘶嘶聲,風扇噪音等等… …)
WebRTC視頻處理引擎
VideoEngine是包含一系列視頻處理的整體框架,從攝像頭采集視頻到視頻信息網絡傳輸再到視頻顯示整個完整過程的解決方案。
a. VP8
視頻圖像編解碼器,是WebRTC視頻引擎的默認的編解碼器
VP8適合實時通信應用場景,因為它主要是針對低延時而設計的編解碼器。
PS:VPx編解碼器是Google收購ON2公司后開源的,VPx現在是WebM項目的一部分,而WebM項目是Google致力於推動的HTML5標准之一
b. Video Jitter Buffer
視頻抖動緩沖器,可以降低由於視頻抖動和視頻信息包丟失帶來的不良影響。
c. Image enhancements
圖像質量增強模塊
對網絡攝像頭采集到的圖像進行處理,包括明暗度檢測、顏色增強、降噪處理等功能,用來提升視頻質量。
WebRTC重用了libjingle的一些組件,主要是network和transport組件,關於libjingle的文檔資料可以查看這里。參見:https://developers.google.com/talk/talk_developers_home
現在這個模塊已經放到webrtc中進行維護了。位於源碼根目錄的talk目錄下。
代碼位於源碼根目錄的talk\app\webrtc 目錄下。webrtc封裝了peerconnection 對象,這個對象把流媒體傳輸也封裝進去了。
PeerConnection類廠接口:
//PeerConnectionFactoryInterface is the factory interface use for creating
//PeerConnection, MediaStream and media tracks.
//PeerConnectionFactoryInterface will create required libjingle threads,
//socket and network manager factory classes for networking.
//If an application decides to provide its own threads and network
//implementation of these classes it should use the alternate
//CreatePeerConnectionFactory method which accepts threads as input and use the
//CreatePeerConnection version that takes a PortAllocatorFactoryInterface as
//argument.
PeerConnectionFactoryInterface
PeerConnectionInterface接口:
peerconnection API:talk/app/webrtc/peerconnectioninterface.h
webrtc本地API:talk/app/webrtc/mediastreaminterface.h
常量\VideoEngine\VoiceEngine
注意:以下所有的方法、類、結構體、枚舉常量等都在webrtc命名空間里
類、結構體、枚舉常量 |
頭文件 |
說明 |
Structures |
common_types.h |
Lists the structures common to the VoiceEngine & VideoEngine |
Enumerators |
common_types.h |
List the enumerators common to the VoiceEngine & VideoEngine |
Classes |
common_types.h |
List the classes common to VoiceEngine & VideoEngine |
class VoiceEngine |
voe_base.h |
How to allocate and release resources for the VoiceEngine using factory methods in the VoiceEngine class. It also lists the APIs which are required to enable file tracing and/or traces as callback messages |
class VideoEngine |
vie_base.h |
How to allocate and release resources for the VideoEngine using factory methods in the VideoEngine class. It also lists the APIs which are required to enable file tracing and/or traces as callback messages |
下表列的是目前在 VoiceEngine中可用的sub APIs
sub-API |
頭文件 |
說明 |
VoEAudioProcessing |
voe_audio_processing.h |
Adds support for Noise Suppression (NS), Automatic Gain Control (AGC) and Echo Control (EC). Receiving side VAD is also included. |
VoEBase |
voe_base.h |
Enables full duplex VoIP using G.711. |
VoECallReport |
voe_call_report.h |
Adds support for call reports which contains number of dead-or-alive detections, RTT measurements, and Echo metrics. |
VoECodec |
voe_codec.h |
Adds non-default codecs (e.g. iLBC, iSAC, G.722 etc.), Voice Activity Detection (VAD) support. |
VoEDTMF |
voe_dtmf.h |
Adds telephone event transmission, DTMF tone generation and telephone event detection. (Telephone events include DTMF.) |
VoEEncryption |
voe_encryption.h |
Adds external encryption/decryption support. |
VoEErrors |
voe_errors.h |
Error Codes for the VoiceEngine |
VoEExternalMedia |
voe_external_media.h |
Adds support for external media processing and enables utilization of an external audio resource. |
VoEFile |
voe_file.h |
Adds file playback, file recording and file conversion functions. |
VoEHardware |
voe_hardware.h |
Adds sound device handling, CPU load monitoring and device information functions. |
VoENetEqStats |
voe_neteq_stats.h |
Adds buffer statistics functions. |
VoENetwork |
voe_network.h |
Adds external transport, port and address filtering, Windows QoS support and packet timeout notifications. |
VoERTP_RTCP |
voe_rtp_rtcp.h |
Adds support for RTCP sender reports, SSRC handling, RTP/RTCP statistics, Forward Error Correction (FEC), RTCP APP, RTP capturing and RTP keepalive. |
VoEVideoSync |
voe_video_sync.h |
Adds RTP header modification support, playout-delay tuning and monitoring. |
VoEVolumeControl |
voe_volume_control.h |
Adds speaker volume controls, microphone volume controls, mute support, and additional stereo scaling methods. |
下表列的是目前在VideoEngine中可用的sub APIs
sub-API |
頭文件 |
說明 |
ViEBase |
vie_base.h |
Basic functionality for creating a VideoEngine instance, channels and VoiceEngine interaction. NOTE: This API must always be created. |
ViECapture |
vie_capture.h |
Adds support for capture device allocation as well as capture device capabilities. |
ViECodec |
vie_codec.h |
Adds non-default codecs, codec settings and packet loss functionality. |
ViEEncryption |
vie_encryption.h |
Adds external encryption/decryption support. |
ViEErrors |
vie_errors.h |
Error codes for the VideoEngine |
ViEExternalCodec |
vie_external_codec.h |
Adds support for using external codecs. |
ViEFile |
vie_file.h |
Adds support for file recording, file playout, background images and snapshot. |
ViEImageProcess |
vie_image_process.h |
Adds effect filters, deflickering, denoising and color enhancement. |
ViENetwork |
vie_network.h |
Adds send and receive functionality, external transport, port and address filtering, Windows QoS support, packet timeout notification and changes to network settings. |
ViERender |
vie_render.h |
Adds rendering functionality. |
ViERTP_RTCP |
vie_rtp_rtcp.h |
Adds support for RTCP reports, SSRS handling RTP/RTCP statistics, NACK/FEC, keep-alive functionality and key frame request methods. |
webRTC本地API:http://www.webrtc.org/reference/native-apis。
翻譯:http://www.cnblogs.com/longrenle/archive/2012/03/04/2378433.html 。
webRTC本地API是基於WebRTC spec的實現。webRTC的實現代碼(包括流和PeerConnection API)是在libjingle中實現。
線程模型
WebRTCnative APIs 擁有兩個全局線程:信令線程(signaling thread)和工作者線程(worker thread)。取決於PeerConnection factory被創建的方式,應用程序可以提供這兩個線程或者直接使用內部創建好的線程。
Stream APIs和PeerConnectionAPIs的調用會被代理到信令線程,這就意味着應用程序可以在任何線程調用這些APIs。
所有的回調函數都在信令線程調用。應用程序應當盡快地跳出回調函數以避免阻塞信令線程。嚴重消耗資源的過程都應當其他的線程執行。
工作者線程被用來處理資源消耗量大的過程,比如說數據流傳輸。
3.2.3.1 libjingle_peerconnection
這個模塊封裝了對等網絡連接通信過程。
塊圖:
調用順序:
安裝調用:
// The Following steps areneeded to setup a typical call usingJsep.
// 1. Create aPeerConnectionFactoryInterface. Check constructors for more
// information about inputparameters.
// 2. Create a PeerConnectionobject. Provide a configuration string which
// points either to stun orturn server to generate ICE candidates and provide
// an object that implementsthe PeerConnectionObserver interface.
// 3. Create localMediaStream and MediaTracks using the PeerConnectionFactory
// and add it toPeerConnection by calling AddStream.
// 4. Create an offer andserialize it and send it to the remote peer.
// 5. Once an ice candidatehave been found PeerConnection will call the
// observer functionOnIceCandidate. The candidates must also be serialized and
// sent to the remote peer.
// 6. Once an answer isreceived from the remote peer, call
// SetLocalSessionDescriptionwith the offer and SetRemoteSessionDescription
// with the remote answer.
// 7. Once a remote candidateis received from the remote peer, provide it to
// the peerconnectionby calling AddIceCandidate.
接收調用:
// The Receiver of a call candecide to accept or reject the call.
// This decision will betaken by the application notpeerconnection.
// If application decides toaccept the call
// 1. CreatePeerConnectionFactoryInterface if it doesn't exist.
// 2. Create a newPeerConnection.
// 3. Provide the remoteoffer to the new PeerConnection object by calling
//SetRemoteSessionDescription.
// 4. Generate an answer tothe remote offer by calling CreateAnswer and send it
// back to the remote peer.
// 5. Provide the localanswer to the new PeerConnection by calling
// SetLocalSessionDescriptionwith the answer.
// 6. Provide the remote icecandidates by calling AddIceCandidate.
// 7. Once a candidate havebeen found PeerConnection will call the observer
// function OnIceCandidate.Send these candidates to the remote peer.
關閉調用:
視頻捕獲類:
l 得到VideoCapture過程:
cricket::VideoCapturer*Conductor::OpenVideoCaptureDevice() {
talk_base::scoped_ptr<cricket::DeviceManagerInterface>dev_manager(
cricket::DeviceManagerFactory::Create());
if (!dev_manager->Init()) {
LOG(LS_ERROR) <<"Can't create devicemanager";
returnNULL;
}
std::vector<cricket::Device>devs;
if (!dev_manager->GetVideoCaptureDevices(&devs)) {
LOG(LS_ERROR) <<"Can't enumerate videodevices";
returnNULL;
}
std::vector<cricket::Device>::iteratordev_it = devs.begin();
cricket::VideoCapturer*capturer =NULL;
for (;dev_it !=devs.end(); ++dev_it) {
capturer =dev_manager->CreateVideoCapturer(*dev_it);
if (capturer !=NULL)
break;
}
returncapturer;
}
說明:
Ø 捕獲設備的建立:
建立DeviceManagerFactory實例。Windows下在實例初始化時並實例化DefaultVideoCapturerFactory。
調用dev_manager->GetVideoCaptureDevices得到設備。
調用dev_manager->CreateVideoCapturer建立一個視頻捕獲對象。
在dev_manager->CreateVideoCapturer中,
先檢查是否是文件捕獲對象。如果,則返回文件捕獲對象指針。
如果不是,調用device_video_capturer_factory_->Create(device);建立視頻捕獲對象。這里device_video_capturer_factory_=DefaultVideoCapturerFactory
classDefaultVideoCapturerFactory :publicVideoCapturerFactory {
public:
DefaultVideoCapturerFactory() {}
virtual ~DefaultVideoCapturerFactory() {}
VideoCapturer*Create(constDevice&device) {
#ifdefined(VIDEO_CAPTURER_NAME)
VIDEO_CAPTURER_NAME*return_value =newVIDEO_CAPTURER_NAME;
if (!return_value->Init(device)) {
deletereturn_value;
returnNULL;
}
returnreturn_value;
#else
return NULL;
#endif
}
};
這個DefaultVideoCapturerFactory類廠很簡單,在Create函數中建立WebRtcVideoCapturer對象。並調用Init初始化此對象。在這個初始化中,會建立調用module_ = factory_->Create(0,vcm_id);其中factory_建立過程如下:
WebRtcVcmFactory->VideoCaptureFactory-> videocapturemodule::VideoCaptureImpl-> VideoCaptureDS
類關系詳見捕獲設備類:
VideoCaptureModuleV4L2:linux下v4l2捕獲設備。
VideoCaptureDS:windows下DirectxShow捕獲設備。
VideoCaptureAndroid:android下捕獲設備。
Ø 捕獲數據流:
在WebRtcVideoCapturer中的Start函數中,會把回調處理對象VideoCaptureDataCallback與視頻捕獲設備對象VideoCaptureModule進行關聯。這樣,當視頻捕獲設備對象有視頻幀被捕獲時。會調用VideoCaptureDataCallback中的OnIncomingCapturedFrame。WebRtcVideoCapturer繼承VideoCaptureDataCallback,所以會調用WebRtcVideoCapturer::OnIncomingCapturedFrame。在其中調用SignalFrameCaptured(this, &frame);發送捕獲信息。會調用基類VideoCapturer::OnFrameCaptured。在這里會對視頻格式由BITMAP轉換為I420。並且會調用已注冊的VideoProcessors對象。還會SignalVideoFrame(this, &i420_frame);發送視頻幀信號。
l 渲染類:
GdiVideoRenderer:windows gdi渲染
GtkVideoRenderer: GTK庫渲染,這個用在linux平台。
Libjingle現在已轉到webrtc項目中維護了。位於源碼talk目錄中。
一對libjingle的對等連接實際上是由兩個通道組成:
會話協商通道(也稱為信令信道)是用於協商數據連接的通信鏈路。此通道用於請求連接,交換人選,並協商會話的詳細信息(如套接字地址,需要編解碼器,以交換文件,連接變更請求,並終止請求)。這是計算機之間進行的第一次連接,並且僅在由該連接可以將數據信道來建立。對libjingle采用先導,以jingle指定要建立數據連接所需的節和響應(見jingle和對libjingle),該通道通過中介XMPP服務器發送節;示例代碼中使用了谷歌Talk服務器作為中介。
數據通道是在點對點會話之間交換實際數據(音頻、視頻、文件等)。數據通道的數據依賴於傳輸協商,被捆綁在TCP或UDP包中。並且不經過XMPP服務器。
首先會話協商信道被建立,作為計算機協商數據信道的細節;數據連接后,大部分活動發生在數據信道,除非一個編解碼器的改變,一個新的文件請求,偶爾請求重定向請求或終止請求。
下圖顯示了這兩種途徑。在該圖中,兩個備用數據路徑被示出,雖然只有一個數據通路將被激活,在一個連接。這是因為數據通路可以是直接連接(連接嘗試的92%,可以直接發生),或者通過中繼服務器(連接嘗試的8%所需要的中介中繼服務器)。第三個數據通路,沒有顯示,是從計算機到計算機的直接連接時,有沒有中介防火牆。
注意事項:
對libjingle發出偶爾STUN數據包,以保持可寫性,保持防火牆和NAT地址綁定活躍,並檢查連接延遲。
對libjingle分配一個用戶名和密碼來連接端口。這確保了連接上的數據信道的計算機是同一個,協商通過信令信道的連接。由於這些用戶名和密碼值發送XMPP協議可能會或可能不會使用TLS進行加密,在STUN包這些值僅作識別,未加密的身份驗證使用。
https://developers.google.com/talk/libjingle/libjingle_applications?hl=zh-CN
Libjingle的應用程序首先調用XMPP Messaging Component的XmppClient對象進行登錄,然后做一些message,iq,presence等request/respond操作。
其次,每個application可能包含一個或多個sessionclient用來做P2P操作,比如遠程協助,視頻會議,音頻連接,文件共享等等。
4.2.2 XMPP MessagingComponent.模塊
此模塊主要由三個部分組成:XmppClient,LoginHandler和Xmpp Helper Task.此模塊主要做相當於一個peer的防火牆的功能,連接服務器和客戶端,負責發送所有本地的stanza請求(即XMPP協議內容),並負責接收服務器的stanza請求,並分發到各個Helper Task里。
- XmppClient主要是代理登錄,發送stanza,接收stanza。之所以說是代理,是因為真正發送,接收的消息都是通過XmppEngine來實現的。
- XmppEngine能注冊多個XmppStanzaHandler回調,然后所有從服務器接收的Stanza都轉發到已綁定的XmppStanzaHandler進行過濾,而實際上是回調XmppTask對象。
- XmppStanzaHandler類定義如下:
- //! Callback to deliver stanzas to an Xmpp application module.
- //! Register via XmppEngine.SetDefaultSessionHandler or via
- //! XmppEngine.AddSessionHAndler.
- class XmppStanzaHandler {
- public:
- virtual ~XmppStanzaHandler() {}
- //! Process the given stanza.
- //! The handler must return true if it has handled the stanza.
- //! A false return value causes the stanza to be passed on to
- //! the next registered handler.
- virtual bool HandleStanza(const XmlElement * stanza) = 0;
- };
- XmppTask是所有XmppHelperTask的基類,並繼承自XmppStanzaHandler,主要有監聽,過濾 XmppEngine對象轉發過來的Stanza消息。XmppTask有多種類型,當取類型為HL_PEEK時,只有監聽功能,無法做到過濾;而其他類 型可以做到過濾。過濾是有HandlerStanza函數來完成,當返回為true時,過濾,否則XmppEngine枚舉下一個綁定的XmppTask 繼續嘗試分發、過濾。
- 所有XmppHelperTask都要繼承XmppTask並要重載HandlerStanza函數和ProcessStart函數。
- HandlerStanza是用來過濾,相當於windows消息處理的GetMessage()
- 而ProcessStart是用來處理HandlerStanza過濾的消息。
- 比如在源代碼example/call/presencepushtask.h里:
[cpp] view plaincopy
- class PresencePushTask : public XmppTask {
- public:
- PresencePushTask(XmppTaskParentInterface* parent, CallClient* client)
- : XmppTask(parent, XmppEngine::HL_TYPE),
- client_(client) {}
- virtual int ProcessStart();
- sigslot::signal1<const Status&> SignalStatusUpdate;
- sigslot::signal1<const Jid&> SignalMucJoined;
- sigslot::signal2<const Jid&, int> SignalMucLeft;
- sigslot::signal2<const Jid&, const MucStatus&> SignalMucStatusUpdate;
- protected:
- virtual bool HandleStanza(const XmlElement * stanza);
- void HandlePresence(const Jid& from, const XmlElement * stanza);
- void HandleMucPresence(buzz::Muc* muc,
- const Jid& from, const XmlElement * stanza);
- static void FillStatus(const Jid& from, const XmlElement * stanza,
- Status* status);
- static void FillMucStatus(const Jid& from, const XmlElement * stanza,
- MucStatus* status);
- private:
- CallClient* client_;
- };
- 這里PresencePushTask類,通過HandleStanza過濾所有presence相關的stanza並在ProcessStart里處理所有來自服務器的用戶狀態更新消息。
- LoginHandler部分是由XmppPump來負責的。主要調用XmppClient的connect和disconnect方法建立、斷開連接,監聽SignalStateChange事件來獲取連接信息,類型為STATE_OPENED的事件表示連接成功。
4.2.3 Session Logicand management commponent模塊。
所有p2p session邏輯相關的部分都放在了這個模塊。可以session可能是處理文件傳輸的連接,或者可能是視頻會話,或者音頻會話等等。
- 我們需要繼承SessionClient來處理每個Session相關具體任務,比如文件傳輸Session:當接收對端客戶端建立一個文件傳輸 session的時候,如果此Session是新創建的,SessionManager對象會回調所有注冊的SessionClient的 OnSessionCreate的接口,並以SessionManger創建的Session對象為參數穿進去;如果是已有的Session則會調用 Session的OnIncomingMessage方法。
- Session對象則抽象了兩個peer之間的數據傳輸接口。當收到OnSessionCreate回調時,SessionClient可以通過Session的方法Accept來接受創建,Reject來拒絕。
- 那怎么讀寫p2p數據呢?
- 首先需要調用session的CreateChannel方法獲取TransportChannel對象指針,然后監聽TransportChannel的事件SignalReadPacket來接收數據,通過SendPacket方法來發送數據。
- class TransportChannel: public sigslot::has_slots<> {
- public:
- //......
- // Attempts to send the given packet. The return value is < 0 on failure.
- virtual int SendPacket(const char *data, size_t len) = 0;
- // Signalled each time a packet is received on this channel.
- sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;
- //......
- };
4.2.4 Peer to peerComponent模塊。
此模塊才是libjingle核心,libjingle項目的初衷也是能夠把模塊設計得完美,使得所有需要通過P2P傳輸數據的應用層調用libjingle時,不用擔心數據傳輸的穩定性,可靠性,高效性。
- 剛才上面提到,當服務器發送stanza時XmppEngine把Stanza發送到XmppTask過濾,在這個模 塊,SessionManagerTask代理SessionManager過濾session相關的stanza,並轉發到 SessionManager對象,如下:
- class SessionManagerTask : public buzz::XmppTask {
- public:
- ......
- virtual int ProcessStart() {
- const buzz::XmlElement *stanza = NextStanza();
- if (stanza == NULL)
- return STATE_BLOCKED;
- session_manager_->OnIncomingMessage(stanza);
- return STATE_START;
- }
- protected:
- virtual bool HandleStanza(const buzz::XmlElement *stanza) {
- if (!session_manager_->IsSessionMessage(stanza))
- return false;
- QueueStanza(stanza);
- return true;
- }
- } // namespace cricket
- SessionManager類在這里起到連接上述3個模塊的橋梁作用。
- 當上層調用SessionManager創建的Session對象的CreateChannel時,實際上是調用P2PTransport的CreateChannel方法。
- 上層通過P2PTransport創建P2pTransportChannel的類的。
- P2pTransportChannel 繼承自TransportChannel,並創建多個不同的Connection,每個Connection代表一個TCP或者UDP或者SSL連接。上 層傳輸數據最終是調到P2pTransportChannel的相關方法,當發送,接收數據時,P2pTransportChannel選擇表現最好的 Connection進行傳輸。
LigJingle提供了很多接口供我們繼承,用於特定的個性化Session,同時也提供了不少實例(如pcp,login,call)讓調用者更容易的理解框架思路。當需要着手研究libjingle時,如果能夠充分的利用已成的實例,對於縮短熟悉時間,很有幫助。
peerconnection_client.exe!webrtc::RtpReceiverImpl::RtpReceiverImpl(intid=65536, webrtc::Clock * clock=0x017d4e68, webrtc::RtpAudioFeedback *incoming_audio_messages_callback=0x0417a7d4, webrtc::RtpFeedback *incoming_messages_callback=0x0417a7c4, webrtc::RTPPayloadRegistry * rtp_payload_registry=0x0417d060,webrtc::RTPReceiverStrategy * rtp_media_receiver=0x0417d338) 行91 C++
peerconnection_client.exe!webrtc::RtpReceiver::CreateAudioReceiver(intid=65536, webrtc::Clock * clock=0x017d4e68, webrtc::RtpAudioFeedback *incoming_audio_feedback=0x0417a7d4, webrtc::RtpData *incoming_payload_callback=0x0417a7c0, webrtc::RtpFeedback *incoming_messages_callback=0x0417a7c4, webrtc::RTPPayloadRegistry *rtp_payload_registry=0x0417d060) 行61 + 0x47 字節 C++
peerconnection_client.exe!webrtc::voe::Channel::Channel(intchannelId=0, unsigned int instanceId=1, const webrtc::Config &config={...}) 行931 + 0x1e7 字節 C++
peerconnection_client.exe!webrtc::voe::Channel::CreateChannel(webrtc::voe::Channel* & channel=0xcccccccc, int channelId=0, unsigned int instanceId=1, constwebrtc::Config & config={...}) 行753 + 0x2a 字節 C++
peerconnection_client.exe!webrtc::voe::ChannelManager::CreateChannelInternal(constwebrtc::Config & config={...}) 行64 + 0x1f 字節 C++
peerconnection_client.exe!webrtc::voe::ChannelManager::CreateChannel(constwebrtc::Config & external_config={...}) 行59 + 0x10 字節 C++
peerconnection_client.exe!webrtc::VoEBaseImpl::CreateChannel(constwebrtc::Config & config={...}) 行552 C++
peerconnection_client.exe!cricket::WebRtcVoiceEngine::CreateVoiceChannel(cricket::VoEWrapper* voice_engine_wrapper=0x01a572f8) 行1652 + 0x23 字節 C++
peerconnection_client.exe!cricket::WebRtcVoiceEngine::CreateMediaVoiceChannel() 行1657 C++
peerconnection_client.exe!cricket::WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel(cricket::WebRtcVoiceEngine* engine=0x01a56e1c) 行1765 + 0x3d 字節 C++
peerconnection_client.exe!cricket::WebRtcVoiceEngine::CreateChannel() 行635 + 0x22 字節 C++
peerconnection_client.exe!cricket::CompositeMediaEngine<cricket::WebRtcVoiceEngine,cricket::WebRtcVideoEngine>::CreateChannel() 行191 C++
peerconnection_client.exe!cricket::ChannelManager::CreateVoiceChannel_w(cricket::BaseSession* session=0x03fade50, const std::basic_string<char,std::char_traits<char>,std::allocator<char>> & content_name="audio", bool rtcp=true) 行327 + 0x1d 字節 C++
peerconnection_client.exe!talk_base::MethodFunctor3<cricket::ChannelManager,cricket::VoiceChannel* (__thiscall cricket::ChannelManager::*)(cricket::BaseSession*,std::basic_string<char,std::char_traits<char>,std::allocator<char>> const &,bool),cricket::VoiceChannel *,cricket::BaseSession*,std::basic_string<char,std::char_traits<char>,std::allocator<char>> const &,bool>::operator()() 行294 + 0x2b 字節 C++
peerconnection_client.exe!talk_base::FunctorMessageHandler<cricket::VoiceChannel*,talk_base::MethodFunctor3<cricket::ChannelManager,cricket::VoiceChannel *(__thiscall cricket::ChannelManager::*)(cricket::BaseSession*,std::basic_string<char,std::char_traits<char>,std::allocator<char>> const &,bool),cricket::VoiceChannel *,cricket::BaseSession*,std::basic_string<char,std::char_traits<char>,std::allocator<char>> const &,bool> >::OnMessage(talk_base::Message *msg=0x03a0fdc8) 行58 + 0xb 字節 C++
peerconnection_client.exe!talk_base::Thread::ReceiveSends() 行456 + 0x13 字節 C++
peerconnection_client.exe!talk_base::MessageQueue::Get(talk_base::Message* pmsg=0x03a0ff38, int cmsWait=-1, bool process_io=true) 行205 + 0xf 字節 C++
peerconnection_client.exe!talk_base::Thread::ProcessMessages(intcmsLoop=-1) 行508 + 0x19 字節 C++
peerconnection_client.exe!talk_base::Thread::Run() 行371 C++
peerconnection_client.exe!talk_base::Thread::PreRun(void* pv=0x01a551f0) 行358 + 0x13 字節 C++
kernel32.dll!76033c45()
[下面的框架可能不正確和/或缺失,沒有為 kernel32.dll 加載符號]
ntdll.dll!771a37f5()
ntdll.dll!771a37c8()
peerconnection_client.exe!webrtc::acm1::AudioCodingModuleImpl::IncomingPacket(constunsigned char * incoming_payload=0x0026f5bc, const int payload_length=3, constwebrtc::WebRtcRTPHeader & rtp_info={...}) 行2032 C++
peerconnection_client.exe!webrtc::voe::Channel::OnReceivedPayloadData(constunsigned char * payloadData=0x0026f5bc, unsigned short payloadSize=3, constwebrtc::WebRtcRTPHeader * rtpHeader=0x03a5e0fc) 行545 + 0x2a 字節 C++
peerconnection_client.exe!webrtc::RTPReceiverAudio::ParseAudioCodecSpecific(webrtc::WebRtcRTPHeader* rtp_header=0x03a5e0fc, const unsigned char * payload_data=0x0026f5bc,unsigned short payload_length=3, const webrtc::AudioPayload &audio_specific={...}, bool is_red=false) 行395 + 0x22 字節 C++
peerconnection_client.exe!webrtc::RTPReceiverAudio::ParseRtpPacket(webrtc::WebRtcRTPHeader* rtp_header=0x03a5e0fc, const webrtc::PayloadUnion &specific_payload={...}, bool is_red=false, const unsigned char *payload=0x0026f5bc, unsigned short payload_length=3, __int64 timestamp_ms=16210844,bool is_first_packet=true) 行206 + 0x1e 字節 C++
peerconnection_client.exe!webrtc::RtpReceiverImpl::IncomingRtpPacket(const webrtc::RTPHeader & rtp_header={...}, const unsignedchar * payload=0x0026f5bc, int payload_length=3, webrtc::PayloadUnionpayload_specific={...}, bool in_order=true) 行244 + 0x65 字節 C++
peerconnection_client.exe!webrtc::voe::Channel::ReceivePacket(constunsigned char * packet=0x0026f5b0, int packet_length=15, constwebrtc::RTPHeader & header={...}, bool in_order=true) 行2095 + 0x44 字節 C++
peerconnection_client.exe!webrtc::voe::Channel::ReceivedRTPPacket(constchar * data=0x0026f5b0, int length=15) 行2076 + 0x19 字節 C++
peerconnection_client.exe!webrtc::VoENetworkImpl::ReceivedRTPPacket(intchannel=0, const void * data=0x0026f5b0, unsigned int length=15) 行128 + 0x10 字節 C++
peerconnection_client.exe!cricket::WebRtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet=0x03a5e784, const talk_base::PacketTime & packet_time={...}) 行2964 + 0x47 字節 C++
peerconnection_client.exe!cricket::BaseChannel::HandlePacket(boolrtcp=false, talk_base::Buffer * packet=0x03a5e784, const talk_base::PacketTime& packet_time={...}) 行643 + 0x23 字節 C++
peerconnection_client.exe!cricket::BaseChannel::OnChannelRead(cricket::TransportChannel* channel=0x040bf4a0, const char * data=0x04244008, unsigned int len=25, consttalk_base::PacketTime & packet_time={...}, int flags=0) 行392 C++
peerconnection_client.exe!cricket::VoiceChannel::OnChannelRead(cricket::TransportChannel* channel=0x040bf4a0, const char * data=0x04244008, unsigned int len=25, consttalk_base::PacketTime & packet_time={...}, int flags=0) 行1425 C++
peerconnection_client.exe!sigslot::_connection5<cricket::BaseChannel,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::emit(cricket::TransportChannel *a1=0x040bf4a0, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::operator()(cricket::TransportChannel *a1=0x040bf4a0, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::TransportChannelProxy::OnReadPacket(cricket::TransportChannel* channel=0x0423b968, const char * data=0x04244008, unsigned int size=25, consttalk_base::PacketTime & packet_time={...}, int flags=0) 行243 C++
peerconnection_client.exe!sigslot::_connection5<cricket::TransportChannelProxy,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::emit(cricket::TransportChannel *a1=0x0423b968, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::operator()(cricket::TransportChannel *a1=0x0423b968, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::DtlsTransportChannelWrapper::OnReadPacket(cricket::TransportChannel* channel=0x0423bb10, const char * data=0x04244008, unsigned int size=25, consttalk_base::PacketTime & packet_time={...}, int flags=0) 行463 C++
peerconnection_client.exe!sigslot::_connection5<cricket::DtlsTransportChannelWrapper,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::emit(cricket::TransportChannel *a1=0x0423bb10, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::operator()(cricket::TransportChannel *a1=0x0423bb10, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::P2PTransportChannel::OnReadPacket(cricket::Connection* connection=0x05c2e260, const char * data=0x04244008, unsigned int len=25,const talk_base::PacketTime & packet_time={...}) 行1260 C++
peerconnection_client.exe!sigslot::_connection4<cricket::P2PTransportChannel,cricket::Connection*,char const *,unsigned int,talk_base::PacketTime const&,sigslot::single_threaded>::emit(cricket::Connection * a1=0x05c2e260,const char * a2=0x04244008, unsigned int a3=25, const talk_base::PacketTime& a4={...}) 行1993 + 0x26 字節 C++
peerconnection_client.exe!sigslot::signal4<cricket::Connection*,char const *,unsigned int,talk_base::PacketTime const&,sigslot::single_threaded>::operator()(cricket::Connection *a1=0x05c2e260, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::PacketTime & a4={...}) 行2544 + 0x2c 字節 C++
peerconnection_client.exe!cricket::Connection::OnReadPacket(constchar * data=0x04244008, unsigned int size=25, const talk_base::PacketTime &packet_time={...}) 行962 C++
peerconnection_client.exe!cricket::UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket=0x05c30528, const char * data=0x04244008, unsigned int size=25, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime& packet_time={...}) 行268 C++
peerconnection_client.exe!cricket::UDPPort::HandleIncomingPacket(talk_base::AsyncPacketSocket* socket=0x05c30528, const char * data=0x04244008, unsigned int size=25, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime& packet_time={...}) 行104 C++
peerconnection_client.exe!cricket::AllocationSequence::OnReadPacket(talk_base::AsyncPacketSocket* socket=0x05c30528, const char * data=0x04244008, unsigned int size=25, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime& packet_time={...}) 行1021 + 0x30 字節 C++
peerconnection_client.exe!sigslot::_connection5<cricket::AllocationSequence,talk_base::AsyncPacketSocket*,char const *,unsigned int,talk_base::SocketAddress const&,talk_base::PacketTime const&,sigslot::single_threaded>::emit(talk_base::AsyncPacketSocket *a1=0x05c30528, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::SocketAddress & a4={...}, const talk_base::PacketTime &a5={...}) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<talk_base::AsyncPacketSocket*,char const *,unsigned int,talk_base::SocketAddress const&,talk_base::PacketTime const&,sigslot::single_threaded>::operator()(talk_base::AsyncPacketSocket *a1=0x05c30528, const char * a2=0x04244008, unsigned int a3=25, consttalk_base::SocketAddress & a4={...}, const talk_base::PacketTime &a5={...}) 行2616 + 0x30 字節 C++
peerconnection_client.exe!talk_base::AsyncUDPSocket::OnReadEvent(talk_base::AsyncSocket* socket=0x05c30044) 行133 C++
peerconnection_client.exe!sigslot::_connection1<talk_base::AsyncUDPSocket,talk_base::AsyncSocket*,sigslot::multi_threaded_local>::emit(talk_base::AsyncSocket *a1=0x05c30044) 行1852 + 0x1a 字節 C++
peerconnection_client.exe!sigslot::signal1<talk_base::AsyncSocket*,sigslot::multi_threaded_local>::operator()(talk_base::AsyncSocket *a1=0x05c30044) 行2346 + 0x20 字節 C++
peerconnection_client.exe!talk_base::SocketDispatcher::OnEvent(unsignedint ff=1, int err=0) 行1150 C++
peerconnection_client.exe!talk_base::PhysicalSocketServer::Wait(intcmsWait=32, bool process_io=true) 行1671 + 0x23 字節 C++
peerconnection_client.exe!talk_base::MessageQueue::Get(talk_base::Message* pmsg=0x03a5ff38, int cmsWait=-1, bool process_io=true) 行271 + 0x21 字節 C++
peerconnection_client.exe!talk_base::Thread::ProcessMessages(intcmsLoop=-1) 行508 + 0x19 字節 C++
peerconnection_client.exe!talk_base::Thread::Run() 行371 C++
peerconnection_client.exe!talk_base::Thread::PreRun(void* pv=0x00275080) 行358 + 0x13 字節 C++
kernel32.dll!76033c45()
[下面的框架可能不正確和/或缺失,沒有為 kernel32.dll 加載符號]
ntdll.dll!771a37f5()
ntdll.dll!771a37c8()
peerconnection_client.exe!webrtc::ModuleRTPUtility::RTPPayloadParser::ParseVP8(webrtc::ModuleRTPUtility::RTPPayload& parsedPacket={...}) 行658 C++
peerconnection_client.exe!webrtc::ModuleRTPUtility::RTPPayloadParser::Parse(webrtc::ModuleRTPUtility::RTPPayload& parsedPacket={...}) 行581 + 0xc 字節 C++
peerconnection_client.exe!webrtc::RTPReceiverVideo::ReceiveVp8Codec(webrtc::WebRtcRTPHeader* rtp_header=0x039ee0b8, const unsigned char * payload_data=0x03f7f206,unsigned short payload_data_length=129) 行181 + 0xc 字節 C++
peerconnection_client.exe!webrtc::RTPReceiverVideo::ParseVideoCodecSpecific(webrtc::WebRtcRTPHeader* rtp_header=0x039ee0b8, const unsigned char * payload_data=0x03f7f206,unsigned short payload_data_length=129, webrtc::RtpVideoCodecTypesvideo_type=kRtpVideoVp8, __int64 now_ms=2915007, boolis_first_packet=true) 行126 + 0x15 字節 C++
peerconnection_client.exe!webrtc::RTPReceiverVideo::ParseRtpPacket(webrtc::WebRtcRTPHeader* rtp_header=0x039ee0b8, const webrtc::PayloadUnion & specific_payload={...},bool is_red=false, const unsigned char * payload=0x03f7f206, unsigned shortpayload_length=129, __int64 timestamp_ms=2915007, boolis_first_packet=true) 行75 + 0x28 字節 C++
peerconnection_client.exe!webrtc::RtpReceiverImpl::IncomingRtpPacket(constwebrtc::RTPHeader & rtp_header={...}, const unsigned char *payload=0x03f7f206, int payload_length=129, webrtc::PayloadUnionpayload_specific={...}, bool in_order=false) 行244 + 0x65 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::ReceivePacket(constunsigned char * packet=0x03f7f1ee, int packet_length=153, constwebrtc::RTPHeader & header={...}, bool in_order=false) 行243 + 0x44 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::OnRecoveredPacket(constunsigned char * rtp_packet=0x03f7f1ee, int rtp_packet_length=153) 行184 C++
peerconnection_client.exe!webrtc::FecReceiverImpl::ProcessReceivedFec() 行220 + 0x24 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::ParseAndHandleEncapsulatingHeader(constunsigned char * packet=0x03f81768, int packet_length=154, constwebrtc::RTPHeader & header={...}) 行259 + 0x1d 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::ReceivePacket(constunsigned char * packet=0x03f81768, int packet_length=154, constwebrtc::RTPHeader & header={...}, bool in_order=true) 行232 + 0x14 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::InsertRTPPacket(constunsigned char * rtp_packet=0x03f81768, int rtp_packet_length=154, constwebrtc::PacketTime & packet_time={...}) 行224 + 0x1c 字節 C++
peerconnection_client.exe!webrtc::ViEReceiver::ReceivedRTPPacket(constvoid * rtp_packet=0x03f81768, int rtp_packet_length=154, constwebrtc::PacketTime & packet_time={...}) 行156 C++
peerconnection_client.exe!webrtc::ViEChannel::ReceivedRTPPacket(constvoid * rtp_packet=0x03f81768, const int rtp_packet_length=154, constwebrtc::PacketTime & packet_time={...}) 行1670 C++
peerconnection_client.exe!webrtc::ViENetworkImpl::ReceivedRTPPacket(constint video_channel=0, const void * data=0x03f81768, const int length=154, constwebrtc::PacketTime & packet_time={...}) 行160 + 0x14 字節 C++
peerconnection_client.exe!cricket::WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet=0x039ee7a4, const talk_base::PacketTime & packet_time={...}) 行2570 + 0x5f 字節 C++
peerconnection_client.exe!cricket::BaseChannel::HandlePacket(boolrtcp=false, talk_base::Buffer * packet=0x039ee7a4, const talk_base::PacketTime& packet_time={...}) 行643 + 0x23 字節 C++
peerconnection_client.exe!cricket::BaseChannel::OnChannelRead(cricket::TransportChannel* channel=0x041d0500, const char * data=0x065e2008, unsigned int len=164, consttalk_base::PacketTime & packet_time={...}, int flags=0) 行392 C++
peerconnection_client.exe!sigslot::_connection5<cricket::BaseChannel,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::emit(cricket::TransportChannel *a1=0x041d0500, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::operator()(cricket::TransportChannel *a1=0x041d0500, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::TransportChannelProxy::OnReadPacket(cricket::TransportChannel* channel=0x041d2108, const char * data=0x065e2008, unsigned int size=164,const talk_base::PacketTime & packet_time={...}, int flags=0) 行243 C++
peerconnection_client.exe!sigslot::_connection5<cricket::TransportChannelProxy,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const &,int,sigslot::single_threaded>::emit(cricket::TransportChannel* a1=0x041d2108, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::operator()(cricket::TransportChannel *a1=0x041d2108, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::DtlsTransportChannelWrapper::OnReadPacket(cricket::TransportChannel* channel=0x041d22b0, const char * data=0x065e2008, unsigned int size=164,const talk_base::PacketTime & packet_time={...}, int flags=0) 行463 C++
peerconnection_client.exe!sigslot::_connection5<cricket::DtlsTransportChannelWrapper,cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const&,int,sigslot::single_threaded>::emit(cricket::TransportChannel *a1=0x041d22b0, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<cricket::TransportChannel*,char const *,unsigned int,talk_base::PacketTime const &,int,sigslot::single_threaded>::operator()(cricket::TransportChannel* a1=0x041d22b0, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}, int a5=0) 行2616 + 0x30 字節 C++
peerconnection_client.exe!cricket::P2PTransportChannel::OnReadPacket(cricket::Connection* connection=0x06675900, const char * data=0x065e2008, unsigned int len=164,const talk_base::PacketTime & packet_time={...}) 行1260 C++
peerconnection_client.exe!sigslot::_connection4<cricket::P2PTransportChannel,cricket::Connection*,char const *,unsigned int,talk_base::PacketTime const&,sigslot::single_threaded>::emit(cricket::Connection * a1=0x06675900,const char * a2=0x065e2008, unsigned int a3=164, const talk_base::PacketTime& a4={...}) 行1993 + 0x26 字節 C++
peerconnection_client.exe!sigslot::signal4<cricket::Connection*,char const *,unsigned int,talk_base::PacketTime const&,sigslot::single_threaded>::operator()(cricket::Connection *a1=0x06675900, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::PacketTime & a4={...}) 行2544 + 0x2c 字節 C++
peerconnection_client.exe!cricket::Connection::OnReadPacket(constchar * data=0x065e2008, unsigned int size=164, const talk_base::PacketTime& packet_time={...}) 行962 C++
peerconnection_client.exe!cricket::UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket=0x041e4dd8, const char * data=0x065e2008, unsigned int size=164, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime& packet_time={...}) 行268 C++
peerconnection_client.exe!cricket::UDPPort::HandleIncomingPacket(talk_base::AsyncPacketSocket* socket=0x041e4dd8, const char * data=0x065e2008, unsigned int size=164, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime &packet_time={...}) 行104 C++
peerconnection_client.exe!cricket::AllocationSequence::OnReadPacket(talk_base::AsyncPacketSocket* socket=0x041e4dd8, const char * data=0x065e2008, unsigned int size=164, consttalk_base::SocketAddress & remote_addr={...}, const talk_base::PacketTime& packet_time={...}) 行1021 + 0x30 字節 C++
peerconnection_client.exe!sigslot::_connection5<cricket::AllocationSequence,talk_base::AsyncPacketSocket*,char const *,unsigned int,talk_base::SocketAddress const&,talk_base::PacketTime const&,sigslot::single_threaded>::emit(talk_base::AsyncPacketSocket *a1=0x041e4dd8, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::SocketAddress & a4={...}, const talk_base::PacketTime &a5={...}) 行2047 + 0x2a 字節 C++
peerconnection_client.exe!sigslot::signal5<talk_base::AsyncPacketSocket*,char const *,unsigned int,talk_base::SocketAddress const&,talk_base::PacketTime const&,sigslot::single_threaded>::operator()(talk_base::AsyncPacketSocket *a1=0x041e4dd8, const char * a2=0x065e2008, unsigned int a3=164, consttalk_base::SocketAddress & a4={...}, const talk_base::PacketTime &a5={...}) 行2616 + 0x30 字節 C++
peerconnection_client.exe!talk_base::AsyncUDPSocket::OnReadEvent(talk_base::AsyncSocket* socket=0x041e46bc) 行133 C++
peerconnection_client.exe!sigslot::_connection1<talk_base::AsyncUDPSocket,talk_base::AsyncSocket*,sigslot::multi_threaded_local>::emit(talk_base::AsyncSocket *a1=0x041e46bc) 行1852 + 0x1a 字節 C++
peerconnection_client.exe!sigslot::signal1<talk_base::AsyncSocket*,sigslot::multi_threaded_local>::operator()(talk_base::AsyncSocket *a1=0x041e46bc) 行2346 + 0x20 字節 C++
peerconnection_client.exe!talk_base::SocketDispatcher::OnEvent(unsignedint ff=1, int err=0) 行1150 C++
peerconnection_client.exe!talk_base::PhysicalSocketServer::Wait(intcmsWait=477, bool process_io=true) 行1671 + 0x23 字節 C++
peerconnection_client.exe!talk_base::MessageQueue::Get(talk_base::Message* pmsg=0x039eff38, int cmsWait=-1, bool process_io=true) 行271 + 0x21 字節 C++
peerconnection_client.exe!talk_base::Thread::ProcessMessages(intcmsLoop=-1) 行508 + 0x19 字節 C++
peerconnection_client.exe!talk_base::Thread::Run() 行371 C++
peerconnection_client.exe!talk_base::Thread::PreRun(void* pv=0x00262510) 行358 + 0x13 字節 C++
kernel32.dll!76993c45()
[下面的框架可能不正確和/或缺失,沒有為 kernel32.dll 加載符號]
ntdll.dll!77d437f5()
ntdll.dll!77d437c8()
6.1 XMPP協議:
XMPP(可擴展消息處理現場協議)是基於可擴展標記語言(XML)的協議,它用於即時消息(IM)以及在線現場探測。它在促進服務器之間的准即時操作。這個協議可能最終允許因特網用戶向因特網上的其他任何人發送即時消息,即使其操作系統和瀏覽器不同。
XMPP的前身是Jabber,一個開源形式組織產生的網絡即時通信協議。XMPP目前被IETF國際標准組織完成了標准化工作。標准化的核心結果分為兩部分;
在IETF 中,把IM協議划分為四種協議,即即時信息和出席協議(Instant Messaging and Presence Protocol, IMPP)、出席和即時信息協議(Presence and Instant Messaging Protocol, PRIM)、針對即時信息和出席擴展的會話發起協議(Session Initiation Protocol for Instant Messaging and PresenceLeveraging Extensions, SIMPLE),以及可擴展的消息出席協議(XMPP)。最初研發IMPP 也是為了創建一種標准化的協議,但是今天,IMPP 已經發展成為基本協議單元,定義所有即時通信協議應該支持的核心功能集。
XMPP 和SIMPLE 兩種協議是架構,有助於實現IMPP協議所描述的規范。PRIM 最初是基於即時通信的協議,與XMPP 和SIMPLE 類似,但是己經不再使用
1. XMPP 協議是公開的,由JSF開源社區組織開發的。XMPP 協議並不屬於任何的機構和個人,而是屬於整個社區,這一點從根本上保證了其開放性。
2. XMPP 協議具有良好的擴展性。在XMPP 中,即時消息和到場信息都是基於XML 的結構化信息,這些信息以XML 節(XML Stanza)的形式在通信實體間交換。XMPP 發揮了XML 結構化數據的通用傳輸層的作用,它將出席和上下文敏感信息嵌入到XML 結構化數據中,從而使數據以極高的效率傳送給最合適的資源。基於XML 建立起來的應用具有良好的語義完整性和擴展性。
3. 分布式的網絡架構。XMPP 協議都是基於Client/Server 架構,但是XMPP協議本身並沒有這樣的限制。網絡的架構和電子郵件十分相似,但沒有結合任何特定的網絡架構,適用范圍非常廣泛。
4.XMPP 具有很好的彈性。XMPP 除了可用在即時通信的應用程序,還能用在網絡管理、內容供稿、協同工具、檔案共享、游戲、遠端系統監控等。
5.安全性。XMPP在Client-to-Server通信,和Server-to-Server通信中都使用TLS (Transport LayerSecurity)協議作為通信通道的加密方法,保證通信的安全。任何XMPP服務器可以獨立於公眾XMPP網絡(例如在企業內部網絡中),而使用 SASL及TLS等技術更加增強了通信的安全性。如下圖所示:
XMPP 是一個典型的C/S架構,而不是像大多數即時通訊軟件一樣,使用P2P客戶端到客戶端的架構,也就是說在大多數情況下,當兩個客戶端進行通訊時,他們的消息都是通過服務器傳遞的(也有例外,例如在兩個客戶端傳輸文件時).采用這種架構,主要是為了簡化客戶端,將大多數工作放在服務器端進行,這樣,客戶端的 工作就比較簡單,而且,當增加功能時,多數是在服務器端進行.XMPP服務的框架結構如下圖所示.XMPP中定義了三個角色,XMPP客戶端,XMPP服 務器、網關.通信能夠在這三者的任意兩個之間雙向發生.服務器同時承擔了客戶端信息記錄、連接管理和信息的路由功能.網關承擔着與異構即時通信系統的互聯 互通,異構系統可以包括SMS(短信)、MSN、ICQ等.基本的網絡形式是單客戶端通過TCP/IP連接到單服務器,然后在之上傳輸XML,工作原理 是:
1) 節點連接到服務器;
2) 服務器利用本地目錄系統中的證書對其認證;
3) 節點指定目標地址,讓服務器告知目標狀態;
4) 服務器查找、連接並進行相互認證;
5) 節點之間進行交互.
· XMPP客戶端
XMPP 系統的一個設計標准是必須支持簡單的客戶端。事實上,XMPP 系統架構對客戶端只有很少的幾個限制。一個XMPP 客戶端必須支持的功能有:
1) 通過 TCP 套接字與XMPP服務器進行通信;
2) 解析組織好的 XML 信息包;
3) 理解消息數據類型。
XMPP 將復雜性從客戶端轉移到服務器端。這使得客戶端編寫變得非常容易,更新系統功能也同樣變得容易。XMPP 客戶端與服務端通過XML 在TCP 套接字的5222 端口進行通信,而不需要客戶端之間直接進行通信。
基本的XMPP 客戶端必須實現以下標准協議(XEP-0211):
1) RFC3920 核心協議Core
2) RFC3921 即時消息和出席協議Instant Messaging andPresence
3) XEP-0030 服務發現Service Discovery
4) XEP-0115 實體能力Entity Capabilities
· XMPP服務器
XMPP 服務器遵循兩個主要法則:
5) 監聽客戶端連接,並直接與客戶端應用程序通信;
6) 與其他 XMPP 服務器通信;
XMPP開源服務器一般被設計成模塊化,由各個不同的代碼包構成,這些代碼包分別處理Session管理、用戶和服務器之間的通信、服務器之間的通信、DNS(Domain Name System)轉換、存儲用戶的個人信息和朋友名單、保留用戶在下線時收到的信息、用戶注冊、用戶的身份和權限認證、根據用戶的要求過濾信息和系統記錄等。另外,服務器可以通過附加服務來進行擴展,如完整的安全策略,允許服務器組件的連接或客戶端選擇,通向其他消息系統的網關。
基本的XMPP 服務器必須實現以下標准協議
1) RFC3920 核心協議Core
2) RFC3921 即時消息和出席協議Instant Messaging andPresence
3) XEP-0030 服務發現Service Discovery
· XMPP網關
XMPP 突出的特點是可以和其他即時通信系統交換信息和用戶在線狀況。由於協議不同,XMPP 和其他系統交換信息必須通過協議的轉換來實現,目前幾種主流即時通信協議都沒有公開,所以XMPP 服務器本身並沒有實現和其他協議的轉換,但它的架構允許轉換的實現。實現這個特殊功能的服務端在XMPP 架構里叫做網關(gateway)。目前,XMPP 實現了和AIM、ICQ、IRC、MSN Massager、RSS0.9和Yahoo Massager 的協議轉換。由於網關的存在,XMPP架構事實上兼容所有其他即時通信網絡,這無疑大大提高了XMPP 的靈活性和可擴展性。
主要的XMPP 協議范本及當今應用很廣的XMPP 擴展:
RFC 3920 :XMPP核心。全稱:The Extensible Messaging andPresence Protocol,即可 擴展通訊和表示協議。說白了,就是規定基於XML流傳輸指定節點數據的協議。這么做的好處就是統一(注:大家都按照這個定義,做的東西就可以相互通訊、交流,這個應該很有發展前景!)。它是一個開放並且可擴展的協議,包括Jingle協議都是XMPP協議的擴展。(注:使用Wireshark抓包時,早期的版本可能找不到這個協議,這時候可以選擇Jabber,它是XMPP協議的前身)。現在很多的IM都是基於XMPP協議開發的,包括gtalk等。定義了XMPP 協議框架下應用的網絡架構,引入了XML Stream(XML 流)與XML Stanza(XML 節),並規定XMPP 協議在通信過程中使用的XML 標簽。使用XML 標簽從根本上說是協議開放性與擴展性的需要。此外,在通信的安全方面,把TLS 安全傳輸機制與SASL 認證機制引入到內核,與XMPP 進行無縫的連接,為協議的安全性、可靠性奠定了基礎。核心 文檔還規定了錯誤的定義及處理、XML 的使用規范、JID(Jabber Identifier,Jabber 標識符)的定義、命名規范等等。所以這是所有基於XMPP 協議的應用都必需支持的文檔。
RFC 3921:用戶成功登陸到服務器之后,發布更新自己的在線好友管理、發送即時聊天消息等業務。所有的這些業務都是通過三種基本的XML 節來完成的:IQ Stanza(IQ 節), Presence Stanza(Presence 節), Message Stanza(Message 節)。RFC3921 還對阻塞策略進行了定義,定義是多種阻塞方式。可以說,RFC3921 是RFC3920 的充分補充。兩個文檔結合起來,就形成了一個基本的即時通信協議平台,在這個平台上可以開發出各種各樣的應用。
XEP-0030 服務搜索。一個強大的用來測定XMPP 網絡中的其它實體所支持特性的協議。
XEP-0115 實體性能。XEP-0030 的一個通過即時出席的定制,可以實時改變交變廣告功能。
XEP-0045 多人聊天。一組定義參與和管理多用戶聊天室的協議,類似於Internet 的Relay Chat,具有很高的安全性。
XEP-0096 文件傳輸。定義了從一個XMPP 實體到另一個的文件傳輸。
XEP-0124 HTTP 綁定。將XMPP 綁定到HTTP 而不是TCP,主要用於不能夠持久的維持與服務器TCP 連接的設備。
XEP-0166 Jingle。規定了多媒體通信協商的整體架構。Jingle協議是XMPP協議上的擴展協議,它着手解決在XMPP協議框架下的點對點的連接問題,也即P2P連接。在Jingle框架下,即使用戶在防火牆或是NAT網絡保護之下,也能夠建立連接,從而提供文件傳送、視頻、音頻服務等。
XEP-0167 Jingle Audio Content DescriptionFormat。定義了從一個XMPP 實體到另一個的語音傳輸過程。
TURN協議:全稱:Traversal Using Relays around NAT,顧名思義,就是通過中繼服務器來傳輸數據的協議。
STUN協議:全稱:Simple Traversal of UDP over NATs,即NAT 的UDP簡單穿越,它允許位於NAT(或多重NAT)后的客戶端找出自己的公網地址,查出自己位於哪種類型的NAT之后以及NAT為某一個本地端口所綁定的Internet端端口。知道NAT類型並且有了公網IP和port,P2P就方便多了。
XEP-0176 Jingle ICE(Interactive Connectivity Establishment)Transport。即 交互式連接建立,說白了,它就是利用STUN和TURN等協議找到最適合的連接。
XEP-0177 Jingle Raw UDP Transport。純UDP 傳輸機制,文件講述了如何在沒有防火牆且在同一網絡下建立連接的。
XEP-0180 Jingle Video Content DescriptionFormat。定義了從一個XMPP 實體到另一個的視頻傳輸過程。
XEP-0181 Jingle DTMF(Dual Tone Multi-Frequency)。
XEP-0183 Jingle Telepathy Transport Method。
中文版本:
英文版本:
STUN(Session Traversal Utilities for NAT,NAT會話傳輸應用程序)是一種網絡協議,它允許位於NAT(或多重NAT)后的客戶端找出自己的公網地址,查出自己位於哪種類型的NAT之后以及NAT為某一個本地端口所綁定的Internet端端口。這些信息被用來在兩個同時處於NAT 路由器之后的主機之間建立UDP通信。該協議由RFC 5389定義。
一旦客戶端得知了Internet端的UDP端口,通信就可以開始了。如果NAT是完全圓錐型的,那么雙方中的任何一方都可以發起通信。如果NAT是受限圓錐型或端口受限圓錐型,雙方必須一起開始傳輸。
需要注意的是,要使用STUN RFC中描述的技術並不一定需要使用STUN協議——還可以另外設計一個協議並把相同的功能集成到運行該協議的服務器上。
SIP之類的協議是使用UDP分組在Internet上傳輸音頻和/或視頻數據的。不幸的是,由於通信的兩個末端往往位於NAT之后,因此用傳統的方法是無法建立連接的。這也就是STUN發揮作用的地方。
STUN是一個客戶機-服務器協議。一個VoIP電話或軟件包可能會包括一個STUN客戶端。這個客戶端會向STUN服務器發送請求,之后,服務器就會向STUN客戶端報告NAT路由器的公網IP地址以及NAT為允許傳入流量傳回內網而開通的端口。
以上的響應同時還使得STUN客戶端能夠確定正在使用的NAT類型——因為不同的NAT類型處理傳入的UDP分組的方式是不同的。四種主要類型中有三種是可以使用的:完全圓錐型NAT、受限圓錐型NAT和端口受限圓錐型NAT——但大型公司網絡中經常采用的對稱型NAT(又稱為雙向NAT)則不能使用。
首先先介紹一些基本概念:
NAT(NetworkAddress Translators),網絡地址轉換:網絡地址轉換是在IP地址日益缺乏的情況下產生的,它的主要目的就是為了能夠地址重用。NAT從歷史發展上分為 兩大類,基本的NAT和NAPT(Network Address/Port Translator)。
最先提出的是基本的 NAT(注:剛開始其實只是路由器上的一個功能模塊),它的產生基於如下事實:一個私有網絡(域)中的節點中只有很少的節點需要與外網連 接(這是在上世紀90年代中期提出的)。那么這個子網中其實只有少數的節點需要全球唯一的IP地址,其他的節點的IP地址應該是可以重用的。因此,基本的NAT實現的功能很簡單,在子網內使用一個保留的IP子網段,這些IP對外是不可見的。子網內只有少數一些IP地址可以對應到真正全球唯一的 IP地址。如果這些節點需要訪問外部網絡,那么基本NAT就負責將這個節點的子網內IP轉化為一個全球唯一的IP然后發送出去。(基本的NAT會改變IP 包中的原IP地址,但是不會改變IP包中的端口)
關於基本的NAT可以參看RFC 1631
另外一種NAT叫做NAPT,從名稱上我們也可以看得出,NAPT不但會改變經過這個NAT設備的IP數據報的IP地址,還會改變IP數據報的TCP/UDP端口。基本NAT的設備可能我們見的不多(基本已經淘汰了),NAPT才是我們真正需要關注的。看下圖:
有一個私有網絡10.*.*.*,Client A是其中的一台計算機,這個網絡的網關(一個NAT設備)的外網IP是155.99.25.11(應該還有一個內網的IP地址,比如 10.0.0.10)。如果Client A中的某個進程(這個進程創建了一個UDP Socket,這個Socket綁定1234端口)想訪問外網主機18.181.0.31的1235端口,那么當數據包通過NAT時會發生什么事情呢?
首先NAT會改變這個數據包的原IP地址,改為155.99.25.11。接着NAT會為這個傳輸創建一個Session(Session是一個抽象的概 念,如果是TCP,也許Session是由一個SYN包開始,以一個FIN包結束。而UDP呢,以這個IP的這個端口的第一個UDP開始,結束呢,呵呵, 也許是幾分鍾,也許是幾小時,這要看具體的實現了)並且給這個Session分配一個端口,比如62000,然后改變這個數據包的源端口為62000。所以本來是(10.0.0.1:1234->18.181.0.31:1235)的數據包到了互聯網上變為了(155.99.25.11:62000->18.181.0.31:1235)。
一旦NAT創建了一個Session后,NAT會記住62000端口對應的是10.0.0.1的1234端口,以后從18.181.0.31發送到 62000端口的數據會被NAT自動的轉發到10.0.0.1上。(注意:這里是說18.181.0.31發送到62000端口的數據會被轉發,其他的 IP發送到這個端口的數據將被NAT拋棄)這樣Client A就與ServerS1建立以了一個連接。
上面的是一些基礎知識,下面的才是關鍵的部分了。
看看下面的情況:
接上面的例子,如果Client A的原來那個Socket(綁定了1234端口的那個UDPSocket)又接着向另外一個Server S2發送了一個UDP包,那么這個UDP包在通過NAT時會怎么樣呢?
這時可能會有兩種情況發生,一種是NAT再次創建一個Session,並且再次為這個Session分配一個端口號(比如:62001)。另外一種是NAT 再次創建一個Session,但是不會新分配一個端口號,而是用原來分配的端口號62000。前一種NAT叫做Symmetric NAT,后一種叫做Cone NAT。如果你的NAT剛好是第一種,那么很可能會有很多P2P軟件失靈。(可以慶幸的是,現在絕大多數的NAT屬於后者,即Cone NAT)
注:Cone NAT具體又分為3種:
(1)全克隆( Full Cone) : NAT把所有來自相同內部IP地址和端口的請求映射到相同的外部IP地址和端口。任何一個外部主機均可通過該映射發送IP包到該內部主機。
(2)限制性克隆(Restricted Cone) : NAT把所有來自相同內部IP地址和端口的請求映射到相同的外部IP地址和端口。但是,只有當內部主機先給IP地址為X的外部主機發送IP包,該外部主機才能向該內部主機發送IP包。
(3)端口限制性克隆( Port Restricted Cone) :端口限制性克隆與限制性克隆類似,只是多了端口號的限制,即只有內部主機先向IP地址為X,端口號為P的外部主機發送1個IP包,該外部主機才能夠把源端口號為P的IP包發送給該內部主機。
好了,我們看到,通過NAT,子網內的計算機向外連結是很容易的(NAT相當於透明的,子網內的和外網的計算機不用知道NAT的情況)。但是如果外部的計算機想訪問子網內的計算機就比較困難了(而這正是P2P所需要的)。那么我們如果想從外部發送一個數據報給內網的計算機有什么辦法呢?
首先,我們必須在內網的NAT上打上一個“洞”(也就是前面我們說的在NAT上建立一個 Session),這個洞不能由外部來打,只能由內網內的主機來打。而且這個洞是有方向的,比如從內部某台主機(比如:192.168.0.10)向外部的某個IP(比如:219.237.60.1)發送一個UDP包,那么就在這個內網的NAT設備上打了一個方向為219.237.60.1的“洞”,(這就是稱為UDP HolePunching的技術)以后219.237.60.1就可以通過這個洞與內網的192.168.0.10聯系了。(但是其他的IP不能利用這個洞)。
NAT的四種類型
Full cone NAT,亦即著名的一對一(one-to-one) · 一旦一個內部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發自iAddr:port1的包都經由eAddr:port2向外發送.任意外部主機都能通過給eAddr:port2發包到達iAddr:port1 |
|
Address-Restricted cone NAT · 一旦一個內部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發自iAddr:port1的包都經由eAddr:port2向外發送.任意外部主機(hostAddr:any)都能通過給eAddr:port2發包到達iAddr:port1的前提是:iAddr:port1之前發送過包到hostAddr:any. |
|
Port-Restricted cone NAT 類似受限制錐形NAT(Restricted cone NAT),但是還有端口限制。 · 一旦一個內部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發自iAddr:port1的包都經由eAddr:port2向外發送.一個外部主機(hostAddr:port3)能夠發包到達iAddr:port1的前提是:iAddr:port1之前發送過包到hostAddr:port3. |
|
Symmetric NAT(對稱NAT) · 每一個來自相同內部IP與port的請求到一個特定目的地的IP地址和端口,映射到一個獨特的外部來源的IP地址和端口。 · 只有曾經收到過內部主機封包的外部主機,才能夠把封包發回來 |
一、普通的直連式P2P實現
通過上面的理論,實現兩個內網的主機通訊就差最后一步了:那就是雞生蛋還是蛋生雞的問題了,兩邊都無法主動發出連接請求,誰也不知道誰的公網地址,那我們如何來打這個洞呢?我們需要一個中間人來聯系這兩個內網主機。
現在我們來看看一個P2P軟件的流程,以下圖為例:
首先,Client A登錄服務器,NAT A為這次的Session分配了一個端口60000,那么Server S收到的ClientA的地址是202.187.45.3:60000,這就是ClientA的外網地址了。 同樣,Client B登錄Server S,NATB給此次Session分配的端口是40000,那么Server S收到的B的地址是187.34.1.56:40000。
此時,Client A與ClientB都可以與Server S通信了。如果Client A此時想直接發送信息給Client B,那么他可以從Server S那兒獲得B的公網地址187.34.1.56:40000,是不是Client A向這個地址發送信息Client B就能收到了呢?答案是不行,因為如果這樣發送信息,NAT B會將這個信息丟棄(因為這樣的信息是不請自來的,為了安全,大多數NAT都會執行丟棄動作)。現在我們需要的是在NAT B上打一個方向為202.187.45.3(即Client A的外網地址)的洞,那么Client A發送到187.34.1.56:40000的信息,Client B就能收到了。這個打洞命令由誰來發呢?自然是Server S。
總結一下這個過程:如果Client A想向Client B發送信息,那么Client A發送命令給Server S,請求Server S命令Client B向Client A方向打洞。然后Client A就可以通過Client B的外網地址與Client B通信了。
注意:以上過程只適合於Cone NAT的情況,如果是Symmetric NAT,那么當Client B向Client A打洞的端口已經重新分配了,Client B將無法知道這個端口(如果Symmetric NAT的端口是順序分配的,那么我們或許可以猜測這個端口號,可是由於可能導致失敗的因素太多,這種情況下一般放棄P2P)。
二、STUN方式的P2P實現
STUN是RFC3489規定的一種NAT穿透方式,它采用輔助的方法探測NAT的IP和端口。毫無疑問的,它對穿越早期的NAT起了巨大的作用,並且還將繼續在NAT穿透中占有一席之地。
STUN 的探測過程需要有一個公網IP的STUN server,在NAT后面的UAC必須和此server配合,互相之間發送若干個UDP數據包。UDP包中包含有UAC需要了解的信息,比如NAT外網 IP,PORT等等。UAC通過是否得到這個UDP包和包中的數據判斷自己的NAT類型。
假設有如下UAC(B),NAT(A),SERVER(C),UAC的IP為IPB,NAT的IP為 IPA ,SERVER的 IP為IPC1 、IPC2。請注意,服務器C有兩個IP,后面你會理解為什么需要兩個IP。
(1)NAT的探測過程
STEP1:B 向C的IPC1的port1端口發送一個UDP包。C收到這個包后,會把它收到包的源IP和port寫到UDP包中,然后把此包通過IP1C和port1 發還給B。這個IP和port也就是NAT的外網IP和port,也就是說你在STEP1中就得到了NAT的外網IP。
熟悉NAT工作原理的應該都知道,C返回給B的這個UDP包B一定收到。如果在你的應用中,向一個STUN服務器發送數據包后,你沒有收到STUN的任何回應包,那只有兩種可能:1、STUN服務器不存在,或者你弄錯了port。2、你的NAT設備拒絕一切UDP包從外部向內部通過,如果排除防火牆限制規則,那么這樣的NAT設備如果存在,那肯定是壞了„„
當B收到此UDP后,把此UDP中的IP和自己的IP做比較,如果是一樣的,就說明自己是在公網,下步NAT將去探測防火牆類型,就不多說了(下面有圖)。如果不一樣,說明有NAT的存在,系統進行STEP2的操作。
STEP2:B向C的IPC1發送一個UDP包,請求C通過另外一個IPC2和PORT(不同與SETP1的IP1)向B返回一個UDP數據包(現在知道為什么C要有兩個IP了吧,為了檢測cone NAT的類型)。
我們來分析一下,如果B收到了這個數據包,那說明什么?說明NAT來着不拒,不對數據包進行任何過濾,這也就是STUN標准中的full cone NAT。遺憾的是,full cone nat太少了,這也意味着你能收到這個數據包的可能性不大。如果沒收到,那么系統進行STEP3的操作。
STEP3:B向C的IPC2的port2發送一個數據包,C收到數據包后,把它收到包的源IP和port寫到UDP包中,然后通過自己的IPC2和port2把此包發還給B。和step1一樣,B肯定能收到這個回應UDP包。此包中的port是我們最關心的數據,下面我們來分析:
如果這個port和step1中的port一樣,那么可以肯定這個NAT是個CONENAT,否則是對稱NAT。道理很簡單:根據對稱NAT的規則,當目的地址的IP和port有任何一個改變,那么NAT都會重新分配一個port使用,而在step3中,和step1對應,我們改變了IP和port。因此,如果是對稱NAT,那這兩個port肯定是不同的。
如果在你的應用中,到此步的時候PORT是不同的,那就只能放棄P2P了,原因同上面實現中的一樣。如果不同,那么只剩下了restrict cone 和port restrict cone。系統用 step4探測是是那一種。
STEP4:B向C的IP2的一個端口PD發送一個數據請求包,要求C用IP2和不同於PD的port返回一個數據包給B。
我們來分析結果:如果B收到了,那也就意味着只要IP相同,即使port不同,NAT也允許UDP包通過。顯然這是restrict cone NAT。如果沒收到,沒別的好說,port restrict NAT.
STUN 使用下列的算法(取自RFC3489)來發現 NAT gateways 以及防火牆(firewalls):
一旦路徑通過紅色箱子的終點時,UDP的連通是沒有可能性的。一旦通過黃色或是綠色的箱子,就有連接的可能。
stunURI = scheme ":" host [ ":" port ]
scheme = "stun" / "stuns"
RFC 3489:
RFC 5389:
STUNTMAN:http://www.stunprotocol.org/
https://github.com/jselbie/stunserver
http://sourceforge.net/projects/stun/
Stuntman- STUN server and client
High performance, production qualitySTUN server and client library
- mystun: STUN server and client library from the iptel.org guys. Old but mature. License: GPL, Homepage:http://developer.berlios.de/projects/mystun/. You have to download the file via CVS.
- Vovida STUN server (stund): STUN server and client library/application for Linux and Windows from the Vovida guys. Old but mature. License: Vovida Software License 1.0, Homepage:http://sourceforge.net/projects/stun/.
- WinSTUN: A Windows STUN client, part of the Vovida STUN server (see above). A nice application to test your NAT box. Homepage: http://sourceforge.net/projects/stun/files/WinStun/.
- reTurn: STUN/TURN server and client library, part of the resiprocate project. Server application is provided as well, but it seems incomplete (authentication). License: 3-clause BSD license. Homepage:http://www.resiprocate.org/ReTurn_Overview.
- restund: STUN/TURN server, supports authentication against a mysql DB. License: 3-clause BSD license. Homepage: http://www.creytiv.com/restund.html.
- TurnServer: STUN/TURN server. License: GPL3. Homepage: http://turnserver.sourceforge.net/.
- PJNATH : Open Source ICE, STUN, and TURN Library,http://www.pjsip.org/pjnath/docs/html/
- Numd:a free STUN/TURN serve, http://numb.viagenie.ca/
//from origin post
stunserver.org
stun.xten.com
stun.fwdnet.net
stun.fwdnet.net:3478
stun.wirlab.net
stun01.sipphone.com
stun.iptel.org
stun.ekiga.net
stun.fwdnet.net
stun01.sipphone.com (no DNS SRV record)
stun.softjoys.com (no DNS SRV record)
stun.voipbuster.com (no DNS SRV record)
stun.voxgratia.org (no DNS SRV record)
stun.xten.com
stunserver.org
stun.sipgate.net:10000
stun.softjoys.com:3478
//fromhttps://gist.github.com/zziuni/3741933
# source : http://code.google.com/p/natvpn/source/browse/trunk/stun_server_list
# A list of available STUN server.
stun.l.google.com:19302
stun1.l.google.com:19302
stun2.l.google.com:19302
stun3.l.google.com:19302
stun4.l.google.com:19302
stun01.sipphone.com
stun.ekiga.net
stun.fwdnet.net
stun.ideasip.com
stun.iptel.org
stun.rixtelecom.se
stun.schlund.de
stunserver.org
stun.softjoys.com
stun.voiparound.com
stun.voipbuster.com
stun.voipstunt.com
stun.voxgratia.org
stun.xten.com
TURN(全名 Traversal Using Relay NAT),是一種資料傳輸協議(data-transfer protocol)。允許在TCP或UDP的連線上跨越 NAT 或防火牆。
TURN是一個client-server協議。TURN的NAT穿透方法與STUN類似,都是通過取得應用層中的公有地址達到NAT穿透。但實現TURN client的終端必須在通訊開始前與TURN server進行交互,並要求TURN server產生"relay port", 也就是relayed-transport-address。這時 TURN server會建立peer, 即遠端端點(remote endpoints), 開始進行中繼(relay)的動作,TURN client利用relay port將資料傳送至peer, 再由peer轉傳到另一方的TURN client。
turnURI = scheme":" host [ ":" port ]
[ "?transport="transport ]
scheme = "turn" /"turns"
transport = "udp" /"tcp" / transport-ext
transport-ext = 1*unreserved
Table 1 shows how the <secure>,<port> and <transport> components are
populated from various URIs. For all these examples, the <host>
component is populated with"example.org".
+---------------------------------+----------+--------+-------------+
| URI | <secure> |<port> | <transport> |
+---------------------------------+----------+--------+-------------+
|turn:example.org |false | | |
| turns:example.org | true | | |
| turn:example.org:8000 | false | 8000 | |
| turn:example.org?transport=udp | false | | UDP |
| turn:example.org?transport=tcp | false | | TCP |
| turns:example.org?transport=tcp |true | | TLS |
+---------------------------------+----------+--------+-------------+
- Restund OpenSource Modular STUN/TURN Server (BSD License)
- Numb is a free STUN/TURN server.
- TurnServer - OpenSource TURN server.
- reTurn - opensource STUN/TURN server and client library (C++)
- TURN Server - High-Performance Open Source TURN/STUN server (BSD license) and client library (C)
https://code.google.com/p/rfc5766-turn-server/
搭建教程:http://www.dialogic.com/den/developer_forums/f/71/t/10238.aspx
http://zhangjunli177.blog.163.com/blog/static/138607308201341411384462/
- AnyFirewall - STUN, TURN & ICE library.
- Libnice - STUN, TURN & ICE library used in Pidgin, GNOME, MeeGo, etc.
- ice4j - STUN, TURN & ICE library in Java
6.4 交互式連接建立(Interactive Connectivity Establishment),一種綜合性的NAT穿越的技術。
交互式連接建立是由IETF的MMUSIC工作組開發出來的一種framework,可整合各種NAT穿透技術,如STUN、TURN(Traversal Using Relay NAT,中繼NAT實現的穿透)、RSIP(Realm Specific IP,特定域IP)等。該framework可以讓SIP的客戶端利用各種NAT穿透方式打穿遠程的防火牆。
一、ICE產生的背景
基於信令協議的多媒體傳輸是一個兩段式傳輸。首先,通過信令協議(如SIP)建立一個會話連接,通過該連接,會話雙方(Agent)通過SIP交互所承載的SDP消息彼此學習傳輸媒體時所必須的信息,針對媒體傳輸機制達成共識。然后,通常采用RTP協議進行媒體傳輸。
基於傳輸效率的考慮,通常在完成第一階段的交互之后,通信雙方另外建立一條直接的連接傳輸媒體。這樣就會減少傳輸時延、降低丟包率並減少開銷。這樣,用於SIP傳輸的鏈路就不再用於傳輸媒體。現在,問題出現了,由於不采用原來的鏈路,當傳輸雙方中任一方位於NAT之后,新的傳輸鏈接必須考慮NAT穿越問題。
通常有四種形式的NAT,對於每一中NAT方式,都有相應的解決方案。然而,每一種NAT穿越解決方案都局限於穿越對應得NAT方式,對於復雜的網絡環境來說,將會出現無法進行媒體傳輸的情況,同時這些方案給整個系統帶來了在不同程度上的脆弱性和復雜性。
在這種背景下,InteractiveConnectivity Establishment(交互式連通建立方式)也即ICE解決方案應運而生。ICE方式能夠在不增加整個系統的復雜性和脆弱性的情況下,實現對各種形式的NAT進行穿越,使得媒體流在通信雙方順利傳輸。
二、ICE工作的基本原理及特性
ICE是一種探索和更新式的解決方案。通過收集自己的和通信對端的盡可能多的網絡信息(各種網絡地址),嘗試在這些地址之間建立數據通道,並在此過程中不斷更新先前收集到的信息,從而找到和選擇一條能夠進行NAT穿越的數據通道。
其特性如下:ICE實現不是很復雜,支持TCP穿透,對NAT設備沒有要求,支持所有類型的NAT,必須在客戶端實現ICE,在網絡結構中需要STUN/TURN服務器,具有與協議無關性和良好的可擴展性,安全性和健壯性都比較好。
三、ICE工作的核心
如下內容是ICE實現NAT穿透的所要完成的核心處理。包括收集地址,對地址進行排序、配對,然后執行連通性檢查。
1、收集地址
Agent必須確定所有的候選的地址。這些地址包括本地網絡接口的地址和由它派生的其他所有地址。本地網絡地址包括本地網卡地址、VPN網絡地址、MIP網絡地址等。派生地址指的是通過本地地址向STUN服務器發送STUN請求獲得的網絡地址,這些地址分為兩類,一類是通過STUN的綁定發現用法得到的地址,稱為服務器反向候選地址(ServerReflexive Candidates)或服務器反向地址。另一類是通過中繼用法得到的,稱為中繼地址(RELAYEDCANDIDATES)。上面提到的兩種用法在相應的規范中提出。
服務器反向地址實際上就是終端的網絡包經過一重或多重NAT穿透之后,由STUN服務器觀察到的經過NAT轉換之后的地址。中繼地址是STUN服務器收到STUN請求后,為請求發起方在本機上分配的代理地址,所有被路由到該地址的網絡包將會被轉發到服務器反向地址,繼而穿透NAT發送到終端,因此如名字所示,它是STUN服務器完成中繼功能的地址。
為了找到服務器反向地址,Agent通過每一個主機候選地址(通過綁定主機某個接口和端口而獲取的候選地址),使用綁定發現用法(BindingDiscovery Usage [11])發送一個STUN綁定請求給STUN服務器(STUN服務器的地址已經配置或者可以通過某種途徑學習到)。當Agent發送綁定請求,NAT將分配一個綁定,它映射該服務器反向地址到主機候選地址。這樣,通過主機候選地址發送的外發包,將通過NAT轉換為通過服務器反向地址發送的包。發往服務器反向候選地址的包,將被NAT轉換為發往該主機候選地址的包,並轉發給Agent。
當Agent與STUN服務器之間存在多重NAT,那么STUN請求將會針對每一個NAT創建一個綁定,但是,只有最外部的服務器反向地址會被Agent發現。如果Agent不在任何NAT之后,那么,基候選傳輸地址將與服務器反向地址相同,服務器反向地址可以忽略。
關於中繼地址,STUN中繼用法允許STUN服務器作為一個媒體中繼器進行工作,在L與R之間進行轉發。為了發送消息到L,R必須發送消息給媒體中繼器,通過媒體中繼器轉發給L。反之亦然。
從L到R的消息其地址信息將兩次被重寫:第一次被NAT,第二次被STUN中繼服務器。這樣,R所了解的想與之通信的地址就是STUN中繼服務器的地址。這個地址就是中繼地址。
2、連通性檢查
Agent L收集到所有的候選地址后,就將它們按優先級高低進行排序,再通過信令信道發送給AgentR。這些候選地址作為SDP請求的屬性被傳輸。當R收到請求,它執行相同的地址收集過程,並且把它自己的候選地址作為響應消息發給請求者。這樣,每個Agent都將有一個完整的包含了雙方候選地址的列表,然后准備執行連通性檢查。
連通性檢查的基本原理是:
l 按照優先順序對候選地址進行排序。
l 利用每個候選地址發送一個檢查包。
l 收到另一個Agent的確認檢查包。
首先,Agent將本地地址集和遠程地址集進行配對,如本地有n個地址,遠程有m個地址,那么配成n*m對。對這些地址對進行連通性檢查是通過發送和接收STUN請求和響應完成的,此時,Agent在每個地址對的本地地址上,必須同時充當STUN服務器和STUN客戶端的角色。若通信雙方以某一地址對通過一個完整的四次握手,那么該地址對就是有效地址對。
四次握手是指:當通過地址對中的本地地址向地址對中遠程地址發送一個STUN請求,並成功收到STUN響應,稱該地址對是可接收的;當地址對中的本地地址收到地址對中遠程地址的一個STUN請求,並成功地響應,則稱該地址對為可發送的。若一個地址對是可接收的,同時又是可發送的,則稱該地址對是有效的,即通過連通性檢查。則此地址對可用於媒體傳輸。
通常在對稱NAT的情況下,在地址對驗證過程中,會出現發現以前收集地址時沒有收集到的地址對,這時就要對這些新的地址對進行連通性檢查。
3、對候選地址進行排序
由於收集候選地址時,收集的是所有的候選地址,為了能夠更快更好的找到能夠正常工作的候選地址對,對所有組合進行排序是勢在必行的。在此說明進行排序的兩個基本原則,詳細地排序算法將在后續文檔中描述。
l Agent為它的每個候選地址設置一個數值的優先級,這個優先級連同候選地址對一起發送給通信的對端。
l 綜合本地的和遠程的候選地址的優先級,計算出候選地址對的優先級,這樣,雙方的同一個候選地址對的優先級相同。以此排序,則通信雙方的排序結果相同。
4、進行SDP編碼
為了實現基於ICE的NAT穿越,對SDP進行了擴展,主要增加了四個屬性。分別是candidate屬性、ice-ufrag屬性、ice-pwd屬性和remote-candidates屬性。
candidate屬性為通信提供多種可能的候選地址中的一個。這些地址是使用STUN的端到端的連通性檢查為有效的。
remote-candidates屬性提供請求者想要應答者在應答中使用的遠程候選傳輸地址標識。
ice-pwd屬性提供用於保護STUN連通性檢查的密碼。
ice-ufrag屬性提供在STUN連通性檢查中組成用戶名的片斷。
四、一個例子
兩個Agent,L和R,使用ICE。它們都有單個IPv4接口。對於Agent L地址為10.0.1.1,對於R,192.0.2.1。它們都配置了單獨的STUN服務器(實際上是同一個),STUN服務器在192.0.2.2地址的3478端口監聽STUN請求。這個STUN服務器同時支持綁定發現和中繼功能。Agent L位於NAT之后,R位於公網。NAT有一個終端獨立的映射特性和依靠地址的過濾特性。NAT公網端的地址是192.0.2.3。網絡結構圖如下所示。
為了便於理解,傳輸地址用變量名代替。變量名的格式是entity-type-seqno,其中entity是具有該傳輸地址的接口所在實體,具體為是L、R、STUN或NAT之一。type不是PUB(地址位於公網)就是PRIV(地址位於內網)。seqno是在實體上的相同類型的各傳輸地址各自的序列號。每個變量都有一個IP地址和端口號,分別用varname.IP和varname.port表示,varname就是變量名。
STUN服務器有公網的傳輸層地址STUN-PUB-1(192.0.2.2:3478),綁定發現用法和中繼用法都使用這個地址。但在此處,兩個Agent都不使用中繼用法。
在呼叫過程中,STUN消息有被許多屬性注解。“S=”屬性表明消息的源傳輸地址,“D=”屬性表明消息的目標傳輸地址。“MA”屬性用於STUN綁定響應消息,指明映射的地址。
基於以上規定,媒體傳輸的初始過程如下圖所示。
消息
雙方都對獲取的傳輸地址進行配對,確定優先級並排序。之后,R開始執行其連通性檢查(消息9),由於來自L的候選地址是一個私有地址,所以此檢查必定失敗,而被丟棄。
同時,L收到應答后,除去包含了服務器反向地址的那對檢查,只剩一對檢查。基於此對地址,執行連通性檢查(消息10),經過NAT轉換后發送給R(消息11)。R收到之后發送響應給L(消息12),該消息中通過MA屬性指明映射地址,經過NAT之后返回給L(消息13),這樣L的連通性檢查成功。L檢查收到的消息13,以NAT-PUB-1為本地地址,R-PUB-1為遠程地址創建新的地址對,並添加到有效列表中。
ICE查看有效列表,發現有一對存在,就發送一個更新請求(消息14)給R,這個請求用於刪除沒有被選中的候選地址,並且指示遠程地址。
消息11到達R之后,會觸發R執行一個相同地址對的檢查,消息16-19反映了這個過程,在收到消息19的響應之后,R會像L一樣創建一對新的地址對(以R-PUB-1為本地地址,以NAT-PUB-1為遠程地址),並添加到有效列表中。這樣就可以進行媒體傳輸了。
五、總結
本文檔從ICE的產生背景入手,討論了ICE的基本原理及其特性,並對其工作的幾個核心部位進行了簡單的概述,在此基礎之上,分析了一個基於ICE通信的例子。所涉及的內容都是在宏觀上的考慮,進一步的詳細論述將在后續工作中展開。
1-4獲取服務器反向地址。消息5發送一個請求給R,該請求包括了本地主機候選地址和服務器反向地址。R收到消息5之后,通過消息6-7獲取服務器反向地址(由於R不在NAT之后,服務器反向地址與主機候選地址相同),然后發送一個應答(消息8)給L,應答中包括主機候選地址。至此,通信雙方都獲取了彼此的網絡信息。 ICE的典型應用環境
· InteractiveConnectivity Establishment (ICE): A Protocol for Network Address Translator(NAT) Traversal for Offer/Answer ProtocolsRFC5245
· SessionTraversal Utilities for NAT (STUN): RFC5389
· TraversalUsing Relays around NAT (TURN): Relay Extensions to STUN RFC5766
PJNATH- Open Source ICE, STUN, and TURN Library
本文檔定義了在Jabber/XMPP客戶間初始化及管理點對點的多媒體會話(sessions)(比如,聲音和圖像的交換)框架,它在一定程度上與現有的Internet標准具有互操作性。
警告:本標准跟蹤文檔是實驗性的。作為XMPP擴展協議發表,並不意味着XMPP標准基金會批准了這個協議。我們鼓勵對本協議進行探索性的實現,但在本協議的狀態發展為草稿之前,產品性的系統不應實現本協議。
文檔信息
系列:XEP
序號:0166
發布者:XMPP標准基金會
狀態:實驗性的
類型:標准跟蹤
版本:0.14
最后更新:2007-04-17
批准機構:XMPP理事會
依賴標准:XMPP核心標准
被替代標准:無
縮略名:未指派
Wiki頁:[1]
作者信息
法律通告
討論地點
相關的XMPP
術語
從Jabber/XMPP客戶內部初始化和管理點到點(p2p)互操作(象聲音、圖像、或文件共享交換)的未廣泛采用的標准已經有了。雖然,一些大 的服務提供商和Jabber/XMPP客戶已經寫出和實現了他們自己獨有的用於點對點信號處理的XMPP擴展,但這些技術沒有公開,並且總是沒有考慮到與公共轉換電話網絡(PSTN)或跨互聯網聲音協議(VoIP)的互操作性的需求。這些網絡建立在IETF的 __會話初始化協議(SIP)__上,在RFC3261[\[注1\]|XMPP文檔列表/XMPP擴展/XEP-0166]及其各種擴展中有詳細說明。
與此相反,唯一存在的開放協議是{link:初始化及協商會話的傳輸|http://www.xmpp.org/extensions/xep-0111.html}[\ [注2\]|XMPP文檔列表/XMPP擴展/XEP-0166],它使得初始化及管理點對點會話成為可能,卻沒有提供足夠多的在Jabber/XMPP 客戶端中能輕松地實現的關鍵性的信號處理語義。其結果導致在XMPP社區里有關信號處理的協議支離破碎。基本上,有兩中方法可以解決這個問題:
- 推薦所有的客戶端開發者實現雙重(XMPP+SIP)解決方案.
- 定義一個XMPP信號處理的完整特征的協議。
實現經驗表明,雙重方法也許不會在所有的計算平台上都可行-也許Jabber客戶端已經寫完了,或者雖然可行但並不值當。因此,定義一個XMPP信號處理協議似乎合情合理,這個協議能提供所需的信號處理語義,同時也使得與現有互聯網標准的互操作性相對簡單。
作為收到的XEP-0111返饋的一個結果,文檔的原作者(Joe Hildebrand 和Peter Saint-Andre)開始定義這樣的一個信號處理協議,代碼名為Jingle。通過與Google Talk小組\[4\]成員交流,發現形成的Jingle方法在概念上(甚至在句法上)都與在Google Talk程序中使用的信號處理協議非常相似。因此,為了保持互操作性和適用性,我們決定協調這兩種方法。因此,由本文詳細說明的信號處理協議基本上等同於現有的Google Talk協議,只是根據在XMPP標准基金會的標准化的實施及發表進程中收到的反饋作了一些調整。
Jingle的目的不是排擠或替代SIP。因為構建雙重XMPP+SIP客戶端非常困難,導致本質上程序控制的兩個中心,所以,我們將 Jingle設計成純的XMPP信號處理協議。Jingle意欲與SIP相互作用,這樣數百萬已布置的XMPP客戶端能夠加到現有的開放的VoIP網絡之 中,而不是將XMPP用戶限制在某個分離的獨特的VoIP網絡中。
這里定義的協議的目標是滿足如下需求:
- 使得XMPP內多種點對點會話(不限於聲音和視頻)的管理成為可能\[6\]。
- 明確分離信號處理通道(XMPP)與數據通道(例如,在RFC3550中說明的實時傳輸協議\[7\])。
- 明確分離內容描述格式(例如,用於語音聊天的)與內容傳輸方法(比如,在RFC768\[8\]中說明的用戶數據報協議)。
- 使得從現有會話中加入、修改、刪除內容類型成為可能。
- 使得實現支持標准的Jabber/XMPP客戶端中的協議相對容易。
- 當需要與非XMPP實體通訊的時候,盡可能將復雜性推到XMPP網絡與非XMPP網絡間的服務器端網關上。
本文檔僅定義了信號處理協議。其他文檔詳細說明了如下內容:
- 各種內容描述格式(音頻、視頻等),如有可能,將這些類型映射到會話描述協議(SDP,參見RFC4566[9])中;示例包括經由RTP的Jingle音頻[10]及經由RTP的視頻[11]。
- 各種內容傳輸方法;示例包括Jingle ICE傳輸方法\[12\]及Jingle原生UDP傳輸\[13\]。
- 映射Jingle信號處理協議到現有現有信號處理標准的方法,象IETF的會話初始化協議(SIP;參見RFC2361[14]),ITU的H.323協議(見H.323[15]);這些文檔即將完成。
1.1 2.術語表{anchor:術語表}
{table}
術語|定義
會話 |連接兩個實體的許多對已協商的內容傳輸方法和內容描述格式。它被限定在初始化請求和會話結束時間的時間段內。在一個會話的生命周期內,可加入或刪除成對的內容描述和內容傳輸方法。在某一時刻,一個會話至少有一個已協商的內容類型。
內容類型|一個內容描述和一個內容傳輸方法的組合。
內容描述|內容類型將被建立的格式,從形式上聲明了會話的一種用途(如,"voice"或"video")。這是會話的(即,傳輸的比特位)“是什么”,象建立語音通話時可接受的編碼器等。按照Jingle XML語法,內容類型是元素<description/>的命名空間。
傳輸方法|實體間建立數據流的方法。可能的傳輸包括ICE-TCP,原生UDP,帶內數據(inbanddata)等。這是關於會話“怎樣”的部分。按照Jingle XML語法,這是元素<transport/>的命名空間。內容傳輸方法定義了怎樣將比特位從一台主機傳到另一台。每種傳輸方法必須指定是有損的(允許丟包)還是無損的(不允許丟包)。
組件|組件是需要在端點間傳輸的特定會話上下文中特定內容類型的編號的數據流。協商每個組件的細節是由傳輸負責。根據內容類型和內容描述,一個內容描述可能需要多個組件來通訊(例如,音頻內容類型也許用兩個組件:一個傳輸RTP流,另一個傳輸RTCP定時信息)。
{table}
1.1 4.概念及方法{anchor:概念和方法}
Jingle有三部分組成,每部分由自己的語法、語義及狀態機:
1. 總會話管理
1. 內容描述格式("什么")
1. 內容傳輸方法("怎樣")
本文檔定義了總會話管理的語義和語法。另有單獨的文檔,詳細說明了用於內容描述和內容傳輸方法的可插入式“槽(slots)”。基於完整性的考慮,本文檔也包含了與描述格式和傳輸方法有關的全部動作的示例。
從最根本上來說,協商Jingle會話的過程是這樣的:
1. 一個用戶(“發起方”)向另一個用戶(“接收方”)發送一個帶內容類型的會話請求,會話請求至少包含一個內容類型。
1. 如果接收者想要處理,它會通過發送一個IQ結果暫時接受這個請求。
1. 發起方和接收方以盡可能快的速度交換可能的傳輸候選方法(進一步協商前的傳輸候選方法的快速發送,是為了縮短媒體數據可流動前的必要時間)。
1. 檢查這些傳輸候選方法的連通性。
1. 一旦接收方找到了媒體數據可流動的候選方法,接收方會向初始方發出一個“會話接受”動作。
1. 雙發開始通過協商好的候選方法發送媒體數據。
如果雙方隨后發現了更好的候選方法,他們會進行“內容修改”協商,然后轉到這個更好的候選方法上。自然他們也會修改與會話相關的其他參數(如給語音聊天加入視頻)。
1.1 4.1總會話管理{anchor:總會話管理}
總會話管理的狀態機(也即每個Session ID的狀態)如下:
{code}
o
|
|會話開始
|
| +-----------+
|/ |
阻塞 o----------+ |
| |內容接受 | |
| |內容修改 | |
| |內容移除 | |
| |會話信息 | |
| |傳輸信息 | |
| +--------+ |
| |
|會話接受 |
| |
活動 o----------+ |
| |內容接受 | |
| |內容增加 | |
| |內容修改 | |
| |內容移除 | |
| |會話信息 | |
| |傳輸信息 | |
| +--------+ |
| |
+-------------+
|
|會話中止
|
o 結束
{code}
總會話狀態有三種:
1. 阻塞
1. 活動
1. 結束
與管理總體Jingle會話相關的動作如下:
__表2:Jingle動作__
{table}
動作|描述
內容接受|接受從另一方受到的內容增加或內容移除。
內容增加|增加一個或多個內容類型到會話中。這個動作 __不能__ 在會話的 __阻塞__ 狀態時發出。當一方發出內容增加的時候,它 __必須__ 忽略從另一方收到的任何動作,直到收到內容增加的確認。
內容修改|改變現有的內容類型。接收方 __不能__ 以另一個內容修改來回應內容修改動作。
內容移除|從會話中移除一個或多個內容類型。
會話接受|最終接受會話協商。表明這個動作也適合內容接受(進而適合描述接受和傳輸接收)。
會話信息|發送會話級的信息/消息,如響鈴消息(對Jingle音頻來說)。
會話開始|請求一個新Jingle會話協商。
會話中止|結束現有會話。
傳輸信息|交換傳輸候選方法;主要用在XEP-0176中,也可以用在其他規范中。
{table}
1.1 5.會話流{anchor:會話流}
1.1 5.1資源確定{anchor:資源確定}
為了開始Jingle會話,發起方必須確定接收方的哪種XMPP資源最適合想要的內容描述格式。如果聯系方僅有一種XMPP資源,這一任務 __必須__ 用服務發現\[18\]或用在實體能力\[19\]中說明的基於現身(presence-based)的簡介(profile)的服務發現來完成。
很自然,用實體能力而不是向用戶花名冊中的每個聯系人發送服務發現請求效率更高。由此,某個客戶版本對Jingle及各種Jingle內容 描述格式和內容傳輸方法的支持等一般性信息(而不是每個的JID的)都能確定下來,這些信息隨后被緩存。具體細節參考EXP-0115。
如果聯系方有不止一種XMPP資源,可能僅有一種資源支持Jingle和想要的內容描述格式,在這種情況下,用戶 __必須__ 用這一資源初始化Jingle信號處理。
如果聯系方有超過一種XMPP資源支持Jingle和想要的內容描述格式, __建議__ 用 ~~資源應用優先權~~\[20\]來確定哪種資源最適合初始化Jingle會話。
1.1 5.2初始化{anchor:初始化}
一旦發起方發現了接收方哪種XMPP資源適合想要的內容描述格式,就向接收方發送一個會話初始化請求。這個請求是個包含<jingle/>元素的IQ集,元素的命名空間為‘http://www.xmpp.org/extensions/xep-0166.html#ns’。<jingle/> 元素 __必須__ 有'action' 、'initiator'和'sid'屬性(兩個字符唯一區分會話)。初始化時,'action'屬性 __必須__ 是"session-initiate",<jingle/>元素 __必須__ 包含一個或多個<content/>元素,每個元素定義會話期間的要傳輸的內容類型;每個<content/>元素分別包 含<description/>子元素,指定想要的內容描述格式,<transport/>子元素,指定了可能的內容傳輸方法。 如果任何一方想要對相同的內容描述使用多種傳輸方法,則必須發送多個<content/>元素。
下面是一個Jingle會話初始化請求的例子,會話包含了音頻和視頻:
__例1. 初始化示例__
{code:xml}
<iqfrom='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='jingle1'type='set'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='session-initiate'
initiator='romeo@montague.net/orchard'
sid='a73sjjvkla37jfea'>
<content creator='initiator' name='this-is-the-audio-content'>
<description xmlns='http://www.xmpp.org/extensions/xep-0167.html#ns'>
...
</description>
<transport xmlns='http://www.xmpp.org/extensions/xep-0176.html#ns'/>
</content>
<content creator='initiator' name='this-is-the-video-content'>
<description xmlns='http://www.xmpp.org/extensions/xep-0180.html#ns'>
...
</description>
<transport xmlns='http://www.xmpp.org/extensions/xep-0176.html#ns'/>
</content>
</jingle>
</iq>
{code}
注意:元素<description/>和<transport/>的語法、語義超出了本規范的范圍,它們在相關的規范中定義。
元素<jingle/>有如下屬性:
- ‘action’屬性是 __必需的__ ;它指定了本文中列出Jingle動作(如,"session-initiate")。
- 'initiator'屬性是發起會話流的實體的完整JID(可能與IQ集中地址'from'不同)。
- ‘reasoncode’屬性是 __可選的__ ,指定機器可讀的動作發送目的(如,用在會話中止動作的"connectivity-error")。
- 'reasontext'屬性是 __可選的__ ,指定人可讀的動作發送目的(如,用於會話中止動作的"Sorry,gotta go")。
- 'responder'屬性(見下面的例子)是回應發起的實體的完整JID(可能與IQ集中'to'地址不同)。
- ‘sid’屬性是由發起方產生的隨機的會話識別符;它 __應該__ 符合XML Nmtoken production\[21\],這樣對&之類的字符就不需要進行XML字符的轉義了。(注意:'sid'屬性可有效地映射到SIP的‘Call-ID’參數)
元素<content/>有如下屬性:
- 'creator'屬性是 __必需的__ ;它指明了那一方最初產生的內容描述(用於防止修改時的競態條件(race conditions)的發生)。
- ‘name’屬性是 __必需的__ ;它指明了內容類型的獨特的名字或識別符(這個識別符是不透明的,沒有語義上的意義)。
- 'profile'屬性是 __推薦的__ ;對有些內容類型,它說明了所用的簡介(如,在實時傳輸協議上下文中的"RTP/AVP")
- ‘senders’屬性是 __推薦的__ ;它說明了會話中那個實體將要產生內容;允許的值是"initiator","recipient"或"both"(缺省值是"both")。
注意:為了加速會話建立,發起方 __可以__ 在發送“session-initiate”消息后接到接收方響應之前,立即發送傳輸候選方法(如,用於ICE傳輸的協商),(也就是說,發起方 __必須__ 認定會話是存在的,即使還沒有收到響應)。如果按順序傳輸的話,接收方應在收到"session-initiate"消息后,應收到諸如"transport-info"之類的消息(如果沒收到,那么接收方返回<unknown-session/>錯誤是恰當的,因為按照它 的狀態機,會話並不存在)。
1.1 5.3接收方響應{anchor:接收方響應}
除非有錯誤發生,接收方 __必須__ 收到的發起請求:
例2. 接收方響應發起請求
{code:xml}
<iq type='result'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'/>
{code}
如果接收方響應了發起請求,雙方都必須認定會話處在 __阻塞__ 狀態。
有幾種原因接收方會返回一個錯誤,而不是響應發起請求:
- 對接收方來說發起方是未知的(比如,通過在線(presence)訂閱),接收方不能與未知實體通訊。
- 接收方希望轉到另一個地址。
- 接收方不支持Jingle。
- 接收方不支持任何指定的內容描述格式。
- 接收方不支持任何指定的內容傳輸方法。
- 發起請求格式是錯誤的。
如果對接收方來說發起方是未知的(比如,通過在線訂閱),並且接收方有不與未知實體經由Jingle通訊的策略,則接收方 __應該__ 返回一個<service-unavailable/>錯誤。
例3. 發起方未知
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
{code}
如果接收方希望轉向另一個地址,它 __應該__ 返回一個<redirect/>錯誤。
例4. 接收方轉向
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<redirect xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>xmpp:voicemail@capulet.com</redirect>
</error>
</iq>
{code}
如果接收方不支持Jingle,則 __必須__ 返回一個錯誤。
例5. 接收方不支持Jingle
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
{code}
如果接收方不支持任何一種指定的內容描述格式,它 __必須__ 返回一個<feature-not-implemented/>錯誤,和具有Jingle特性的出錯條件的<unsupported-content>。
例6. 接收方不支持任何內容描述格式
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<unsupported-content xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns-errors'/>
</error>
</iq>
{code}
如果接收方不支持任何一種指定的內容傳輸方法,它 __必須__ 返回一個<feature-not-implemented/>錯誤,和具有Jingle特性的出錯條件的<unsupported-transports>。
例7. 接收方不支持任何內容傳輸方法
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<unsupported-transports xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns-errors'/>
</error>
</iq>
{code}
如果發起請求的格式錯誤,接收方 __必須__ 返回一個<bad-request/>錯誤。
例8. 發起請求的格式錯誤
{code:xml}
<iq type='error'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='jingle1'>
<error type='cancel'>
<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>
{code}
1.1 5.4拒絕{anchor:拒絕}
為拒絕會話發起請求,接收方 __必須__ 響應收到的會話發起請求,然后按[中止|XMPP文檔列表/XMPP擴展/XEP-0166#中止]中描述的方法中止會話。
1.1 5.5 協商{anchor:協商}
一般情況下,雙方在達成可接受的一系列內容類型、內容描述格式和內容傳輸方法前,協商是必要的。可能要協商的這些參數的組合是很多的,這里並沒有列出全部(有些在各種內容描述格式和內容傳輸方法規范中列出)。
一個會話級的協商是移除一種內容類型。例如,讓我們設想,有一天朱麗葉的心情很糟糕,她當然不想在和羅密歐的Jingle會話中包含視頻,所以她給羅密歐發送了一個“內容移除”請求:
例9. 內容類型移除
{code:xml}
<iqfrom='juliet@capulet.com/balcony' to='romeo@montague.net/orchard' id='reduce1'type='set'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='content-remove'
initiator='romeo@montague.net/orchard'
sid='a73sjjvkla37jfea'>
<content creator='initiator' name='this-is-the-video-content'/>
</jingle>
</iq>
{code}
實體收到了這個會話縮減請求,然后響應這個請求:
例10. 響應
{code:xml}
<iqfrom='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='reduce1'type='result'/>
{code}
如果縮減的結果是會話不再有任何內容類型,收到會話縮減的實體 __應該__ 向另一方發送會話中止動作(因為沒有內容類型的會話是無效的)。
另一個會話級的協商是增加一個內容類型;然而,這個動作__必不能__ 在會話處於 __阻塞__ 狀態時來做,只有在會話處於 __活動__ 狀態時才可以。
1.1 5.6 接受{anchor:接受}
協商過內容傳輸方法和內容描述格式后,如果接收方確定能夠建立連接,它將向發起方法送確定接受:
例11. 接收方確定接受呼叫
{code:xml}
<iq type='set'from='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'id='accept1'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='session-accept'
initiator='romeo@montague.net/orchard'
responder='juliet@capulet.com/balcony'
sid='a73sjjvkla37jfea'>
<content creator='initiator' name='this-is-the-audio-content'>
<description xmlns='http://www.xmpp.org/extensions/xep-0167.html#ns'>
...
</description>
<transport xmlns='http://www.xmpp.org/extensions/xep-0177.html#ns'>
<candidate .../>
</transport>
</content>
</jingle>
</iq>
{code}
<jingle/>元素 __必須__ 包含一個或多個<content/>元素,后者 __必須__ 包含一個<description>元素和一個<transport/>元素。<jingle/>元素 __應該__ 有‘responder’屬性,以明確指明響應實體的完整JID,有關當前Jingle會話的所有通訊,發起方應該向這個JID發送。
然后發起方響應接收方的確認接受:
例12. 發起方響應確認接受
{code:xml}
<iq type='result'to='juliet@capulet.com/balcony' from='romeo@montague.net/orchard'id='accept1'/>
{code}
此時,發起方和接收方可以通過協商好的連接開始發送內容了。
如果一方無法找到合適的內容傳輸方法,它 __應該__ 下面描述的那樣中止會話。
1.1 5.7 修改活動會話{anchor:修改活動會話}
為修改一個活動會話,任一方都可向另一方發送"content-remove"、"content-add"、"content-modify", "description-modify"、"transport-modify" 動作。然后接收方發送恰當的"-accept"或"-decline"動作,也可能首先發送一個"-info"動作。
如果雙方同時發送了修改消息,那么會話發起方的修改消息__必須__ 勝過接收方的修改消息,發起方 __應該__ 返回一個<unexpected-request/>錯誤。
修改活動會話的一個例子是增加一個會話內容。例如,設想一下朱麗葉的心情好了,現在想加入視頻。於是向羅密歐發送"content-add"請求:
例13. 增加一個內容類型
{code:xml}
<iqfrom='juliet@capulet.com/balcony' to='romeo@montague.net/orchard' id='add1'type='set'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='content-add'
initiator='romeo@montague.net/orchard'
sid='a73sjjvkla37jfea'>
<content creator='responder' name='video-is-back'>
<description xmlns='http://www.xmpp.org/extensions/xep-0180.html#ns'>
...
</description>
<transport xmlns='http://www.xmpp.org/extensions/xep-0177.html#ns'>
<candidate .../>
</transport>
</content>
</jingle>
</iq>
{code}
實體接收到會話擴展請求,響應這個請求,如果可接受,返回一個內容接受:
The entity receiving the sessionextension request then acknowledges the request and, if it is acceptable,returns a content-accept:
例14. 響應
{code:xml}
<iqfrom='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='add1'type='result'/>
{code}
例15. 內容接受
{code:xml}
<iqfrom='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='add2'type='set'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='content-accept'
initiator='romeo@montague.net/orchard'
sid='a73sjjvkla37jfea'>
<content creator='responder' name='video-is-back'>
<description xmlns='http://www.xmpp.org/extensions/xep-0180.html#ns'>
...
</description>
<transport xmlns='http://www.xmpp.org/extensions/xep-0177.html#ns'>
<candidate .../>
</transport>
</content>
</jingle>
</iq>
{code}
另一方響應接受。
例16. 響應
{code:xmk}
<iqfrom='juliet@capulet.com/balcony' to='romeo@montague.net/orchard' id='add2'type='result'/>
{code}
1.1 5.8 中止{anchor:中止}
為了順利地結束會話(在響應了初始化請求后的任何時候都__可以__ 這么做,包括立即想立刻拒絕請求的時候),無論接收方還是初始方都 __必須__ 向對方發送一個“中止”動作。
例17 接收方中止會話
{code:xml}
<iqfrom='juliet@capulet.com/balcony'
id='term1'
to='romeo@montague.net/orchard'
type='set'>
<jingle xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
action='session-terminate'
initiator='romeo@montague.net/orchard'
reason='Sorry, gotta go!'
sid='a73sjjvkla37jfea'/>
</iq>
{code}
另一方(這里是初始方)必須響應會話中止:
例18. 初始方響應中止
{code:xml}
<iq type='result'to='juliet@capulet.com/balcony' from='romeo@montague.net/orchard'id='term1'/>
{code}
注意:一旦實體發送了"會話中止"動作,它 __必須__ 認定會話已中止(即使在收到對方的響應之前)。如果中止方在發送了“會話中止”動作后收到了對方額外的IQ設置,它 __必須__ 返回一個<unknown-session/>錯誤。
不幸的是,並非所有會話都順利地結束。下面的事件 __必須__ 認定為會話結束事件,對內容描述格式和內容傳輸方法的進一步協商 __必須__ 通過協商一個新會話來完成:
- 從對方那里收到‘會話轉向’或'會話中止'動作。
- 從對方那收到<presence type='unavailable'/>。
特別地,如果一方從對方收到的在線(presence)類型是"未知(unavailable)"的話,則它 __必須__ 認定會話處於 __結束__ 狀態。
例19. 接收方離線
{code:xml}
<presencefrom='juliet@capulet.com/balcony' to='romeo@montague.net/orchard'type='unavailable'/>
{code}
自然在這種情況下初始方沒什么可響應的。
1.1 5.9 通知消息{anchor:通知消息}
Jingle會話開始后的任何時候,任一實體都 __可以__ 向對方發送通知消息,比如,改變內容傳輸方法或內容描述格式的參數,通知對方一個會話開始請求已經列隊等待,設備正在響鈴,或一個事先計划的事件已經發生或將要發生。通知消息 __必須__ 是帶有<jingle/>元素的IQ設置(IQ-set)指令,<jingle/>元素的'action'屬性的值 是"session-info","description-info"或“transport-info”之一;<jingle/>元素 __必須__ 進一步包含有效的子元素(會話,內容描述格式或內容傳輸方法)來說明正在交流的信息。如果一個活動會話的任一方收到了一個空的“session-info”消息,它 __必須__ 返回一個空的IQ結果;這樣,一個空的“session-info”消息可用作一個“ping”,來確定會話的活性。(本規范的未來版本也許會定義與“session-info”動作相關的內容載荷。)
1.1 6. 出錯處理{anchor:出錯處理}
Jingle專有的出錯條件如下。
表3:其他出錯條件
{table}
Jingle條件|xmpp條件|說明
<out-of-order/>|<unexpected-request/>|請求不可能在狀態機的這一點發生(比如,會話接受后再次初始化)。
<unknown-session/>|<bad-request/>|指定會話的‘sid’屬性對接收方未知(例如,根據接收方的狀態機會話已經不再有效,因為接收方先前已中止了會話)
<unsupported-content/>|<not-acceptable/>|接收方不支持任何期望的內容描述格式。
<unsupported-transports/>|<not-acceptable/>|接收方不支持任何期望的內容傳輸方法。
{table}
1.1 7. 支持性檢測{anchor:支持性檢測}
如果一個實體支持Jingle,它 __必須__ 在響應{link:服務發現|http://www.xmpp.org/extensions/xep-0030.html}\[22\]信息請求時,通過返回特性“http://www.xmpp.org/extensions/xep-0166.html#ns”(見有關{link:協議命名空間%7Chttp://www.xmpp.org/extensions/xep-0166.html#ns})將這一事實公布出去。
例20. 服務發現信息請求
{code:xml}
<iq from='romeo@montague.net/orchard'
id='disco1'
to='juliet@capulet.com/balcony'
type='get'>
<query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
{code}
例21. 服務發現信息響應
{code:xml}
<iq from='juliet@capulet.com/balcony'
id='disco1'
to='romeo@montague.net/orchard'
type='result'>
<query xmlns='http://jabber.org/protocol/disco#info'>
...
<feature var='http://www.xmpp.org/extensions/xep-0166.html#ns'/>
...
</query>
</iq>
{code}
1.1 8.使用協議的一致性{anchor:使用協議的一致性}
1.1 8.1 應用類型{anchor:應用類型}
說明某種Jingle應用類型的文檔(比如,經由RTP的音頻) __必須__ 定義:
1. 為封裝進Jingle,如何成功地進行內容協商。
1. 用於表現內容的<description/>元素及相關語義。
1. 能否及怎樣將內容描述映射到會話描述協議上。
1. 是通過可靠的還是有損的傳輸方式(或兩者都是)來傳輸內容。
1. 通過可靠或有損傳輸來收發內容的精確說明。
1.1 8.2 傳輸方法{anchor:傳輸方法}
說明Jingle傳輸方法的文檔(比如,純UDP) __必須__ 定義:
1. 為封裝進Jingle,怎樣成功地進行傳輸協商。
1. 用於表現傳輸類型的<transport/>元素及相關語義。
1. 傳輸是可靠的還是有損的。
1. 傳輸是否及怎樣處理在這定義的組件(例如,對實時控制協議來說)。
1.1 9. 安全性事項{anchor:安全性事項}
1.1 9.1 拒絕服務
Jingle會話可能是資源密集型的。因此,有可能用增加過多Jingle會話負擔的方法向一個實體發動決絕服務攻擊。必須小心地只從已知的實體那接受內容,並且只接受實體設備能處理的會話。
1.1 9.2 通過網關通訊{anchor:通過網關通訊}
Jingle通訊可通過網關與非XMPP網絡完成,這些網絡的安全特性與XMPP有很大的不同。(例如,在有些SIP網絡中鑒定是可選的,“from”地址可輕易偽造。)與這些網通通訊時必須小心。
1.1 IANA事項{anchor:IANA事項}
本文檔要求不與{link:互聯網指派數字授權(IANA)|http://www.iana.org/}\[23\]相互作用。
1.1 11. XMPP注冊處事項{anchor:XMPP注冊處事項}
1.1 1\1.1 協議命名空間{anchor:協議命名空間}
在本規范成為草稿狀態之前,其相關的命名空間是“http://www.xmpp.org/extensions/xep-0166.html#ns”和"http://www.xmpp.org/extensions/xep-0166.html#ns-errors“;隨着規范的發展,{link:XMPP注冊員\[24\]|http://www.xmpp.org/registrar/}將按照{link:XMPP注冊處功能\[25\]|http://www.xmpp.org/extensions/xep-0053.html}的第四節中定義的過程來發布永久命名空間。
1.1 11.2 Jingle內容描述格式注冊
XMPP注冊處會維護Jingle內容描述格式的注冊。整個內容描述格式的注冊會在單獨的規范中定義(不在本文檔中)。定義在XEP系列里 的內容描述格式也 __必須__在XMPP注冊處注冊,其結果是協議的URN的格式是“urn:xmpp:jingle:description:name”(其中“name”是內容描述格式的注冊名)。
為提交注冊的新值,注冊人須按下面的格式定義一個XML段,內容包括相關的XMPP擴展協議,或者將它發送到<registrar@xmpp.org>。
{code:xml}
<content>
<name>the name of the content description format</name>
<desc>a natural-language summary of the content description format</desc>
<transport>whether the content should be sent over a "reliable" or "lossy" transport</transport>
<doc>the document in which this content description format is specified</doc>
</content>
{code}
1.1 11.3 Jingle內容傳輸方法注冊
XMPP注冊處會維護Jingle內容傳輸方法的注冊。整個內容傳輸方法的注冊會在單獨的規范中定義(不在本文檔中)。定義在XEP系列里 的內容傳輸方法也 __必須__在XMPP注冊處注冊,其結果是協議的URN的格式是“urn:xmpp:jingle:transport:name”(其中“name”是內容傳輸方法的注冊名)。
為提交注冊的新值,注冊人須按下面的格式定義一個XML段,內容包括相關的XMPP擴展協議,或者將它發送到<registrar@xmpp.org>。
{code:xml}
<transport>
<name>the name of the content transport method</name>
<desc>a natural-language summary of the content transport method</desc>
<type>whether the transport method is "reliable" or "lossy"</type>
<doc>the document in which this content transport method is specified</doc>
</transport>
{code}
1.1 11.4 Jingle原因代碼注冊{anchor:Jingle原因代碼注冊}
1.1 11.4.1 過程{anchor:過程}
XMPP注冊處會維護一份Jingle動作原因的注冊。
為提交注冊的新值,注冊人須按下面的格式定義一個XML段,內容包括相關的XMPP擴展協議,或者將它發送到<registrar@xmpp.org>。
{code:xml}
<reason>
the value of the 'reasoncode' attribute</name>
<desc>a natural-language summary of the reason code</desc>
<doc>the document in which this reason code is specified</doc>
</reason>
{code}
1.1 11.4.2 初始注冊{anchor:初始注冊}
下面提交的原因代碼注冊從2007年4月開始使用。完整內容和最新的原因代碼列表參見注冊。
{code:xml}
<reason>
<code>connectivity-error
<desc>the action (e.g., session-terminate) is related to connectivity problems</desc>
<doc>XEP-0166</doc>
</reason>
<reason>
general-error
<desc>the action (e.g., session-terminate) is related to a non-specific application error</desc>
<doc>XEP-0166</doc>
</reason>
<reason>
media-error
<desc>the action (e.g., session-terminate) is related to media processing problems</desc>
<doc>XEP-0166</doc>
</reason>
<reason>
no-error
<desc>the action is generated during the normal course of state management</desc>
<doc>XEP-0166</doc>
</reason>
{code}
1.1 12. XML方案(Schemas){anchor:XML Schemas}
1.1 12.1 Jingle{anchor:Jingle}
{code:xml}
<?xml version='1.0'encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='http://www.xmpp.org/extensions/xep-0166.html#ns'
xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns'
elementFormDefault='qualified'>
<xs:element name='jingle'>
<xs:complexType>
<xs:sequence minOccurs='1' maxOccurs='unlimited'>
<xs:element ref='content'/>
</xs:sequence>
<xs:attribute name='action' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='content-accept'/>
<xs:enumeration value='content-add'/>
<xs:enumeration value='content-modify'/>
<xs:enumeration value='content-remove'/>
<xs:enumeration value='session-accept'/>
<xs:enumeration value='session-info'/>
<xs:enumeration value='session-initiate'/>
<xs:enumeration value='session-terminate'/>
<xs:enumeration value='transport-info'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='initiator' type='xs:string' use='required'/>
<xs:attribute name='reasoncode' type='xs:string' use='optional'/>
<xs:attribute name='reasontext' type='xs:string' use='optional'/>
<xs:attribute name='responder' type='xs:string' use='optional'/>
<xs:attribute name='sid' type='xs:NMTOKEN' use='required'/>
</xs:complexType>
</xs:element>
<xs:element name='content'>
<xs:complexType>
<xs:choice minOccurs='0'>
<xs:sequence>
<xs:any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</xs:sequence>
</xs:choice>
<xs:attribute name='creator' use='required'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='initiator'>
<xs:enumeration value='responder'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name='name' use='required' type='xs:string'/>
<xs:attribute name='profile' use='optional' type='xs:string'/>
<xs:attribute name='senders' use='optional' default='both'>
<xs:simpleType>
<xs:restriction base='xs:NCName'>
<xs:enumeration value='both'>
<xs:enumeration value='initiator'>
<xs:enumeration value='responder'/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
{code}
1.1 12.2 Jingle出錯信息{anchor:Jingle出錯信息}
{code:xml}
<?xml version='1.0'encoding='UTF-8'?>
<xs:schema
xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='http://www.xmpp.org/extensions/xep-0166.html#ns-errors'
xmlns='http://www.xmpp.org/extensions/xep-0166.html#ns-errors'
elementFormDefault='qualified'>
<xs:element name='out-of-order' type='empty'/>
<xs:element name='unknown-session' type='empty'/>
<xs:element name='unsupported-content' type='empty'/>
<xs:element name='unsupported-transports' type='empty'/>
<xs:simpleType name='empty'>
<xs:restriction base='xs:string'>
<xs:enumeration value=/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
{code}
GYP 簡介:轉載自:http://blog.xiaogaozi.org/2011/10/29/introduction-to-gyp/
說起項目構建工具,Linux 用戶最熟悉的恐怕就是 Autotools,它將編譯安裝這個步驟大大簡化。但對於項目作者來說,想要使用 Autotools 生成有效的配置文件着實需要下一番功夫,用現在流行的話來說就是用戶體驗不夠友好。對 Unix shell 的依賴,也使得 Autotools 天生對於跨平台支持不佳。
與其類似的有 CMake,CMake 使用 C++ 編寫,原生支持跨平台,不需要像 Autotools 那樣寫一堆的配置文件,只需一個 CMakeLists.txt 文件即可。簡潔的使用方式,強大的功能使得我立馬對 CMake 情有獨鍾。在后來的使用過程中,雖然會遇到一些因為使用習慣帶來的小困擾,但我對於 CMake 還是基本滿意的。直到我發現了 GYP。
GYP(Generate Your Projects)是由 Chromium 團隊開發的跨平台自動化項目構建工具,Chromium 便是通過 GYP 進行項目構建管理。為什么我要選擇 GYP,而放棄 CMake 呢?功能上 GYP 和 CMake 很是相似,在我看來,它們的最大區別在於配置文件的編寫方式和其中蘊含的思想。
編寫 CMake 配置文件相比 Autotools 來說已經簡化很多,一個最簡單的配置文件只需要寫上源文件及生成類型(可執行文件、靜態庫、動態庫等)即可。對分支語句和循環語句的支持也使得 CMake 更加靈活。但是,CMake 最大的問題也是在這個配置文件,請看下面這個示例文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
cmake_minimum_required(VERSION 2.8) project(VP8 CXX)
add_definitions(-Wall) cmake_policy(SET CMP0015 NEW) include_directories("include") link_directories("lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "../lib") set(VP8SRC VP8Encoder.cpp VP8Decoder.cpp)
if(X86) set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_SYSTEM_PROCESSOR i386) set(CMAKE_OSX_ARCHITECTURES "i386")
add_library(vp8 STATIC ${VP8SRC}) elseif(IPHONE) if(SIMULATOR) set(PLATFORM "iPhoneSimulator") set(PROCESSOR i386) set(ARCH "i386") else() set(PLATFORM "iPhoneOS") set(PROCESSOR arm) set(ARCH "armv7") endif()
set(SDKVER "4.0") set(DEVROOT "/Developer/Platforms/${PLATFORM}.platform/Developer") set(SDKROOT "${DEVROOT}/SDKs/${PLATFORM}${SDKVER}.sdk") set(CMAKE_OSX_SYSROOT "${SDKROOT}") set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR ${PROCESSOR}) set(CMAKE_CXX_COMPILER "${DEVROOT}/usr/bin/g++") set(CMAKE_OSX_ARCHITECTURES ${ARCH})
include_directories(SYSTEM "${SDKROOT}/usr/include") link_directories(SYSTEM "${SDKROOT}/usr/lib")
add_definitions(-D_PHONE) add_library(vp8-armv7-darwin STATIC ${VP8SRC}) endif() |
你能一眼看出這個配置文件干了什么嗎?其實這個配置文件想要產生的目標(target)只有一個,就是通過 ${VP8SRC} 編譯生成的靜態庫,但因為加上了條件判斷,及各種平台相關配置,使得這個配置文件看起來很是復雜。在我看來,編寫 CMake 配置文件是一種線性思維,對於同一個目標的配置可能會零散分布在各個地方。而 GYP 則相當不同,GYP 的配置文件更多地強調模塊化、結構化。看看下面這個示例文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
{ 'targets': [ { 'target_name': 'foo', 'type': '<(library)', 'dependencies': [ 'bar', ], 'defines': [ 'DEFINE_FOO', 'DEFINE_A_VALUE=value', ], 'include_dirs': [ '..', ], 'sources': [ 'file1.cc', 'file2.cc', ], 'conditions': [ ['OS=="linux"', { 'defines': [ 'LINUX_DEFINE', ], 'include_dirs': [ 'include/linux', ], }], ['OS=="win"', { 'defines': [ 'WINDOWS_SPECIFIC_DEFINE', ], }, { # OS != "win", 'defines': [ 'NON_WINDOWS_DEFINE', ], }] ], } ], } |
我們可以立馬看出上面這個配置文件的輸出目標只有一個,也就是 foo,它是一個庫文件(至於是靜態的還是動態的這需要在生成項目時指定),它依賴的目標、宏定義、包含的頭文件路徑、源文件是什么,以及根據不同平台設定的不同配置等。這種定義配置文件的方式相比 CMake 來說,讓我覺得更加舒服,也更加清晰,特別是當一個輸出目標的配置越來越多時,使用 CMake 來管理可能會愈加混亂。
配置文件的編寫方式是我區分 GYP 和 CMake 之間最大的不同點,當然 GYP 也有一些小細節值得注意,比如支持跨平台項目工程文件輸出,Windows 平台默認是 Visual Studio,Linux 平台默認是Makefile,Mac 平台默認是 Xcode,這個功能CMake 也同樣支持,只是缺少了 Xcode。Chromium 團隊成員也撰文詳細比較了 GYP 和 CMake 之間的優缺點,在開發 GYP 之前,他們也曾試圖轉到 SCons(這個我沒用過,有經驗的同學可以比較一下),但是失敗了,於是 GYP 就誕生了。
當然 GYP 也不是沒有缺點,相反,我覺得它的「缺點」一大堆:
文檔不夠完整,項目不夠正式,某些地方還保留着 Chromium 的影子,看起來像是還沒有完全獨立出來。
大量的括號嵌套,很容易讓人看暈,有過 Lisp 使用經驗的同學可以對號入座。對於有括號恐懼症,或者不使用現代編輯器的同學基本可以繞行。
為了支持跨平台,有時不得不加入某些特定平台的配置信息,比如只適用於 Visual Studio 的 RuntimeLibrary 配置,這不利於跨平台配置文件的編寫,也無形中增加了編寫復雜度。
不支持 make clean,唯一的方法就是將輸出目錄整個刪除或者手動刪除其中的某些文件。
如果你已經打算嘗試 GYP,那一定記得在生成項目工程文件時加上 --depth 參數,譬如:
$ gyp --depth=.foo.gyp
這也是一個從 Chromium 項目遺留下來的歷史問題。
玩轉Google開源C++單元測試框架Google Test系列(gtest)(總)
trunk\webrtc\modules
視頻采集---video_capture
源代碼在webrtc/modules/video_capture/main目錄下,包含接口和各個平台的源代碼。
在windows平台上,WebRTC采用的是dshow技術,來實現枚舉視頻的設備信息和視頻數據的采集,這意味着可以支持大多數的視頻采集設備;對那些需要單獨驅動程序的視頻采集卡(比如海康高清卡)就無能為力了。
視頻采集支持多種媒體類型,比如I420、YUY2、RGB、UYUY等,並可以進行幀大小和幀率控制。
視頻編解碼---video_coding
源代碼在webrtc/modules/video_coding目錄下。
WebRTC采用I420/VP8編解碼技術。VP8是google收購ON2后的開源實現,並且也用在WebM項目中。VP8能以更少的數據提供更高質量的視頻,特別適合視頻會議這樣的需求。
視頻加密--video_engine_encryption
視頻加密是WebRTC的video_engine一部分,相當於視頻應用層面的功能,給點對點的視頻雙方提供了數據上的安全保證,可以防止在Web上視頻數據的泄漏。
視頻加密在發送端和接收端進行加解密視頻數據,密鑰由視頻雙方協商,代價是會影響視頻數據處理的性能;也可以不使用視頻加密功能,這樣在性能上會好些。
視頻加密的數據源可能是原始的數據流,也可能是編碼后的數據流。估計是編碼后的數據流,這樣加密代價會小一些,需要進一步研究。
視頻媒體文件--media_file
源代碼在webrtc/modules/media_file目錄下。
該功能是可以用本地文件作為視頻源,有點類似虛擬攝像頭的功能;支持的格式有Avi。
另外,WebRTC還可以錄制音視頻到本地文件,比較實用的功能。
視頻圖像處理--video_processing
源代碼在webrtc/modules/video_processing目錄下。
視頻圖像處理針對每一幀的圖像進行處理,包括明暗度檢測、顏色增強、降噪處理等功能,用來提升視頻質量。
視頻顯示--video_render
源代碼在webrtc/modules/video_render目錄下。
在windows平台,WebRTC采用direct3d9和directdraw的方式來顯示視頻,只能這樣,必須這樣。
網絡傳輸與流控
對於網絡視頻來講,數據的傳輸與控制是核心價值。WebRTC采用的是成熟的RTP/RTCP技術。
7.4 webrtc代碼相關基礎知識
http://blog.csdn.net/chenyufei1013/article/category/1248211
1 引言
近年來,隨着數據網絡通信逐漸融入傳統的話音業務領域,VoIP技術越來越成為當前商業考慮的對象,並正在向一種正式的商業電話模式演進,而會話初始協議 (SIP,Session Initiation Protoc01)就是用來確保這種演進能夠實現而需要的NGN(下一代網絡)系列協議中重要的一員。SIP是一個用於建立,更改和終止多媒體會話的應用層控制協議。SIP因其簡單、靈活、可擴展性強的特點,已經成為實現VolP系統的熱點技術。
隨着計算機網絡技術的不斷發展,互聯網規模飛速膨脹,大量企業和駐地網采用了私有網絡通過NAT/防火牆出口來接入公共網絡。而由於SIP包頭中含有很多 對於路由、接續SIP信令和建立呼叫連接必不可少的地址信息,這樣引發了業界對於SIP2穿越NAT/防火牆問題的研究。
目前,IETF已經對該問題提出了多種解決方案。例如:ALCes(Application Layer Gateways)、MiddleboxControlProtocol、STUN Simple Traversal of UDPthrough NAT)、TURN(Traversal Using Relay NAT)、RSIP(RealmSpecific IP)、Symmetric RTP等。然而,當這些技術應用於不同的網絡拓撲時都有着顯著的利弊,以至於只能根據不同的接入方式來應用不同的方案,所以,未能很好地解決A11- NATⅢ的問題,同時還會給系統引入許多復雜性和脆弱性因素。此外,由於NAT/防火牆已經大量應用,SIP設備也已經比較成熟,對它們進行升級來支持多 媒體通信穿越NAT/防火牆的代價將相當的大。因此,一種不需要升級任何現有網絡設備,能夠穿越各種NAT/防火牆並且方便在現有網絡中實施的解決方案成 為迫切的需要。
本文試圖尋找一種能夠穿越各種類型的NAT/防火牆,無需對現有NAT/防火牆沒備做任何改動的解決方案——ICE解決方案,這種方式比以前的解決方案更加靈活,具有廣闊的應用前景。
2 現有NAT解決方案的比較分析
主流的NAT穿越解決方案包括STUN、TURN、Proxy及隧道穿越等,這幾種方式各具優缺點,比較如下:
(1)STUN(simpletraversal of UDP over NAT)的原理是通過某種機制預先得到內部私有IP地址對應在出口NAT上的對外公網IP地址,然后在報文負載中所描述的地址信息就直接填寫出口NAT上 的對外IP地址。其最大的優點是無需對現有NAT/防火牆設備做任何改動。局限性在於需要應用程序支持STUN CLIENT的功能,同時STUN並不適合支持TCP連接的穿越。
(2)TURN即通過Relay方式穿越NAT,也是私網中的SIP終端通過某種機制預先得TURN SeI-ver上的公網地址,私網終端發出的報文都要經過TURN Serve:進行Relay轉發。這種方式除了具有STUN方式的優點外,還解決了STUN應用無法穿透對稱NAT(SymmetricNAT)以及類似 的Firewall設備的缺陷,局限性在於需要SIP終端支持TURN Client,並增大了包的延遲和丟包的可能性。
(3)Proxy方式是指通過對私網內用戶呼叫的信令和媒體||d時做Relay來實現對NAT/防火牆的穿越。由於不用對運營商和客戶端的現有網絡設備進行任何改造,具有很強的適應性,組網靈活,可滿是NGN初期多樣化的組網和用戶接入。
(4)隧道穿越技術的基本思想是通過把需要穿越的數據流封裝征某種隧道中,從而繞過NAT/防火牆。它在很大程度上解決了對於不問應用協議需要開發不同穿越策略的辦法,但是必須多媒體終端和服務器能夠支持隧道,這是一個比較大的限制條件。
3 穿越NAT/防火牆方案的實現
3.1 ICE方式
交互式連通建立方式ICE(InteractiveConnectivityEstablishment)並非一種新的協議,它不需要對STUN,TURN或RSIP進行擴展就可適用於各種NAT。ICE 是通過綜合運用上面某幾種協議,使之征最適合的情況下工作,以彌補單獨使用其中任何一種所帶來的固有缺陷。對於SIP來說,ICE只需要定義一些SDP(Sessionescription Protoc01)附加屬性即可,對於別的多媒體信令協議也需要制定一些相應的機制來實現。本文是針對SIP呼叫流程實現ICE的功能。
這種方式的優點是可以根據通訊雙方所處的網絡環境,選取適合穿越NAT/防火牆的方式。首先,獲取用戶所征網絡中NAT的類型,如果用戶沒有設置使用何種方式連接,那么默隊首先使用UDP連接,如果一定時間內沒有連接成功,接着使用TCP連接,同樣如果沒有在一定時間內連接成功,那么將采用其他方式如 Upnp、Httptunnel。如果所有穿越方案都失敗后,將結果返回給用戶,由用戶決定是否重試。
3.2 ICE算法流程
ICE算法流程分為以F幾個過程:
(1)收集本地傳輸地址
會話者從服務器上獲得主機上一個物理(或虛擬)接口綁定一個端口的本地傳輸地址。
(2)啟動STUN
與傳統的STUN不同,ICE用戶名和密碼可以通過信令協議進行交換。
(3)確定傳輸地址的優先級
優先級反映了UA在該地址上接收媒體流的優先級別,取值范圍0到1之間,按照被傳輸媒體流量來確定。
(4)構建初始化信息(InitiateMessage)
初始化消息由一系列媒體流組成,每個媒體流的任意Peer之間實現最人連通可能性的傳輸地址是由公網L轉發服務器(如TURN)提供的地址。
(5)響應處理
連通性檢查和執行ICE算法中描述的地址收集過程。
(6)生成接受信息(AcceptMessage)
若接受則發送Accept消息,其構造過程與InitiateMessage類似。
(7)接受信息處理
接受過程需要發起者使用Send命令,由服務器轉發至響應者。
(8)附加ICE過程
Initiate或Accept消息交換過程結束后,雙方可能仍將繼續收集傳輸地址。
3.3 ICE算法實現
當將ICE算法集成到SIP呼叫過程的時候,流程應該是:(1)SIP終端注冊,並且訪問STUN(STUNT)服務器,判斷NAT/防火牆類型,以及 TCP時三種序列的包的過濾情況。(2)當發起呼叫信息(INVITE)或接收到呼叫信息回應(200 OK)之前根據NAT/防火牆類型進行對RTP進行地址收集(任非對稱性NAT/防火牆后需要收集NAT映射地址,在對稱性NAT/防火牆后還需要收集 TURN地址)。(3)在RTP的地址端口啟動接收線程RSTUN服務程序。(4)發送SIP消息,收集的地址放列SDP消息中的alt屬性中。 (5)SIP終端得到通訊雙方地址后進行地址配對(將雙方地址進行組合),並且根據雙方網絡情況去掉無效的地址對。(6)根據地址對發
送STUN check的包,其中STUN消息的用戶名,密碼從alt屬性中得到,標識該地址對。(7)當檢測到有效的地址對時(可以發送RTP媒體流的地址),停止接收線程STUN服務),開始傳輸RTP流。
本文實現采用WinpcapAPI首先捕獲TCP連接的SYN--out包,修改lP包頭的TTL的值,用pcap—sendpacket()。然后使該socket調用listen函數。實現過程中對應於ICE收集地址的算法描述為:
類中m_nCandidateID對應地址序號,m_nPriority表示地址優先級,m_CandidateAddr表示地址(IP地址,端口)。實現ICE算法的實體算法描述為:
實現ICE中會話發起者和接收者的步驟基本一樣,僅任處理流程中先后次序稍微有些不同,本文中實現的會話流程如圖l所示。
圖1會話流程
4 測試
以安裝了SIP軟終端的雙方都在Full ConeNATNAT/防火牆后為例,進行實例測試。測試過程:
(1)將兩台PC的IP的配置分別為公網59.64.148.187122和私網10.0.0.5/8l
(2)從公網中的用戶代理向私網內的用戶代理呼叫,結果能夠建立會話,無明顯的延時,話音質量良好;
(3)從私網內的用戶代理向公網中的用戶代理呼叫,結果能夠建立會話,且話音質量良好;
通過抓包分析可以確定,使用該算法可以成功地穿越NAT/防火牆設備。
5 結論
ICE方式的優勢是顯而易見的,它消除了現有的機制的許多脆弱性。例如,傳統的STUN有幾個脆弱點,其中一個就是發現過程需要客戶端自己去判斷所在 NAT類型,這實際上不是一個可取的做法。而應用ICE之后,這個發現過程己經不需要了。另一點脆弱性在於STUN,TURN等機制都完全依賴於一個附加 的服務器,而ICE利用服務器分配單邊地址的同時,還允許客戶端直接相連,因此即使STUN或TRUN服務器中有任何一個失敗了,ICE方式仍可讓呼叫過 程繼續下去。此外,傳統的STUN最大的缺陷在於,它不能保證在所有網絡拓撲結構中都正常工作,對於TURN或類似轉發方式工作的協議來說,由於服務器的 負擔過重,很容易出現丟包或者延遲情況。而ICE方式正好提供了一種負載均衡的解決方案,它將轉發服務作為優先級最低的服務,從而在最大程度上保證了服務 的可靠性和靈活性。此外,ICE的優勢還在於對IPv6的支持。由於廣泛的適應能力以及對未來網絡的支持,ICE作為一種綜合的解決方案將有着非常廣闊的 應用前景。
1. 官網地址(要翻牆):STUNTMAN:http://www.stunprotocol.org/
從http://www.stunprotocol.org/stunserver-1.2.3.tgz 下載源碼
2. 編譯依賴:
sudo apt-get install g++
sudo apt-get install make
sudo apt-get install libboost-dev # For Boost
sudo apt-get install libssl-dev # For OpenSSL
3. 編譯 stunserver
cd stunserver
sudo make
4. 在stunserver目錄下生成下面三個程序
stunclient, stunserver, stuntestcode
5. run the unittest. Should HAVE NO "FAIL" in the end of any line
./stuntestcode
Result of CTestDataStream: PASS
Result of CTestReader: PASS
Result of CTestBuilder: PASS
Result of CTestIntegrity: PASS
Result of CTestMessageHandler: PASS
Result of CTestCmdLineParser: PASS
Testing detection for DirectMapping
Testing detection for EndpointIndependentmapping
Testing detection forAddressDependentMapping
Testing detection forAddressAndPortDependentMapping
Testing detection forEndpointIndependentFiltering
Testing detection forAddressDependentFiltering
Testing detection forAddressAndPortDependentFiltering
Result of CTestClientLogic: PASS
Result of CTestRecvFromEx(IPV4): PASS
Result of CTestRecvFromEx(IPV6): PASS
Result of CTestFastHash: PASS
Result of CTestPolling: PASS
Result of CTestAtomicHelpers: PASS
6. start stunserver......
./stunserver --help # 使用說明。
nohup ./stunserver --mode full --primaryinterface eth0--altinterface eth1 &
7. stunclient 檢測地址端口映射及NAT類型
用法:./stunclient --mode full --localport7777 stun.sipgate.net
NOTE: stuntman只具有stun功能,沒有轉發功能。支持UDP,TCP。兼容RFC3489。
原文地址:http://blog.csdn.net/kl222/article/details/19336179
libnice是一個ICE實現庫。它實現了InteractiveConnectivity Establishment (ICE) standard (RFC 5245) 和 theSession Traversal Utilities for NAT (STUN) standard (RFC 5389)。
官網地址:http://nice.freedesktop.org/wiki/
源碼git庫地址:http://cgit.collabora.com/git/libnice.git
1、下載源碼:
git clonegit://git.collabora.co.uk/git/libnice.git
2、編譯:
2.1、linux平台下:
它依賴:
glib >= 2.10
pkg-config
gupnp-igd >= 0.1.2 (optional)
gstreamer-0.10 >= 0.10.0 (optional)
gtk-doc-tools #autogen.sh需要
2.2、編譯
k@k-C410:/home/libnice$ ./autogen.sh
k@k-C410:/home/libnice$ ./configure
k@k-C410:/home/libnice$ make
3、生成的程序和庫
在nice/.libs目錄下生成靜態庫libnice.a、動態庫libnice.so
在example目錄下生成三個例子程序。
4、例子程序的使用
k@k-C410:/home/libnice/examples$ ./simple-example0 stunserver.org
Copy this line to remote client:
Tyyp33oInvKVEn1Lo6LkVVy6P5 1,2013266431,192.168.10.17,47748,host
Enter remote data (single line, no wrapping):
>
紅色部分表示提供給對等端協商時的驗證用戶名、密碼、外網地址,以空格分隔。
啟動二個實例,就可以開始IM對話了:
第一個控制台:
k@k-C410:/home/libnice/examples$ ./simple-example0 stunserver.org
Copy this line to remote client:
Tyyp 33oInvKVEn1Lo6LkVVy6P5 1,2013266431,192.168.10.17,47748,host
Enter remote data (single line, no wrapping):
> h4p1 7M8uL1928RzeRv6cWRDqG8 1,2013266431,192.168.10.17,47758,host
Negotiation complete: ([192.168.10.17]:47748, [192.168.10.17]:47758)
Send lines to remote (Ctrl-D to quit):
> a
>
第二個控制台:
k@k-C410:/home/libnice/examples$ ./simple-example 0 stunserver.org
Copy this line to remote client:
h4p1 7M8uL1928RzeRv6cWRDqG8 1,2013266431,192.168.10.17,47758,host
Enter remote data (single line, no wrapping):
> Tyyp 33oInvKVEn1Lo6LkVVy6P5 1,2013266431,192.168.10.17,47748,host
Negotiation complete: ([192.168.10.17]:47758, [192.168.10.17]:47748)
Send lines to remote (Ctrl-D to quit):
> a
存在的問題:在linux下,stun服務器地址不能通過域名解析到IP地址。解決方法是,直接用stun服務器的IP地址。本人已向項目提交了補丁包。