最近在研究android的推送,一开始准备自己搭建推送服务器,并在android机上建立一个socket长连接,由于之前一直是用c++,在网上搜索一些资料后,临时写了一个基于NIO模式的客户端socket链接类,测试后能使用,当然还有很多问题,没有去修改了,因为最后发现,现在国内已经有现成的第三方推送平台,基于项目的开发时间有限,准备使用第三方推送平台,废话不多说,直接贴代码,说明都在代码中.

package cn.kidstone.cartoon.net;import java.io.ByteArrayOutputStream;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;/** * 通过NIO方式进行socket链接 * @author He Zhongqiu * */public class TCPClient {/** 信道选择器 */private Selector mSelector;/** 服务器通信的信道 */private SocketChannel mChannel;/** 远端服务器ip地址 */private String mRemoteIp;/** 远端服务器端口 */private int mPort;/** 是否加载过的标识 */private boolean mIsInit = false;/** 单键实例 */private static TCPClient gTcp;private TCPClientEventListener mEventListener;/** 默认链接超时时间 */public static final int TIME_OUT = 10000;/** 读取buff的大小 */public static final int READ_BUFF_SIZE = 1024;/** 消息流的格式 */public static final String BUFF_FORMAT = "utf-8";public static synchronized TCPClient instance() {if ( gTcp == null ) {gTcp = new TCPClient();}return gTcp;}private TCPClient() {}/** * 链接远端地址 * @param remoteIp * @param port * @param TCPClientEventListener * @return */public void connect( String remoteIp, int port, TCPClientEventListener tcel ) {mRemoteIp = remoteIp;mPort = port;mEventListener = tcel;connect();}/** * 链接远端地址 * @param remoteIp * @param port * @return */public void connect( String remoteIp, int port ) {connect(remoteIp,port,null);}private void connect() {//需要在子线程下进行链接MyConnectRunnable connect = new MyConnectRunnable();new Thread(connect).start();}/** * 发送字符 * @param msg * @return */public boolean sendMsg(String msg) {boolean bRes = false;try {bRes = sendMsg(msg.getBytes(BUFF_FORMAT));} catch ( Exception e ) {e.printStackTrace();}return bRes;}/** * 发送数据,此函数需要在独立的子线程中完成,可以考虑做一个发送队列 * 自己开一个子线程对该队列进行处理,就好像connect一样 * @param bt * @return */public boolean sendMsg( byte[] bt ) {boolean bRes = false;if ( !mIsInit ) {return bRes;}try {ByteBuffer buf = ByteBuffer.wrap(bt);int nCount = mChannel.write(buf);if ( nCount > 0 ) {bRes = true;}} catch ( Exception e ) {e.printStackTrace();}return bRes;}public Selector getSelector() {return mSelector;}/** * 是否链接着 * @return */public boolean isConnect() {if ( !mIsInit ) {return false;}return mChannel.isConnected();}/** * 关闭链接 */public void close() {mIsInit = false;mRemoteIp = null;mPort = 0;try {if ( mSelector != null ) {mSelector.close();}if ( mChannel != null ) {mChannel.close();}} catch ( Exception e ) {e.printStackTrace();}}/** * 重连 * @return */public void reConnect() {close();connect();}/** * 发送一个测试数据到服务器,检测服务器是否关闭 * @return */public boolean canConnectServer() {boolean bRes = false;if ( !isConnect() ) {return bRes;}try {mChannel.socket().sendUrgentData(0xff);} catch ( Exception e ) {e.printStackTrace();}return bRes;}/** * 每次读完数据后,需要重新注册selector读取数据 * @return */private synchronized boolean repareRead() {boolean bRes = false;try {//打开并注册选择器到信道mSelector = Selector.open();if ( mSelector != null ) {mChannel.register(mSelector, SelectionKey.OP_READ);bRes = true;}} catch ( Exception e ) {e.printStackTrace();} return bRes;}public void revMsg() {if ( mSelector == null ) {return;}boolean bres = true;while ( mIsInit ) {if ( !isConnect() ) {bres = false;}if ( !bres ) {try {Thread.sleep(100);} catch ( Exception e ) {e.printStackTrace();}continue;}try {//有数据就一直接收while (mIsInit && mSelector.select() > 0) {for ( SelectionKey sk : mSelector.selectedKeys() ) {//如果有可读数据if ( sk.isReadable() ) {//使用NIO读取channel中的数据SocketChannel sc = (SocketChannel)sk.channel();//读取缓存ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFF_SIZE);//实际的读取流ByteArrayOutputStream read = new ByteArrayOutputStream();int nRead = 0;int nLen = 0;//单个读取流byte[] bytes;//读完为止while ( (nRead = sc.read(readBuffer) ) > 0 ) {//整理readBuffer.flip();bytes = new byte[nRead];nLen += nRead;//将读取的数据拷贝到字节流中readBuffer.get(bytes);//将字节流添加到实际读取流中read.write(bytes);///////////////////////////////////////@ 需要增加一个解析器,对数据流进行解析/////////////////////////////////////readBuffer.clear();}if ( nLen > 0 ) {if ( mEventListener != null ) {mEventListener.recvMsg(read);} else {String info = new String(read.toString(BUFF_FORMAT));System.out.println("rev:"+info);}}//为下一次读取做准备sk.interestOps(SelectionKey.OP_READ);}//删除此SelectionKeymSelector.selectedKeys().remove(sk);}}} catch ( Exception e ) {e.printStackTrace();}}}public interface TCPClientEventListener {/** * 多线程下接收到数据 * @param read * @return  */void recvMsg(ByteArrayOutputStream read);}/** * 链接线程 * @author HeZhongqiu * */private class MyConnectRunnable implements Runnable {@Overridepublic void run() {// TODO Auto-generated method stubtry {//打开监听信道,并设置为非阻塞模式SocketAddress ad = new InetSocketAddress(mRemoteIp, mPort);mChannel = SocketChannel.open( ad );if ( mChannel != null ) {mChannel.socket().setTcpNoDelay(false);mChannel.socket().setKeepAlive(true);//设置超时时间mChannel.socket().setSoTimeout(TIME_OUT);mChannel.configureBlocking(false);mIsInit = repareRead();//创建读线程RevMsgRunnable rev = new RevMsgRunnable();new Thread(rev).start();}} catch ( Exception e ) {e.printStackTrace();} finally {if ( !mIsInit ) {close();}}}}private class RevMsgRunnable implements Runnable {@Overridepublic void run() {// TODO Auto-generated method stubrevMsg();}}}

其中,还需要完善的是:

1.发送消息,需要自己再创建一个消息队列,在另外的一个线程处理,如果在主线程下,会抛出异常,

2.自定义消息解析器,解析器的基类我就没定义了,可根据实际需求进行添加,根据自己的消息结构体,对消息数据流进行解析

3.链接断开后,收消息的轮询处理需要退出

更多相关文章

  1. android读取apk中已经存在的数据库信息
  2. 第十一章、Android的线程和线程池
  3. android中自定义数据类型在两个activity间的传递
  4. Android菜鸟的成长笔记(22)——Android进程间传递复杂数据(AIDL)
  5. [android盈利模式探索]我也分享一下我Android的收入数据
  6. Android中的线程模型,
  7. [转载]Android中的线程模型
  8. android异步线程利用Handler将消息发送至UI线程

随机推荐

  1. 婚恋网后台管理路由
  2. easywechat实现微信接入并不同消息回复+
  3. easywechat的使用
  4. 微信公众号接入与发送接收消息
  5. 婚恋网后台的资源路由 普通控制器和资源
  6. PDO预处理与会话控制
  7. vue_1
  8. 意派Epub360丨中秋节合成海报H5案例赏析,
  9. 定义网站相关路由内容
  10. 豆瓣" 饭圈 " 整治,如何采集分析评论