在Android中使用UDT


原文链接:http://big-room.org/?p=152
UDT分析及Android平台移植

by BIGROOM posted on 六月 14, 2011
基于UDP的数据传输协议(UDP-based Data Transfer Protocol,简称UDT)是一种互联网数据传输协议,UDT的主要目的是支持高速广域网上的海量数据传输。
因为互联网上标准的数据传输协议TCP并不完美,它不是为所有的具体应用而设计的。在最近的几年,随着光网络及丰富的网络应用的发展,发现TCP并不能满足BDP(延迟带宽产品)的需求。它的AIMD算法大大地减少了TCP的拥塞窗口,但并不能快速地恢复可用带宽。从流级别的理论分析显示,当BDP增幅很大时,TCP变得更加脆弱,更容易丢包。由此可见,TCP在高带宽长距离网络上性能很差。
UDT的最初目的是为了克服TCP在高速广域网中的低效问题。尽管当前部署了各种TCP的变种(如Linux上的BIC TCP,Window上的Compound TCP),但某些问题依然存在。比如,没有任何一个新的TCP变种解决RTT不公平的问题:RTT小的连接占有更多的带宽。而且,随着因特网的演变发展,传输协议将面临一些新的挑战和需求。研究人员需要一个平台快速的开发和测试新的算法和协议。网络研究人员和学生通过UDT可以很简单地实现他们关于传输协议的想法,特别是拥塞控制算法,并在真实网络中进行实验。基于此,基于UDP的数据传输协议——UDT被设计出来。
UDT建于UDP之上,数据包和控制包都是通过UDP传输,并引入了新的拥塞控制和数据可靠性控制机制。UDT是面向连接的双向的应用层协议。UDT是一个单播协议,并且不考虑多播。UDT通过双工的方式传输数据。
UDT同时支持可靠的数据流传输和部分可靠的数据报传输。由于UDT完全在UDP上实现,它也可以应用在除了高速海量数据传输之外的其它应用领域,例如点到点技术(P2P),防火墙穿透,多媒体数据传输等等。
UDT由开源软件作者谷云洪在美国伊利诺伊大学芝加哥分校攻读博士期间开发,并由他在毕业后继续维护和升级。UDT的开源代码可以在SourceForge上获取,目前的稳定版本是4.8,采用BSD license协议发布。
UDT的软件实现C++的函数库,它包含了UDT的API实现以及编程示例。由于UDT完全采用标准C++开发,具有很好的可移植性,是一个跨平台的协议库,支持Linux、Windows、Mac OS X以及BSD。同时,在很多开源软件爱好者的努力下,UDT又衍生出了UDT-Java、Barchart-UDT、UDT-net、UDT Gateway System等适用于不同应用场合的版本。


UDT的移植
由于UDT完全采用标准C++开发,具有很好的可移植性,不依赖于具体的操作系统,又由于Android采用的是Linux内核,所以理论上UDT完全可以移植到Android架构上。
移植前,首先要获得UDT的适合Linux版本的源代码,于是我从UDT的托管地,全球著名的开源软件开发平台及仓库——SourceForge里用CVS下载到了UDT的最新稳定版的源代码。
这里提到的CVS是一个C/S系统,是一个常用的代码版本控制软件,主要在开源软件管理中使用。这里使用如下命令从CVS服务器上取出最新的源代码。
cvs -d:pserver:[email protected]:/cvsroot/udt login
cvs -z3 -d:pserver:[email protected]:/cvsroot/udt co -P modulename
下载得到的源代码包括UDT1、UDT2、UDT3、UDT4等版本,本文使用4.8版本即UDT4里面的代码。
仔细阅读README.txt可知源代码文件夹结构如下:
./src:  UDT源代码
./app:  示例程序
./doc:  HTML格式帮助文档
./win:  Windows版本UDT的Visual C++项目文件
使用make来编译源代码,命令如下:make -e os=XXX arch=YYY
UDT支持的os参数有:Linux、BSD、OSX
UDT支持的架构有:IA32、POWERPC、IA64、AMD64
移植步骤:
1、在Ubuntu10.04系统里编译UDT库
在Linux下编译UDT库可以用官方提供的Makefile
编译脚本Makefile内容如下:
C++ = g++
ifndef os
   os = LINUX
endif
ifndef arch
   arch = IA32
endif
CCFLAGS = -fPIC -Wall -Wextra -D$(os) -finline-functions -O3 -fno-strict-aliasing #-msse3
ifeq ($(arch), IA32)
   CCFLAGS += -DIA32
endif
ifeq ($(arch), POWERPC)
   CCFLAGS += -mcpu=powerpc
endif
ifeq ($(arch), SPARC)
   CCFLAGS += -mcpu=sparc
endif
ifeq ($(arch), IA64)
   CCFLAGS += -DIA64
endif
ifeq ($(arch), AMD64)
   CCFLAGS += -DAMD64
