WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。这段介绍来自百科。
当然websocket也可以用于android建立长链接,实现IM通信
优势:节省内存空间。当然这个是服务端并发时候节省内存空间,支持的并发量更大 ,这个我没做服务端没有验证。
原理和优势不再本文讨论范围内,今天主要记录下怎么编译libwebsocket.so
1.下载Libwebseocket库
git clone https://github.com/warmcat/libwebsockets.git
2.环境准备(Mac版)
2.1安装zlib :
brew install zlib
2.2安装makedepend:
brew install makedepend
2.3安装cmake :
brew install cmake
3.编译.a静态文件
有了上面这些工具,准备工作差不多了,然后通过libwebsocket/contrib目录下的android-make-script.sh编译.a文件 这个sh文件有些问题,比如:只能编译出arm架构的文件,并且在编译zlib库时使用的libtool有问题,libwebsocket原文件编译出错。
针对这些问题作了一些修改:
3.1 修改output.c和http2.c原文件编译错误,这个已经提交到libwebsocket base2.2版本上了
https://github.com/warmcat/libwebsockets/commit/34842d7492b728349f6a6898e3893b08d70625fa
3.2 替换libtool文件
mv ./Makefile ./Makefile.old
sed "s/AR=libtool/AR=`echo ${AR}|sed 's#\/#\\\/#g'`/" ./Makefile.old > Makefile.mid
sed "s/ARFLAGS=-o/ARFLAGS=-r/" ./Makefile.mid > Makefile
3.3 增加build x86架构文件编译
android支持的架构不光是arm,还有x86 x86-64等架构的机器,常用的armabi 和x86 这里提供了编译方式 如果还需要其他架构的可以根据android-make-script-all.sh文件 修改对应的参数
其他架构的参数可以在原始项目的test-server中的NativeLibs.mk中找到,然后修改android-make-script-all.sh文件
4.JNI封装websocket.
4.1初始化 initLws
初始化context
先准备好必要的参数 :host ,port ,path,timeout, ping,ca cert
涉及到函数:
jni_setConnectionParameters // host port path
jni_setTimeout //超时
jni_setPingInterval //ping 间隔
jni_setCaCert //证书
然后调用lws_create_context初始化context
jni_initLws
4.2连接 connect
当context初始化后,就可以连接服务器了
jni_connectLws
在jni_connectLws调用lws_client_connect_via_info连接服务器
还有一个重要的函数
jni_serviceLws
这个方法的作用是,轮训,不断的去检查是否有消息接收,检查到有新消息到达,通过callback回调,然后通过receiveMessage回调到java层
4.3发送/接收消息 writeLws/callback
接收消息:
我们在初始化context时注册了一个协议,这个protocol中包含一个callback,我们就是通过这个callback来接收服务端的消息,及错误信息
static int callback(
struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len
) {
//
return 0;
}
只需要关注下面两个参数
reason:回执code
in :消息体
发送消息:
jni_sendMessageLws
调用的lws_write来发送消息,这里要注意的是发送消息的大小(初始化时候设置的),如果超过这个大小消息会分片,分片的消息需要自行处理,在callback中LWS_CALLBACK_SERVER_WRITEABLE处理剩下的报文。
当然我们平时发送的消息体不会太大,一般几百个字节最够了,我这里设置的4K
够发1500+个汉字。
4.4断开 exitLws
调用lws_context_destroy断开连接
重要函数参数:
状态值 | 含义 |
---|---|
LWS_CALLBACK_WSI_CREATE | 含义:正在创建ws连接对象备注:此时表1中wsi对象和user对象依然为空指针,因此,还不能初始化用户自定义对象。回调函数的参数含义: context: 全局上下文 wsi: 空指针 user: 空指针 in: 空指针len: 0 |
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION | 使用lws库的人员可以在此过滤协议。备注:在此处返回非0值时,lws库将会关闭该链接;该处返回0时,表示ws连接已经建立成功。此时表1中的wsi对象和user对象已不为空,因此,此时可以对用户自定义对象user进行初始化处理。 |
LWS_CALLBACK_RECEIVE | 表示WS服务端收到客户端发送过来的一帧完整数据,此时表1中的in表示收到的数据,len表示收到的数据长度。需要注意的是:指针in的回收、释放始终由LWS框架管理,只要出了回调函数,该空间就会被LWS框架回收。因此,开发者若想将接收的数据进行转发,则必须对该数据进行拷贝 |
LWS_CALLBACK_CLOSED | ws连接已经断开 备注:不能在此释放内存空间,否则存在内存泄漏的风险!!!因为连接断开时,并不总是会回调LWS_CALLBACK_CLOSED的处理! |
LWS_CALLBACK_WSI_DESTROY | 正在销毁ws连接对象 表示libwebsockets框架即将销毁wsi对象。此时如果用户自定义对象中存在动态分配的空间,则需要在此时进行释放 |
重要函数:
状态值 | 功能 |
---|---|
lws_send_pipe_choked | 功能:判断ws连接是否阻塞 备注:如果ws连接阻塞,则返回1,否则返回0 |
lws_create_context | 功能:初始化websocket服务,返回context 如果不为null 初始化成功 |
lws_client_connect_via_info | 功能:建立websocket连接 ,返回wsi 如果不为null 连接成功 |
lws_write | 功能:将数据发送给对端 备注:函数参数说明 wsi: ws连接对 buf: 需要发送数据的起始地址 返回:result > 0 成功,result = 0 失败 |
lws_callback_on_writable | 将ws连接加入可写事件监听 |
lws_service | 检查是否有新消息接收 |
lws_context_destroy | 断开websocket服务 |
5.搭建简易服务端测试
进入到python服务器所在目录 运行下面命令 启动python服务器
python testServer.py
这个py服务器比较简单,只能支持1024字节传输,超过这个长度会乱码,要测试超长字符,服务器还是要自己去搭建。
通过上面的步骤,就可以编译了下面是我编译好的源码及demo,有需要可以下载
https://github.com/honjane/buildLibWebSocket
demo使用步骤:
1.进入buildws目录,运行sh文件 生成.a文件,然后把生成的.a文件拷贝到websocket/src/main/jni/ 对应架构下 libcrypto.a libssl.a libwebsocket.a libz.a
2.使用build-ndk 编译 jni目录生成对应libwebsocket.so 怎么编译就不多说,生成的so文件会在libs和obj目录下,把这些生存的so拷贝到jniLibs/对应架构下目录
3.启动python服务器 ,连接服务器,发送消息