如何实现android连接服务器和服务器长连接

帐号:密码:下次自动登录{url:/nForum/slist.json?uid=guest&root=list-section}{url:/nForum/nlist.json?uid=guest&root=list-section}
贴数:7&分页:专睇老片发信人: kyxkcoach (专睇老片), 信区: MobileDev
标&&题: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 18:36:05 2013), 站内 && 我长期做b/s开发. b/s系统的session机制一般是基于cookie实现:浏览器输入用户名/
密码,服务端校验成功后返回一个cookie并存储相关的用户信息,接下来的交互中浏览
器都会回送这个cookie,服务端根据cookie判断用户是否已登录,并从服务端存储的信
息中拿到用户名等信息。请问android中一般怎么搞? &&&& 好像有一种办法是这样:鉴权成功后android把用户名、密码通过AccountManager存在本
地,然后每次与服务端交互时都在HttpURLConnection中通过setRequestProperty把用户
名、密码写进去;然后服务端校验用户名、密码对不对。 但这意味着服务端对每次交互
都做用户名、密码校验,已经不是一种“会话”了;而且密码用明文或者用对称性加密
算法加密后保存在本地,也不太安全。 &&&& 另一种做法似乎是:服务端实现OAUTH协议,客户端在用户首次登录后从服务端拿到一个
有时效性的oauth token,然后每次通信时都把token发过去; AccountManager里仍然可
以存一个UserName,在离线时或者token失效后,用户仍然可以知道自己是谁。 && 这跟cookie机制很像。 但这意味着服务端必须实现oauth协议? 有点怪。 &&&& 当然不搞OAUTH也可以,服务端返回一个有时效性的令牌,跟后跟上面一样。 && 以上都是网上搜集加YY的结果,看起来都有点山寨。所以在这里求一个最佳实践。&&&&&& -- && ※ 来源:·水木社区 newsmth.net·[FROM: 122.234.4.*]
基因~也许以后~~发信人: cybergene (基因~也许以后~~), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 19:06:44 2013), 站内 &&&& 可以认证一次之后保持连接,类似qq。 && 如果非要模拟浏览器行为,那就实现一个类似cookie的机制。实在不行,每次access都验证用户名密码也不是不可以。考虑传输安全的话,用ssl即可。 && 【 在 kyxkcoach (专睇老片) 的大作中提到: 】
: 我长期做b/s开发. b/s系统的session机制一般是基于cookie实现:浏览器输入用户名/
: 密码,服务端校验成功后返回一个cookie并存储相关的用户信息,接下来的交互中浏览
: 器都会回送这个cookie,服务端根据cookie判断用户是否已登录,并从服务端存储的信
: ...................
C’est La Vie &&&& ※ 修改:·cybergene 于 Jul 27 19:07:05 2013 修改本文·[FROM: 123.117.224.*]
※ 来源:·水木社区 newsmth.net·[FROM: 123.117.224.*]
专睇老片发信人: kyxkcoach (专睇老片), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 19:45:46 2013), 站内 && 现在长连接是主流?
【 在 cybergene (基因~也许以后~~) 的大作中提到: 】
: 可以认证一次之后保持连接,类似qq。
: 如果非要模拟浏览器行为,那就实现一个类似cookie的机制。实在不行,每次access都验证用户名密码也不是不可以。考虑传输安全的话,用ssl即可。
&&&& -- && ※ 来源:·水木社区 newsmth.net·[FROM: 122.234.4.*]
环顾四方有效-巨型菜鸟发信人: CNMAN (环顾四方有效-巨型菜鸟), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 20:17:23 2013), 站内 && 业务是单一的就用时效性的token,客户端保存token,每次需要认证的业务接口用这个token就可以了,时效了就再取. && 业务是多个模块依赖认证服务的就用oauth或类似oauth的就行 && 为了安全,传密码取token的那步最好用https && 【 在 kyxkcoach (专睇老片) 的大作中提到: 】
: 我长期做b/s开发. b/s系统的session机制一般是基于cookie实现:浏览器输入用户名/
: 密码,服务端校验成功后返回一个cookie并存储相关的用户信息,接下来的交互中浏览
: 器都会回送这个cookie,服务端根据cookie判断用户是否已登录,并从服务端存储的信
: ...................
&& -- && ※ 来源:·水木社区 ·[FROM: 114.245.251.*]
奥路菲发信人: Orpherus (奥路菲), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 20:42:12 2013), 站内 && 根据sessionid取得session的过程已经包含校验了,从这个意义上说,session也是每次都验证身份的。
-- && ※ 来源:·水木社区 ·[FROM: 218.82.166.*]
大王叫我来巡山发信人: samyou090 (落埃无蒂), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sat Jul 27 23:43:56 2013), 站内 && defaulthttpclient可以保存和传输cookie啊
-- && ※ 来源:·水木社区 ·[FROM: 211.155.113.*]
专睇老片发信人: kyxkcoach (专睇老片), 信区: MobileDev
标&&题: Re: 安卓与服务器之间的session机制一般是怎么实现的?
发信站: 水木社区 (Sun Jul 28 11:01:19 2013), 站内 && 好。 这些方案中,AccountManager一般有作用吗?
【 在 CNMAN (环顾四方有效-巨型菜鸟) 的大作中提到: 】
: 业务是单一的就用时效性的token,客户端保存token,每次需要认证的业务接口用这个token就可以了,时效了就再取.
: 业务是多个模块依赖认证服务的就用oauth或类似oauth的就行
: 为了安全,传密码取token的那步最好用https
: ...................
&& -- && ※ 来源:·水木社区 newsmth.net·[FROM: 122.234.4.*]
文章数:7&分页:中国领先的IT技术网站
51CTO旗下网站
基于Java Socket的自定义协议,实现Android与服务器的长连接(二)
上一篇文章中,我们对socket编程和自定义协议做了一个简单的了解,本文将在此基础上加以深入,来实现Android和服务器之间的长连接。
作者:枚杉来源:| 17:19
在阅读本文前需要对socket以及自定义协议有一个基本的了解,可以先查看上一篇文章学习相关的基础知识点。
一、协议定义
上一篇文章中,我们对socket编程和自定义协议做了一个简单的了解,本文将在此基础上加以深入,来实现Android和服务器之间的长连接,现定义协议如下:
数据类协议(Data)
长度(length,32bit)
版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
数据类型(type,8bit,0表示数据)
业务类型(pattion,8bit,0表示push,其他暂未定)
数据格式(dtype,8bit,0表示json,其他暂未定)
消息id(msgId,32bit)
正文数据(data)
数据ack类协议(DataAck)
长度(length,32bit)
版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
数据类型(type,8bit,1表示数据ack)
ack消息id(ackMsgId,32bit)
预留信息(unused)
心跳类协议(ping)
长度(length,32bit)
版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
数据类型(type,8bit,2表示心跳)
心跳id(pingId,32bit,client上报取奇数,即1,3,5...,server下发取偶数,即0,2,4...)
预留信息(unused)
心跳ack类协议(pingAck)
长度(length,32bit)
版本号(version,8bit,前3位预留,后5位用于表示真正的版本号)
数据类型(type,8bit,3表示心跳ack)
ack心跳id(pingId,32bit,client上报取奇数,即1,3,5...,server下发取偶数,即0,2,4...)
预留信息(unused)
二、协议实现
从上述的协议定义中,我们可以看出,四种协议有共同的3个要素,分别是:长度、版本号、数据类型,那么我们可以先抽象出一个基本的协议,如下:
1. BasicProtocol
import&android.util.L&&import&com.shandiangou.sdgprotocol.lib.C&import&com.shandiangou.sdgprotocol.lib.ProtocolE&import&com.shandiangou.sdgprotocol.lib.SocketU&&import&java.io.ByteArrayOutputS&&/**&&*&Created&by&meishan&on&16/12/1.&&*&&p&&&*&协议类型:&0表示数据,1表示数据Ack,2表示ping,3表示pingAck&&*/&public&abstract&class&BasicProtocol&{&&&&&&//&长度均以字节(byte)为单位&&&&&public&static&final&int&LENGTH_LEN&=&4;&&&&&&&//记录整条数据长度数值的长度&&&&&protected&static&final&int&VER_LEN&=&1;&&&&&&&//协议的版本长度(其中前3位作为预留位,后5位作为版本号)&&&&&protected&static&final&int&TYPE_LEN&=&1;&&&&&&//协议的数据类型长度&&&&&&private&int&reserved&=&0;&&&&&&&&&&&&&&&&&&&&&//预留信息&&&&&private&int&version&=&Config.VERSION;&&&&&&&&&//版本号&&&&&&/**&&&&&&*&获取整条数据长度&&&&&&*&单位:字节(byte)&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&protected&int&getLength()&{&&&&&&&&&return&LENGTH_LEN&+&VER_LEN&+&TYPE_LEN;&&&&&}&&&&&&public&int&getReserved()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setReserved(int&reserved)&{&&&&&&&&&this.reserved&=&&&&&&}&&&&&&public&int&getVersion()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setVersion(int&version)&{&&&&&&&&&this.version&=&&&&&&}&&&&&&/**&&&&&&*&获取协议类型,由子类实现&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&public&abstract&int&getProtocolType();&&&&&&&&&&/**&&&&&&*&由预留值和版本号计算完整版本号的byte[]值&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&private&int&getVer(byte&r,&byte&v,&int&vLen)&{&&&&&&&&&int&num&=&0;&&&&&&&&&int&rLen&=&8&-&vL&&&&&&&&&for&(int&i&=&0;&i&&&rL&i++)&{&&&&&&&&&&&&&num&+=&(((r&&&&(rLen&-&1&-&i))&&&0x1)&&&&(7&-&i));&&&&&&&&&}&&&&&&&&&return&num&+&v;&&&&&}&&&&&&/**&&&&&&*&拼接发送数据,此处拼接了协议版本、协议类型和数据长度,具体内容子类中再拼接&&&&&&*&按顺序拼接&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&public&byte[]&genContentData()&{&&&&&&&&&byte[]&length&=&SocketUtil.int2ByteArrays(getLength());&&&&&&&&&byte&reserved&=&(byte)&getReserved();&&&&&&&&&byte&version&=&(byte)&getVersion();&&&&&&&&&byte[]&ver&=&{(byte)&getVer(reserved,&version,&5)};&&&&&&&&&byte[]&type&=&{(byte)&getProtocolType()};&&&&&&&&&&ByteArrayOutputStream&baos&=&new&ByteArrayOutputStream(LENGTH_LEN&+&VER_LEN&+&TYPE_LEN);&&&&&&&&&baos.write(length,&0,&LENGTH_LEN);&&&&&&&&&baos.write(ver,&0,&VER_LEN);&&&&&&&&&baos.write(type,&0,&TYPE_LEN);&&&&&&&&&return&baos.toByteArray();&&&&&}&&&&&&/**&&&&&&*&解析出整条数据长度&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*/&&&&&protected&int&parseLength(byte[]&data)&{&&&&&&&&&return&SocketUtil.byteArrayToInt(data,&0,&LENGTH_LEN);&&&&&}&&&&&&/**&&&&&&*&解析出预留位&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*/&&&&&protected&int&parseReserved(byte[]&data)&{&&&&&&&&&byte&r&=&data[LENGTH_LEN];//前4个字节(0,1,2,3)为数据长度的int值,与版本号组成一个字节&&&&&&&&&return&(r&&&&5)&&&0xFF;&&&&&}&&&&&&/**&&&&&&*&解析出版本号&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*/&&&&&protected&int&parseVersion(byte[]&data)&{&&&&&&&&&byte&v&=&data[LENGTH_LEN];&//与预留位组成一个字节&&&&&&&&&return&((v&&&&3)&&&0xFF)&&&&3;&&&&&}&&&&&&/**&&&&&&*&解析出协议类型&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*/&&&&&public&static&int&parseType(byte[]&data)&{&&&&&&&&&byte&t&=&data[LENGTH_LEN&+&VER_LEN];//前4个字节(0,1,2,3)为数据长度的int值,以及ver占一个字节&&&&&&&&&return&t&&&0xFF;&&&&&}&&&&&&/**&&&&&&*&解析接收数据,此处解析了协议版本、协议类型和数据长度,具体内容子类中再解析&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*&@throws&ProtocolException&协议版本不一致,抛出异常&&&&&&*/&&&&&public&int&parseContentData(byte[]&data)&throws&ProtocolException&{&&&&&&&&&int&reserved&=&parseReserved(data);&&&&&&&&&int&version&=&parseVersion(data);&&&&&&&&&int&protocolType&=&parseType(data);&&&&&&&&&if&(version&!=&getVersion())&{&&&&&&&&&&&&&throw&new&ProtocolException(&input&version&is&error:&&&+&version);&&&&&&&&&}&&&&&&&&&return&LENGTH_LEN&+&VER_LEN&+&TYPE_LEN;&&&&&}&&&&&&@Override&&&&&public&String&toString()&{&&&&&&&&&return&&Version:&&&+&getVersion()&+&&,&Type:&&&+&getProtocolType();&&&&&}&}&&
上述涉及到的Config类和SocketUtil类如下:
/**&&*&Created&by&meishan&on&16/12/2.&&*/&public&class&Config&{&&&&&&public&static&final&int&VERSION&=&1;&&&&&&&&&&&&&&&&&//协议版本号&&&&&public&static&final&String&ADDRESS&=&&10.17.64.237&;&//服务器地址&&&&&public&static&final&int&PORT&=&9013;&&&&&&&&&&&&&&&&&//服务器端口号&&&&&&}&&
import&java.io.BufferedInputS&import&java.io.BufferedOutputS&import&java.io.IOE&import&java.io.InputS&import&java.io.OutputS&import&java.nio.ByteB&import&java.util.HashM&import&java.util.M&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&SocketUtil&{&&&&&&private&static&Map&Integer,&String&&msgImp&=&new&HashMap&&();&&&&&&static&{&&&&&&&&&msgImp.put(DataProtocol.PROTOCOL_TYPE,&&com.shandiangou.sdgprotocol.lib.protocol.DataProtocol&);&&&&&&&//0&&&&&&&&&msgImp.put(DataAckProtocol.PROTOCOL_TYPE,&&com.shandiangou.sdgprotocol.lib.protocol.DataAckProtocol&);&//1&&&&&&&&&msgImp.put(PingProtocol.PROTOCOL_TYPE,&&com.shandiangou.sdgprotocol.lib.protocol.PingProtocol&);&&&&&&&//2&&&&&&&&&msgImp.put(PingAckProtocol.PROTOCOL_TYPE,&&com.shandiangou.sdgprotocol.lib.protocol.PingAckProtocol&);&//3&&&&&}&&&&&&/**&&&&&&*&解析数据内容&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*/&&&&&public&static&BasicProtocol&parseContentMsg(byte[]&data)&{&&&&&&&&&int&protocolType&=&BasicProtocol.parseType(data);&&&&&&&&&String&className&=&msgImp.get(protocolType);&&&&&&&&&BasicProtocol&basicP&&&&&&&&&try&{&&&&&&&&&&&&&basicProtocol&=&(BasicProtocol)&Class.forName(className).newInstance();&&&&&&&&&&&&&basicProtocol.parseContentData(data);&&&&&&&&&}&catch&(Exception&e)&{&&&&&&&&&&&&&basicProtocol&=&null;&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&&&&&return&basicP&&&&&}&&&&&&/**&&&&&&*&读数据&&&&&&*&&&&&&*&@param&inputStream&&&&&&*&@return&&&&&&*&@throws&SocketExceptions&&&&&&*/&&&&&public&static&BasicProtocol&readFromStream(InputStream&inputStream)&{&&&&&&&&&BasicProtocol&&&&&&&&&&BufferedInputStream&&&&&&&&&&&&&&&&&&&//header中保存的是整个数据的长度值,4个字节表示。在下述write2Stream方法中,会先写入header&&&&&&&&&byte[]&header&=&new&byte[BasicProtocol.LENGTH_LEN];&&&&&&&&&&try&{&&&&&&&&&&&&&bis&=&new&BufferedInputStream(inputStream);&&&&&&&&&&&&&&int&temp;&&&&&&&&&&&&&int&len&=&0;&&&&&&&&&&&&&while&(len&&&header.length)&{&&&&&&&&&&&&&&&&&temp&=&bis.read(header,&len,&header.length&-&len);&&&&&&&&&&&&&&&&&if&(temp&&&0)&{&&&&&&&&&&&&&&&&&&&&&len&+=&temp;&&&&&&&&&&&&&&&&&}&else&if&(temp&==&-1)&{&&&&&&&&&&&&&&&&&&&&&bis.close();&&&&&&&&&&&&&&&&&&&&&return&null;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&len&=&0;&&&&&&&&&&&&&int&length&=&byteArrayToInt(header);//数据的长度值&&&&&&&&&&&&&byte[]&content&=&new&byte[length];&&&&&&&&&&&&&while&(len&&&length)&{&&&&&&&&&&&&&&&&&temp&=&bis.read(content,&len,&length&-&len);&&&&&&&&&&&&&&&&&&if&(temp&&&0)&{&&&&&&&&&&&&&&&&&&&&&len&+=&temp;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&protocol&=&parseContentMsg(content);&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&return&null;&&&&&&&&&}&&&&&&&&&&return&&&&&&}&&&&&&/**&&&&&&*&写数据&&&&&&*&&&&&&*&@param&protocol&&&&&&*&@param&outputStream&&&&&&*/&&&&&public&static&void&write2Stream(BasicProtocol&protocol,&OutputStream&outputStream)&{&&&&&&&&&BufferedOutputStream&bufferedOutputStream&=&new&BufferedOutputStream(outputStream);&&&&&&&&&byte[]&buffData&=&protocol.genContentData();&&&&&&&&&byte[]&header&=&int2ByteArrays(buffData.length);&&&&&&&&&try&{&&&&&&&&&&&&&bufferedOutputStream.write(header);&&&&&&&&&&&&&bufferedOutputStream.write(buffData);&&&&&&&&&&&&&bufferedOutputStream.flush();&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&关闭输入流&&&&&&*&&&&&&*&@param&is&&&&&&*/&&&&&public&static&void&closeInputStream(InputStream&is)&{&&&&&&&&&try&{&&&&&&&&&&&&&if&(is&!=&null)&{&&&&&&&&&&&&&&&&&is.close();&&&&&&&&&&&&&}&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&关闭输出流&&&&&&*&&&&&&*&@param&os&&&&&&*/&&&&&public&static&void&closeOutputStream(OutputStream&os)&{&&&&&&&&&try&{&&&&&&&&&&&&&if&(os&!=&null)&{&&&&&&&&&&&&&&&&&os.close();&&&&&&&&&&&&&}&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&public&static&byte[]&int2ByteArrays(int&i)&{&&&&&&&&&byte[]&result&=&new&byte[4];&&&&&&&&&result[0]&=&(byte)&((i&&&&24)&&&0xFF);&&&&&&&&&result[1]&=&(byte)&((i&&&&16)&&&0xFF);&&&&&&&&&result[2]&=&(byte)&((i&&&&8)&&&0xFF);&&&&&&&&&result[3]&=&(byte)&(i&&&0xFF);&&&&&&&&&return&&&&&&}&&&&&&public&static&int&byteArrayToInt(byte[]&b)&{&&&&&&&&&int&intValue&=&0;&&&&&&&&&for&(int&i&=&0;&i&&&b.&i++)&{&&&&&&&&&&&&&intValue&+=&(b[i]&&&0xFF)&&&&(8&*&(3&-&i));&//int占4个字节(0,1,2,3)&&&&&&&&&}&&&&&&&&&return&intV&&&&&}&&&&&&public&static&int&byteArrayToInt(byte[]&b,&int&byteOffset,&int&byteCount)&{&&&&&&&&&int&intValue&=&0;&&&&&&&&&for&(int&i&=&byteO&i&&&(byteOffset&+&byteCount);&i++)&{&&&&&&&&&&&&&intValue&+=&(b[i]&&&0xFF)&&&&(8&*&(3&-&(i&-&byteOffset)));&&&&&&&&&}&&&&&&&&&return&intV&&&&&}&&&&&&public&static&int&bytes2Int(byte[]&b,&int&byteOffset)&{&&&&&&&&&ByteBuffer&byteBuffer&=&ByteBuffer.allocate(Integer.SIZE&/&Byte.SIZE);&&&&&&&&&byteBuffer.put(b,&byteOffset,&4);&//占4个字节&&&&&&&&&byteBuffer.flip();&&&&&&&&&return&byteBuffer.getInt();&&&&&}&}&&
接下来我们实现具体的协议。
2. DataProtocol
import&android.util.L&&import&com.shandiangou.sdgprotocol.lib.ProtocolE&import&com.shandiangou.sdgprotocol.lib.SocketU&&import&java.io.ByteArrayOutputS&import&java.io.Serializable;&import&java.io.UnsupportedEncodingE&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&DataProtocol&extends&BasicProtocol&implements&Serializable&{&&&&&&public&static&final&int&PROTOCOL_TYPE&=&0;&&&&&&private&static&final&int&PATTION_LEN&=&1;&&&&&private&static&final&int&DTYPE_LEN&=&1;&&&&&private&static&final&int&MSGID_LEN&=&4;&&&&&&private&int&&&&&&private&int&&&&&&private&int&msgId;&&&&&&private&String&&&&&&&@Override&&&&&public&int&getLength()&{&&&&&&&&&return&super.getLength()&+&PATTION_LEN&+&DTYPE_LEN&+&MSGID_LEN&+&data.getBytes().&&&&&}&&&&&&@Override&&&&&public&int&getProtocolType()&{&&&&&&&&&return&PROTOCOL_TYPE;&&&&&}&&&&&&public&int&getPattion()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setPattion(int&pattion)&{&&&&&&&&&this.pattion&=&&&&&&}&&&&&&public&int&getDtype()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setDtype(int&dtype)&{&&&&&&&&&this.dtype&=&&&&&&}&&&&&&public&void&setMsgId(int&msgId)&{&&&&&&&&&this.msgId&=&msgId;&&&&&}&&&&&&public&int&getMsgId()&{&&&&&&&&&return&msgId;&&&&&}&&&&&&public&String&getData()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setData(String&data)&{&&&&&&&&&this.data&=&&&&&&}&&&&&&/**&&&&&&*&拼接发送数据&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&@Override&&&&&public&byte[]&genContentData()&{&&&&&&&&&byte[]&base&=&super.genContentData();&&&&&&&&&byte[]&pattion&=&{(byte)&this.pattion};&&&&&&&&&byte[]&dtype&=&{(byte)&this.dtype};&&&&&&&&&byte[]&msgid&=&SocketUtil.int2ByteArrays(this.msgId);&&&&&&&&&byte[]&data&=&this.data.getBytes();&&&&&&&&&&ByteArrayOutputStream&baos&=&new&ByteArrayOutputStream(getLength());&&&&&&&&&baos.write(base,&0,&base.length);&&&&&&&&&&//协议版本+数据类型+数据长度+消息id&&&&&&&&&baos.write(pattion,&0,&PATTION_LEN);&&&&&&&//业务类型&&&&&&&&&baos.write(dtype,&0,&DTYPE_LEN);&&&&&&&&&&&//业务数据格式&&&&&&&&&baos.write(msgid,&0,&MSGID_LEN);&&&&&&&&&&&//消息id&&&&&&&&&baos.write(data,&0,&data.length);&&&&&&&&&&//业务数据&&&&&&&&&return&baos.toByteArray();&&&&&}&&&&&&/**&&&&&&*&解析接收数据,按顺序解析&&&&&&*&&&&&&*&@param&data&&&&&&*&@return&&&&&&*&@throws&ProtocolException&&&&&&*/&&&&&@Override&&&&&public&int&parseContentData(byte[]&data)&throws&ProtocolException&{&&&&&&&&&int&pos&=&super.parseContentData(data);&&&&&&&&&&//解析pattion&&&&&&&&&pattion&=&data[pos]&&&0xFF;&&&&&&&&&pos&+=&PATTION_LEN;&&&&&&&&&&//解析dtype&&&&&&&&&dtype&=&data[pos]&&&0xFF;&&&&&&&&&pos&+=&DTYPE_LEN;&&&&&&&&&&//解析msgId&&&&&&&&&msgId&=&SocketUtil.byteArrayToInt(data,&pos,&MSGID_LEN);&&&&&&&&&pos&+=&MSGID_LEN;&&&&&&&&&&//解析data&&&&&&&&&try&{&&&&&&&&&&&&&this.data&=&new&String(data,&pos,&data.length&-&pos,&&utf-8&);&&&&&&&&&}&catch&(UnsupportedEncodingException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&&&&&&return&&&&&&}&&&&&&@Override&&&&&public&String&toString()&{&&&&&&&&&return&&data:&&&+&&&&&&}&}&&
3. DataAckProtocol
import&com.shandiangou.sdgprotocol.lib.ProtocolE&import&com.shandiangou.sdgprotocol.lib.SocketU&&import&java.io.ByteArrayOutputS&import&java.io.UnsupportedEncodingE&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&DataAckProtocol&extends&BasicProtocol&{&&&&&&public&static&final&int&PROTOCOL_TYPE&=&1;&&&&&&private&static&final&int&ACKMSGID_LEN&=&4;&&&&&&private&int&ackMsgId;&&&&&&private&String&&&&&&&@Override&&&&&public&int&getLength()&{&&&&&&&&&return&super.getLength()&+&ACKMSGID_LEN&+&unused.getBytes().&&&&&}&&&&&&@Override&&&&&public&int&getProtocolType()&{&&&&&&&&&return&PROTOCOL_TYPE;&&&&&}&&&&&&public&int&getAckMsgId()&{&&&&&&&&&return&ackMsgId;&&&&&}&&&&&&public&void&setAckMsgId(int&ackMsgId)&{&&&&&&&&&this.ackMsgId&=&ackMsgId;&&&&&}&&&&&&public&String&getUnused()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setUnused(String&unused)&{&&&&&&&&&this.unused&=&&&&&&}&&&&&&/**&&&&&&*&拼接发送数据&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&@Override&&&&&public&byte[]&genContentData()&{&&&&&&&&&byte[]&base&=&super.genContentData();&&&&&&&&&byte[]&ackMsgId&=&SocketUtil.int2ByteArrays(this.ackMsgId);&&&&&&&&&byte[]&unused&=&this.unused.getBytes();&&&&&&&&&&ByteArrayOutputStream&baos&=&new&ByteArrayOutputStream(getLength());&&&&&&&&&baos.write(base,&0,&base.length);&&&&&&&&&&&&&&//协议版本+数据类型+数据长度+消息id&&&&&&&&&baos.write(ackMsgId,&0,&ACKMSGID_LEN);&&&&&&&&&//消息id&&&&&&&&&baos.write(unused,&0,&unused.length);&&&&&&&&&&//unused&&&&&&&&&return&baos.toByteArray();&&&&&}&&&&&&@Override&&&&&public&int&parseContentData(byte[]&data)&throws&ProtocolException&{&&&&&&&&&int&pos&=&super.parseContentData(data);&&&&&&&&&&//解析ackMsgId&&&&&&&&&ackMsgId&=&SocketUtil.byteArrayToInt(data,&pos,&ACKMSGID_LEN);&&&&&&&&&pos&+=&ACKMSGID_LEN;&&&&&&&&&&//解析unused&&&&&&&&&try&{&&&&&&&&&&&&&unused&=&new&String(data,&pos,&data.length&-&pos,&&utf-8&);&&&&&&&&&}&catch&(UnsupportedEncodingException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&&&&&&return&&&&&&}&&}&&
4. PingProtocol
import&com.shandiangou.sdgprotocol.lib.ProtocolE&import&com.shandiangou.sdgprotocol.lib.SocketU&&import&java.io.ByteArrayOutputS&import&java.io.UnsupportedEncodingE&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&PingProtocol&extends&BasicProtocol&{&&&&&&public&static&final&int&PROTOCOL_TYPE&=&2;&&&&&&private&static&final&int&PINGID_LEN&=&4;&&&&&&private&int&pingId;&&&&&&private&String&&&&&&&@Override&&&&&public&int&getLength()&{&&&&&&&&&return&super.getLength()&+&PINGID_LEN&+&unused.getBytes().&&&&&}&&&&&&@Override&&&&&public&int&getProtocolType()&{&&&&&&&&&return&PROTOCOL_TYPE;&&&&&}&&&&&&public&int&getPingId()&{&&&&&&&&&return&pingId;&&&&&}&&&&&&public&void&setPingId(int&pingId)&{&&&&&&&&&this.pingId&=&pingId;&&&&&}&&&&&&public&String&getUnused()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setUnused(String&unused)&{&&&&&&&&&this.unused&=&&&&&&}&&&&&&/**&&&&&&*&拼接发送数据&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&@Override&&&&&public&byte[]&genContentData()&{&&&&&&&&&byte[]&base&=&super.genContentData();&&&&&&&&&byte[]&pingId&=&SocketUtil.int2ByteArrays(this.pingId);&&&&&&&&&byte[]&unused&=&this.unused.getBytes();&&&&&&&&&&ByteArrayOutputStream&baos&=&new&ByteArrayOutputStream(getLength());&&&&&&&&&baos.write(base,&0,&base.length);&&&&&&&&&&//协议版本+数据类型+数据长度+消息id&&&&&&&&&baos.write(pingId,&0,&PINGID_LEN);&&&&&&&&&//消息id&&&&&&&&&baos.write(unused,&0,&unused.length);&&&&&&&&&&&&//unused&&&&&&&&&return&baos.toByteArray();&&&&&}&&&&&&@Override&&&&&public&int&parseContentData(byte[]&data)&throws&ProtocolException&{&&&&&&&&&int&pos&=&super.parseContentData(data);&&&&&&&&&&//解析pingId&&&&&&&&&pingId&=&SocketUtil.byteArrayToInt(data,&pos,&PINGID_LEN);&&&&&&&&&pos&+=&PINGID_LEN;&&&&&&&&&&try&{&&&&&&&&&&&&&unused&=&new&String(data,&pos,&data.length&-&pos,&&utf-8&);&&&&&&&&&}&catch&(UnsupportedEncodingException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&&&&&&return&&&&&&}&&}&&
5. PingAckProtocol
import&com.shandiangou.sdgprotocol.lib.ProtocolE&import&com.shandiangou.sdgprotocol.lib.SocketU&&import&java.io.ByteArrayOutputS&import&java.io.UnsupportedEncodingE&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&PingAckProtocol&extends&BasicProtocol&{&&&&&&public&static&final&int&PROTOCOL_TYPE&=&3;&&&&&&private&static&final&int&ACKPINGID_LEN&=&4;&&&&&&private&int&ackPingId;&&&&&&private&String&&&&&&&@Override&&&&&public&int&getLength()&{&&&&&&&&&return&super.getLength()&+&ACKPINGID_LEN&+&unused.getBytes().&&&&&}&&&&&&@Override&&&&&public&int&getProtocolType()&{&&&&&&&&&return&PROTOCOL_TYPE;&&&&&}&&&&&&public&int&getAckPingId()&{&&&&&&&&&return&ackPingId;&&&&&}&&&&&&public&void&setAckPingId(int&ackPingId)&{&&&&&&&&&this.ackPingId&=&ackPingId;&&&&&}&&&&&&public&String&getUnused()&{&&&&&&&&&return&&&&&&}&&&&&&public&void&setUnused(String&unused)&{&&&&&&&&&this.unused&=&&&&&&}&&&&&&/**&&&&&&*&拼接发送数据&&&&&&*&&&&&&*&@return&&&&&&*/&&&&&@Override&&&&&public&byte[]&genContentData()&{&&&&&&&&&byte[]&base&=&super.genContentData();&&&&&&&&&byte[]&ackPingId&=&SocketUtil.int2ByteArrays(this.ackPingId);&&&&&&&&&byte[]&unused&=&this.unused.getBytes();&&&&&&&&&&ByteArrayOutputStream&baos&=&new&ByteArrayOutputStream(getLength());&&&&&&&&&baos.write(base,&0,&base.length);&&&&&&&&&&&&&&&&//协议版本+数据类型+数据长度+消息id&&&&&&&&&baos.write(ackPingId,&0,&ACKPINGID_LEN);&&&&&&&&&//消息id&&&&&&&&&baos.write(unused,&0,&unused.length);&&&&&&&&&&&&//unused&&&&&&&&&return&baos.toByteArray();&&&&&}&&&&&&@Override&&&&&public&int&parseContentData(byte[]&data)&throws&ProtocolException&{&&&&&&&&&int&pos&=&super.parseContentData(data);&&&&&&&&&&//解析ackPingId&&&&&&&&&ackPingId&=&SocketUtil.byteArrayToInt(data,&pos,&ACKPINGID_LEN);&&&&&&&&&pos&+=&ACKPINGID_LEN;&&&&&&&&&&//解析unused&&&&&&&&&try&{&&&&&&&&&&&&&unused&=&new&String(data,&pos,&data.length&-&pos,&&utf-8&);&&&&&&&&&}&catch&(UnsupportedEncodingException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&&&&&&return&&&&&&}&&}&&
三、任务调度
上述已经给出了四种协议的实现,接下来我们将使用它们来实现app和服务端之间的通信,这里我们把数据的发送、接收和心跳分别用一个线程去实现,具体如下:
import&android.os.H&import&android.os.L&import&android.os.M&import&android.util.L&&import&com.shandiangou.sdgprotocol.lib.protocol.BasicP&import&com.shandiangou.sdgprotocol.lib.protocol.DataP&import&com.shandiangou.sdgprotocol.lib.protocol.PingP&&import&java.io.IOE&import&java.io.InputS&import&java.io.OutputS&import&java.net.ConnectE&import&java.net.S&import&java.util.concurrent.ConcurrentLinkedQ&&import&javax.net.SocketF&&/**&&*&写数据采用死循环,没有数据时wait,有新消息时notify&&*&&p&&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&ClientRequestTask&implements&Runnable&{&&&&&&private&static&final&int&SUCCESS&=&100;&&&&&private&static&final&int&FAILED&=&-1;&&&&&&private&boolean&isLongConnection&=&true;&&&&&private&Handler&mH&&&&&private&SendTask&mSendT&&&&&private&ReciveTask&mReciveT&&&&&private&HeartBeatTask&mHeartBeatT&&&&&private&Socket&mS&&&&&&private&boolean&isSocketA&&&&&private&boolean&closeSendT&&&&&&protected&volatile&ConcurrentLinkedQueue&BasicProtocol&&dataQueue&=&new&ConcurrentLinkedQueue&&();&&&&&&public&ClientRequestTask(RequestCallBack&requestCallBacks)&{&&&&&&&&&mHandler&=&new&MyHandler(requestCallBacks);&&&&&}&&&&&&@Override&&&&&public&void&run()&{&&&&&&&&&try&{&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&mSocket&=&SocketFactory.getDefault().createSocket(Config.ADDRESS,&Config.PORT);&//&&&&&&&&&&&&&&&&mSocket.setSoTimeout(10);&&&&&&&&&&&&&}&catch&(ConnectException&e)&{&&&&&&&&&&&&&&&&&failedMessage(-1,&&服务器连接异常,请检查网络&);&&&&&&&&&&&&&&&&&return;&&&&&&&&&&&&&}&&&&&&&&&&&&&&isSocketAvailable&=&true;&&&&&&&&&&&&&&//开启接收线程&&&&&&&&&&&&&mReciveTask&=&new&ReciveTask();&&&&&&&&&&&&&mReciveTask.inputStream&=&mSocket.getInputStream();&&&&&&&&&&&&&mReciveTask.start();&&&&&&&&&&&&&&//开启发送线程&&&&&&&&&&&&&mSendTask&=&new&SendTask();&&&&&&&&&&&&&mSendTask.outputStream&=&mSocket.getOutputStream();&&&&&&&&&&&&&mSendTask.start();&&&&&&&&&&&&&&//开启心跳线程&&&&&&&&&&&&&if&(isLongConnection)&{&&&&&&&&&&&&&&&&&mHeartBeatTask&=&new&HeartBeatTask();&&&&&&&&&&&&&&&&&mHeartBeatTask.outputStream&=&mSocket.getOutputStream();&&&&&&&&&&&&&&&&&mHeartBeatTask.start();&&&&&&&&&&&&&}&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&failedMessage(-1,&&网络发生异常,请稍后重试&);&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&public&void&addRequest(DataProtocol&data)&{&&&&&&&&&dataQueue.add(data);&&&&&&&&&toNotifyAll(dataQueue);//有新增待发送数据,则唤醒发送线程&&&&&}&&&&&&public&synchronized&void&stop()&{&&&&&&&&&&//关闭接收线程&&&&&&&&&closeReciveTask();&&&&&&&&&&//关闭发送线程&&&&&&&&&closeSendTask&=&true;&&&&&&&&&toNotifyAll(dataQueue);&&&&&&&&&&//关闭心跳线程&&&&&&&&&closeHeartBeatTask();&&&&&&&&&&//关闭socket&&&&&&&&&closeSocket();&&&&&&&&&&//清除数据&&&&&&&&&clearData();&&&&&&&&&&failedMessage(-1,&&断开连接&);&&&&&}&&&&&&/**&&&&&&*&关闭接收线程&&&&&&*/&&&&&private&void&closeReciveTask()&{&&&&&&&&&if&(mReciveTask&!=&null)&{&&&&&&&&&&&&&mReciveTask.interrupt();&&&&&&&&&&&&&mReciveTask.isCancle&=&true;&&&&&&&&&&&&&if&(mReciveTask.inputStream&!=&null)&{&&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&&if&(isSocketAvailable&&&&!mSocket.isClosed()&&&&mSocket.isConnected())&{&&&&&&&&&&&&&&&&&&&&&&&&&mSocket.shutdownInput();//解决java.net.SocketException问题,需要先shutdownInput&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&SocketUtil.closeInputStream(mReciveTask.inputStream);&&&&&&&&&&&&&&&&&mReciveTask.inputStream&=&null;&&&&&&&&&&&&&}&&&&&&&&&&&&&mReciveTask&=&null;&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&关闭发送线程&&&&&&*/&&&&&private&void&closeSendTask()&{&&&&&&&&&if&(mSendTask&!=&null)&{&&&&&&&&&&&&&mSendTask.isCancle&=&true;&&&&&&&&&&&&&mSendTask.interrupt();&&&&&&&&&&&&&if&(mSendTask.outputStream&!=&null)&{&&&&&&&&&&&&&&&&&synchronized&(mSendTask.outputStream)&{//防止写数据时停止,写完再停&&&&&&&&&&&&&&&&&&&&&SocketUtil.closeOutputStream(mSendTask.outputStream);&&&&&&&&&&&&&&&&&&&&&mSendTask.outputStream&=&null;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&mSendTask&=&null;&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&关闭心跳线程&&&&&&*/&&&&&private&void&closeHeartBeatTask()&{&&&&&&&&&if&(mHeartBeatTask&!=&null)&{&&&&&&&&&&&&&mHeartBeatTask.isCancle&=&true;&&&&&&&&&&&&&if&(mHeartBeatTask.outputStream&!=&null)&{&&&&&&&&&&&&&&&&&SocketUtil.closeOutputStream(mHeartBeatTask.outputStream);&&&&&&&&&&&&&&&&&mHeartBeatTask.outputStream&=&null;&&&&&&&&&&&&&}&&&&&&&&&&&&&mHeartBeatTask&=&null;&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&关闭socket&&&&&&*/&&&&&private&void&closeSocket()&{&&&&&&&&&if&(mSocket&!=&null)&{&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&mSocket.close();&&&&&&&&&&&&&&&&&isSocketAvailable&=&false;&&&&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&}&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&清除数据&&&&&&*/&&&&&private&void&clearData()&{&&&&&&&&&dataQueue.clear();&&&&&&&&&isLongConnection&=&false;&&&&&}&&&&&&private&void&toWait(Object&o)&{&&&&&&&&&synchronized&(o)&{&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&o.wait();&&&&&&&&&&&&&}&catch&(InterruptedException&e)&{&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&}&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后&&&&&&*&&&&&&*&@param&o&&&&&&*/&&&&&protected&void&toNotifyAll(Object&o)&{&&&&&&&&&synchronized&(o)&{&&&&&&&&&&&&&o.notifyAll();&&&&&&&&&}&&&&&}&&&&&&private&void&failedMessage(int&code,&String&msg)&{&&&&&&&&&Message&message&=&mHandler.obtainMessage(FAILED);&&&&&&&&&message.what&=&FAILED;&&&&&&&&&message.arg1&=&&&&&&&&&&message.obj&=&&&&&&&&&&mHandler.sendMessage(message);&&&&&}&&&&&&private&void&successMessage(BasicProtocol&protocol)&{&&&&&&&&&Message&message&=&mHandler.obtainMessage(SUCCESS);&&&&&&&&&message.what&=&SUCCESS;&&&&&&&&&message.obj&=&&&&&&&&&&mHandler.sendMessage(message);&&&&&}&&&&&&private&boolean&isConnected()&{&&&&&&&&&if&(mSocket.isClosed()&||&!mSocket.isConnected())&{&&&&&&&&&&&&&ClientRequestTask.this.stop();&&&&&&&&&&&&&return&false;&&&&&&&&&}&&&&&&&&&return&true;&&&&&}&&&&&&/**&&&&&&*&服务器返回处理,主线程运行&&&&&&*/&&&&&public&class&MyHandler&extends&Handler&{&&&&&&&&&&private&RequestCallBack&mRequestCallB&&&&&&&&&&public&MyHandler(RequestCallBack&callBack)&{&&&&&&&&&&&&&super(Looper.getMainLooper());&&&&&&&&&&&&&this.mRequestCallBack&=&callB&&&&&&&&&}&&&&&&&&&&@Override&&&&&&&&&public&void&handleMessage(Message&msg)&{&&&&&&&&&&&&&super.handleMessage(msg);&&&&&&&&&&&&&switch&(msg.what)&{&&&&&&&&&&&&&&&&&case&SUCCESS:&&&&&&&&&&&&&&&&&&&&&mRequestCallBack.onSuccess((BasicProtocol)&msg.obj);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case&FAILED:&&&&&&&&&&&&&&&&&&&&&mRequestCallBack.onFailed(msg.arg1,&(String)&msg.obj);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&数据接收线程&&&&&&*/&&&&&public&class&ReciveTask&extends&Thread&{&&&&&&&&&&private&boolean&isCancle&=&false;&&&&&&&&&private&InputStream&inputS&&&&&&&&&&@Override&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&while&(!isCancle)&{&&&&&&&&&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&if&(inputStream&!=&null)&{&&&&&&&&&&&&&&&&&&&&&BasicProtocol&reciverData&=&SocketUtil.readFromStream(inputStream);&&&&&&&&&&&&&&&&&&&&&if&(reciverData&!=&null)&{&&&&&&&&&&&&&&&&&&&&&&&&&if&(reciverData.getProtocolType()&==&1&||&reciverData.getProtocolType()&==&3)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&successMessage(reciverData);&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&}&else&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&SocketUtil.closeInputStream(inputStream);//循环结束则退出输入流&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&数据发送线程&&&&&&*&当没有发送数据时让线程等待&&&&&&*/&&&&&public&class&SendTask&extends&Thread&{&&&&&&&&&&private&boolean&isCancle&=&false;&&&&&&&&&private&OutputStream&outputS&&&&&&&&&&@Override&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&while&(!isCancle)&{&&&&&&&&&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&BasicProtocol&dataContent&=&dataQueue.poll();&&&&&&&&&&&&&&&&&if&(dataContent&==&null)&{&&&&&&&&&&&&&&&&&&&&&toWait(dataQueue);//没有发送数据则等待&&&&&&&&&&&&&&&&&&&&&if&(closeSendTask)&{&&&&&&&&&&&&&&&&&&&&&&&&&closeSendTask();//notify()调用后,并不是马上就释放对象锁的,所以在此处中断发送线程&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&else&if&(outputStream&!=&null)&{&&&&&&&&&&&&&&&&&&&&&synchronized&(outputStream)&{&&&&&&&&&&&&&&&&&&&&&&&&&SocketUtil.write2Stream(dataContent,&outputStream);&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&SocketUtil.closeOutputStream(outputStream);//循环结束则退出输出流&&&&&&&&&}&&&&&}&&&&&&/**&&&&&&*&心跳实现,频率5秒&&&&&&*&Created&by&meishan&on&16/12/1.&&&&&&*/&&&&&public&class&HeartBeatTask&extends&Thread&{&&&&&&&&&&private&static&final&int&REPEATTIME&=&5000;&&&&&&&&&private&boolean&isCancle&=&false;&&&&&&&&&private&OutputStream&outputS&&&&&&&&&private&int&pingId;&&&&&&&&&&@Override&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&pingId&=&1;&&&&&&&&&&&&&while&(!isCancle)&{&&&&&&&&&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&&mSocket.sendUrgentData(0xFF);&&&&&&&&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&isSocketAvailable&=&false;&&&&&&&&&&&&&&&&&&&&&ClientRequestTask.this.stop();&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&if&(outputStream&!=&null)&{&&&&&&&&&&&&&&&&&&&&&PingProtocol&pingProtocol&=&new&PingProtocol();&&&&&&&&&&&&&&&&&&&&&pingProtocol.setPingId(pingId);&&&&&&&&&&&&&&&&&&&&&pingProtocol.setUnused(&ping...&);&&&&&&&&&&&&&&&&&&&&&SocketUtil.write2Stream(pingProtocol,&outputStream);&&&&&&&&&&&&&&&&&&&&&pingId&=&pingId&+&2;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&&Thread.sleep(REPEATTIME);&&&&&&&&&&&&&&&&&}&catch&(InterruptedException&e)&{&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&SocketUtil.closeOutputStream(outputStream);&&&&&&&&&}&&&&&}&}&&
其中涉及到的RequestCallBack接口如下:
/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&interface&RequestCallBack&{&&&&&&void&onSuccess(BasicProtocol&msg);&&&&&&void&onFailed(int&errorCode,&String&msg);&}&&
import&java.io.DataInputS&import&java.io.DataOutputS&import&java.net.S&import&java.util.I&import&java.util.concurrent.ConcurrentHashM&import&java.util.concurrent.ConcurrentLinkedQ&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&ServerResponseTask&implements&Runnable&{&&&&&&private&ReciveTask&reciveT&&&&&private&SendTask&sendT&&&&&private&Socket&&&&&&private&ResponseCallback&tB&&&&&&private&volatile&ConcurrentLinkedQueue&BasicProtocol&&dataQueue&=&new&ConcurrentLinkedQueue&&();&&&&&private&static&ConcurrentHashMap&String,&Socket&&onLineClient&=&new&ConcurrentHashMap&&();&&&&&&private&String&userIP;&&&&&&public&String&getUserIP()&{&&&&&&&&&return&userIP;&&&&&}&&&&&&public&ServerResponseTask(Socket&socket,&ResponseCallback&tBack)&{&&&&&&&&&this.socket&=&&&&&&&&&&this.tBack&=&tB&&&&&&&&&this.userIP&=&socket.getInetAddress().getHostAddress();&&&&&&&&&System.out.println(&用户IP地址:&&+&userIP);&&&&&}&&&&&&@Override&&&&&public&void&run()&{&&&&&&&&&try&{&&&&&&&&&&&&&//开启接收线程&&&&&&&&&&&&&reciveTask&=&new&ReciveTask();&&&&&&&&&&&&&reciveTask.inputStream&=&new&DataInputStream(socket.getInputStream());&&&&&&&&&&&&&reciveTask.start();&&&&&&&&&&&&&&//开启发送线程&&&&&&&&&&&&&sendTask&=&new&SendTask();&&&&&&&&&&&&&sendTask.outputStream&=&new&DataOutputStream(socket.getOutputStream());&&&&&&&&&&&&&sendTask.start();&&&&&&&&&}&catch&(Exception&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&&&&&}&&&&&&public&void&stop()&{&&&&&&&&&if&(reciveTask&!=&null)&{&&&&&&&&&&&&&reciveTask.isCancle&=&true;&&&&&&&&&&&&&reciveTask.interrupt();&&&&&&&&&&&&&if&(reciveTask.inputStream&!=&null)&{&&&&&&&&&&&&&&&&&SocketUtil.closeInputStream(reciveTask.inputStream);&&&&&&&&&&&&&&&&&reciveTask.inputStream&=&null;&&&&&&&&&&&&&}&&&&&&&&&&&&&reciveTask&=&null;&&&&&&&&&}&&&&&&&&&&if&(sendTask&!=&null)&{&&&&&&&&&&&&&sendTask.isCancle&=&true;&&&&&&&&&&&&&sendTask.interrupt();&&&&&&&&&&&&&if&(sendTask.outputStream&!=&null)&{&&&&&&&&&&&&&&&&&synchronized&(sendTask.outputStream)&{//防止写数据时停止,写完再停&&&&&&&&&&&&&&&&&&&&&sendTask.outputStream&=&null;&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&sendTask&=&null;&&&&&&&&&}&&&&&}&&&&&&public&void&addMessage(BasicProtocol&data)&{&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&return;&&&&&&&&&}&&&&&&&&&&dataQueue.offer(data);&&&&&&&&&toNotifyAll(dataQueue);//有新增待发送数据,则唤醒发送线程&&&&&}&&&&&&public&Socket&getConnectdClient(String&clientID)&{&&&&&&&&&return&onLineClient.get(clientID);&&&&&}&&&&&&/**&&&&&&*&打印已经链接的客户端&&&&&&*/&&&&&public&static&void&printAllClient()&{&&&&&&&&&if&(onLineClient&==&null)&{&&&&&&&&&&&&&return;&&&&&&&&&}&&&&&&&&&Iterator&String&&inter&=&onLineClient.keySet().iterator();&&&&&&&&&while&(inter.hasNext())&{&&&&&&&&&&&&&System.out.println(&client:&&+&inter.next());&&&&&&&&&}&&&&&}&&&&&&public&void&toWaitAll(Object&o)&{&&&&&&&&&synchronized&(o)&{&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&o.wait();&&&&&&&&&&&&&}&catch&(InterruptedException&e)&{&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&}&&&&&&&&&}&&&&&}&&&&&&public&void&toNotifyAll(Object&obj)&{&&&&&&&&&synchronized&(obj)&{&&&&&&&&&&&&&obj.notifyAll();&&&&&&&&&}&&&&&}&&&&&&private&boolean&isConnected()&{&&&&&&&&&if&(socket.isClosed()&||&!socket.isConnected())&{&&&&&&&&&&&&&onLineClient.remove(userIP);&&&&&&&&&&&&&ServerResponseTask.this.stop();&&&&&&&&&&&&&System.out.println(&socket&closed...&);&&&&&&&&&&&&&return&false;&&&&&&&&&}&&&&&&&&&return&true;&&&&&}&&&&&&public&class&ReciveTask&extends&Thread&{&&&&&&&&&&private&DataInputStream&inputS&&&&&&&&&private&boolean&isC&&&&&&&&&&@Override&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&while&(!isCancle)&{&&&&&&&&&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&&&&&&&&&isCancle&=&true;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&BasicProtocol&clientData&=&SocketUtil.readFromStream(inputStream);&&&&&&&&&&&&&&&&&&if&(clientData&!=&null)&{&&&&&&&&&&&&&&&&&&&&&if&(clientData.getProtocolType()&==&0)&{&&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(&dtype:&&&+&((DataProtocol)&clientData).getDtype()&+&&,&pattion:&&&+&((DataProtocol)&clientData).getPattion()&+&&,&msgId:&&&+&((DataProtocol)&clientData).getMsgId()&+&&,&data:&&&+&((DataProtocol)&clientData).getData());&&&&&&&&&&&&&&&&&&&&&&&&&&DataAckProtocol&dataAck&=&new&DataAckProtocol();&&&&&&&&&&&&&&&&&&&&&&&&&dataAck.setUnused(&收到消息:&&+&((DataProtocol)&clientData).getData());&&&&&&&&&&&&&&&&&&&&&&&&&dataQueue.offer(dataAck);&&&&&&&&&&&&&&&&&&&&&&&&&toNotifyAll(dataQueue);&//唤醒发送线程&&&&&&&&&&&&&&&&&&&&&&&&&&tBack.targetIsOnline(userIP);&&&&&&&&&&&&&&&&&&&&&}&else&if&(clientData.getProtocolType()&==&2)&{&&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(&pingId:&&&+&((PingProtocol)&clientData).getPingId());&&&&&&&&&&&&&&&&&&&&&&&&&&PingAckProtocol&pingAck&=&new&PingAckProtocol();&&&&&&&&&&&&&&&&&&&&&&&&&pingAck.setUnused(&收到心跳&);&&&&&&&&&&&&&&&&&&&&&&&&&dataQueue.offer(pingAck);&&&&&&&&&&&&&&&&&&&&&&&&&toNotifyAll(dataQueue);&//唤醒发送线程&&&&&&&&&&&&&&&&&&&&&&&&&&tBack.targetIsOnline(userIP);&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&else&{&&&&&&&&&&&&&&&&&&&&&System.out.println(&client&is&offline...&);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&SocketUtil.closeInputStream(inputStream);&&&&&&&&&}&&&&&}&&&&&&public&class&SendTask&extends&Thread&{&&&&&&&&&&private&DataOutputStream&outputS&&&&&&&&&private&boolean&isC&&&&&&&&&&@Override&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&while&(!isCancle)&{&&&&&&&&&&&&&&&&&if&(!isConnected())&{&&&&&&&&&&&&&&&&&&&&&isCancle&=&true;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&BasicProtocol&procotol&=&dataQueue.poll();&&&&&&&&&&&&&&&&&if&(procotol&==&null)&{&&&&&&&&&&&&&&&&&&&&&toWaitAll(dataQueue);&&&&&&&&&&&&&&&&&}&else&if&(outputStream&!=&null)&{&&&&&&&&&&&&&&&&&&&&&synchronized&(outputStream)&{&&&&&&&&&&&&&&&&&&&&&&&&&SocketUtil.write2Stream(procotol,&outputStream);&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&SocketUtil.closeOutputStream(outputStream);&&&&&&&&&}&&&&&}&
其中涉及到的ResponseCallback接口如下:
/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&interface&ResponseCallback&{&&&&&&void&targetIsOffline(DataProtocol&reciveMsg);&&&&&&void&targetIsOnline(String&clientIp);&}&&
上述代码中处理了几种情况下的异常,比如,建立连接后,服务端停止运行,此时客户端的输入流还在阻塞状态,怎么保证客户端不抛出异常,这些处理可以结合SocketUtil类来看。
四、调用封装
import com.shandiangou.sdgprotocol.lib.protocol.DataP
import&com.shandiangou.sdgprotocol.lib.protocol.DataP&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&ConnectionClient&{&&&&&&private&boolean&isC&&&&&&private&ClientRequestTask&mClientRequestT&&&&&&public&ConnectionClient(RequestCallBack&requestCallBack)&{&&&&&&&&&mClientRequestTask&=&new&ClientRequestTask(requestCallBack);&&&&&&&&&new&Thread(mClientRequestTask).start();&&&&&}&&&&&&public&void&addNewRequest(DataProtocol&data)&{&&&&&&&&&if&(mClientRequestTask&!=&null&&&&!isClosed)&&&&&&&&&&&&&mClientRequestTask.addRequest(data);&&&&&}&&&&&&public&void&closeConnect()&{&&&&&&&&&isClosed&=&true;&&&&&&&&&mClientRequestTask.stop();&&&&&}&}&&
import&com.shandiangou.sdgprotocol.lib.protocol.DataP&&import&java.io.IOE&import&java.net.ServerS&import&java.net.S&import&java.util.concurrent.ExecutorS&import&java.util.concurrent.E&&/**&&*&Created&by&meishan&on&16/12/1.&&*/&public&class&ConnectionServer&{&&&&&&private&static&boolean&isStart&=&true;&&&&&private&static&ServerResponseTask&serverResponseT&&&&&&public&ConnectionServer()&{&&&&&&}&&&&&&public&static&void&main(String[]&args)&{&&&&&&&&&&ServerSocket&serverSocket&=&null;&&&&&&&&&ExecutorService&executorService&=&Executors.newCachedThreadPool();&&&&&&&&&try&{&&&&&&&&&&&&&serverSocket&=&new&ServerSocket(Config.PORT);&&&&&&&&&&&&&while&(isStart)&{&&&&&&&&&&&&&&&&&Socket&socket&=&serverSocket.accept();&&&&&&&&&&&&&&&&&serverResponseTask&=&new&ServerResponseTask(socket,&&&&&&&&&&&&&&&&&&&&&&&&&new&ResponseCallback()&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@Override&&&&&&&&&&&&&&&&&&&&&&&&&&&&&public&void&targetIsOffline(DataProtocol&reciveMsg)&{//&对方不在线&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&if&(reciveMsg&!=&null)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(reciveMsg.getData());&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@Override&&&&&&&&&&&&&&&&&&&&&&&&&&&&&public&void&targetIsOnline(String&clientIp)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(clientIp&+&&&is&onLine&);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&System.out.println(&-----------------------------------------&);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&&&&});&&&&&&&&&&&&&&&&&&if&(socket.isConnected())&{&&&&&&&&&&&&&&&&&&&&&executorService.execute(serverResponseTask);&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&&&&&&serverSocket.close();&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&}&finally&{&&&&&&&&&&&&&if&(serverSocket&!=&null)&{&&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&&isStart&=&false;&&&&&&&&&&&&&&&&&&&&&serverSocket.close();&&&&&&&&&&&&&&&&&&&&&if&(serverSocket&!=&null)&&&&&&&&&&&&&&&&&&&&&&&&&serverResponseTask.stop();&&&&&&&&&&&&&&&&&}&catch&(IOException&e)&{&&&&&&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&}&&&&&&&&&}&&&&&}&}&&
实现自定义协议的关键在于协议的拼装和解析,上述已给出了关键的代码,如果需要查看完整的代码以及demo,可以。
注意:先运行服务端demo的main函数,再查看本机的ip地址,然后修改客户端(android)代码中Config.java里面的ip地址,当然,要确保android手机和服务端在同一个局域里面,最后再打开客户端。【编辑推荐】【责任编辑: TEL:(010)】
大家都在看猜你喜欢
原创头条头条专题专题
24H热文一周话题本月最赞
讲师:2人学习过
讲师:4人学习过
讲师:1人学习过
精选博文论坛热帖下载排行
本书由浅入深、循序渐进地介绍了目前流行的基于Eclipse的优秀框架。全书共分14章,内容涵盖了Eclipse基础、ANT资源构造、数据库应用开发、W...
订阅51CTO邮刊}

我要回帖

更多关于 android连接服务器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信