endif
OBJS = md5.o common.o window.o list.o buffer.o packet.o channel.o queue.o ccc.o cache.o core.o epoll.o api.o
DIR = $(shell pwd)
all: libudt.so libudt.a udt
%.o: %.cpp %.h udt.h
$(C++) $(CCFLAGS) $< -c
libudt.so: $(OBJS)
ifneq ($(os), OSX)
$(C++) -shared -o $@ $^
else
$(C++) -dynamiclib -o libudt.dylib -lstdc++ -lpthread -lm $^
endif
libudt.a: $(OBJS)
ar -rcs $@ $^
udt:
cp udt.h udt
clean:
rm -f *.o *.so *.dylib *.a udt
install:
export LD_LIBRARY_PATH=$(DIR):$$LD_LIBRARY_PATH
直接进入UDT4/src文件夹,在终端输入make all即可,经GCC编译,生成libudt.a静态库和libudt.so动态库文件。
2、在Ubuntu10.04系统里结合上一步中得到的UDT库,编译运行测试官方提供的示例代码
进入UDT4/app文件夹,发现官方提供了很多示例代码,这里只使用利用UDT传输文件这一测试用例,对应的源文件为:recvfile.cpp,sendfile.cpp。
这里修改app文件夹下的Makefile为:
C++ = g++
ifndef os
   os = LINUX
endif
ifndef arch
   arch = IA32
endif
CCFLAGS = -Wall -D$(os) -I../src -finline-functions -O3
ifeq ($(arch), IA32)
   CCFLAGS += -DIA32 #-mcpu=pentiumpro -march=pentiumpro -mmmx -msse
endif
ifeq ($(arch), POWERPC)
   CCFLAGS += -mcpu=powerpc
endif
ifeq ($(arch), IA64)
   CCFLAGS += -DIA64
endif
LDFLAGS = -L../src -ludt -lstdc++ -lpthread -lm
ifeq ($(os), UNIX)
   LDFLAGS += -lsocket
endif
DIR = $(shell pwd)
APP = sendfile recvfile
all: $(APP)
%.o: %.cpp
$(C++) $(CCFLAGS) $< -c
sendfile: sendfile.o
$(C++) $^ -o $@ $(LDFLAGS)
recvfile: recvfile.o
$(C++) $^ -o $@ $(LDFLAGS)
clean:
rm -f *.o $(APP)
install:
export PATH=$(DIR):$$PATH
这样就可以只编译出我们需要的recvfile和sendfile两个二进制可执行文件。
先在sendfile所在目录创建一个测试文件,命名为remotefile。
分别打开两个终端,在终端1中输入./sendfile 6666
终端2中输入./recvfile 127.0.0.1 6666 remotefile localfile
运行后将在recvfile所在目录获得一个和remotefile内容一样的localfile,表明测试成功。
3、用Android NDK编译UDT库
接下来开始移植UDT库到Android环境中。
分析可知由于UDT使用C++编写,需使用支持C++的android-ndk-r4-crystax编译链来编译。
3.1 新建一个文件夹,命名为jni,将UDT4/src中所有代码文件复制到jni文件夹里面。
3.2 分析src中的Makefile可以写出如下Android NDK编译所需的Android.mk文件,内容如下:
LOCAL_PATH := $(call my-dir)
LOCAL_CPP_EXTENSION:=.cpp
include $(CLEAR_VARS)
LOCAL_MODULE    := udt
LOCAL_SRC_FILES := md5.cpp common.cpp window.cpp list.cpp buffer.cpp packet.cpp channel.cpp queue.cpp ccc.cpp cache.cpp core.cpp epoll.cpp api.cpp
include $(BUILD_SHARED_LIBRARY)
打开终端,进入jni所在目录,输入命令ndk-build,经过编译即可在libs/armeabi目录中生成动态共享库libudt.so,该库可用于Android架构。
4、用Android NDK结合上一步中得到的Android版UDT库,编译运行官方提供的示例代码
接下来,参考recvfile.cpp文件编写可运行于Android手机的程序,该程序可接收远程PC机中的文件。
该程序的编写分为两个部分:NDK库函数层和Java GUI调用层。
NDK库函数层用到的udtrecvfile.cpp相对于recvfile.cpp文件增加了接收server_ip、server_port、remote_filename、local_filename参数的jni函数。
使用android-ndk-r4-crystax将上述代码编译成libudtrecvfile.so动态共享库,Android NDK所需的Android.mk的内容为:
LOCAL_PATH := $(call my-dir)
LOCAL_CPP_EXTENSION:=.cpp
include $(CLEAR_VARS)
PATH_TO_UDT_SOURCE:=./include/
PATH_TO_LIBUDT_SO:=./lib/
LOCAL_C_INCLUDES += $(PATH_TO_UDT_SOURCE)
LOCAL_LDLIBS += -L$(PATH_TO_LIBUDT_SO) -ludt
LOCAL_MODULE    := udtrecvfile
LOCAL_SRC_FILES := udtrecvfile.cpp
include $(BUILD_SHARED_LIBRARY)
Java GUI层代码为src/prox/AndroidUDTRecvFileTest/AndroidUDTRecvFileTest.java,其中声明了本地函数RecvFileFromServer,方法如下:
public native String RecvFileFromServer(String UDTServerAddress,String UDTServerPortStr,String RemoteFileName,String LocalFileName);
并且需要加载NDK库,方法如下:
static {
System.loadLibrary("udt");
System.loadLibrary("udtrecvfile");
}
编译生成AndroidUDTRecvFileTest.apk文件,上传到手机中安装。
测试时,先在PC端运行sendfile,再在手机中运行AndroidUDTRecvFileTest,结果发现在SD卡的根目录下生成名为localfile的文件,经查看,其内容与remotefile一样,表明移植成功。
5、移植完成
THIS ENTRY WAS POSTED IN ANDROID. BOOKMARK THE PERMALINK.

猜你喜欢

转载自goblin-god.iteye.com/blog/1143663