基于UDP的数据传输协议(UDP-based Data Transfer Protocol,简称UDT)是一种互联网数据传输协议,UDT的主要目的是支持高速广域网上的海量数据传输。
由于UDT完全采用标准C++开发,具有很好的可移植性,不依赖于具体的操作系统,又由于Android采用的是Linux内核,所以理论上UDT完全可以移植到Android架构上。移植前,首先要获得UDT的适合Linux版本的源代码,于是我从UDT的托管地,全球著名的开源软件开发平台及仓库——SourceForge里用CVS下载到了UDT的最新稳定版的源代码。
这里提到的CVS是一个C/S系统,是一个常用的代码版本控制软件,主要在开源软件管理中使用。这里使用如下命令从CVS服务器上取出最新的源代码。cvs -d:pserver:anonymous@udt.cvs.sourceforge.net:/cvsroot/udt login
cvs -z3 -d:pserver:anonymous@udt.cvs.sourceforge.net:/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=YYYUDT支持的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、移植完成