Android(安卓)socket 工具类
16lz
2021-12-04
import java.io.ByteArrayOutputStream;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;/** * 通过NIO方式进行socket链接 * */public class MySocket_2 { /** 信道选择器 */ private Selector mSelector; /** 服务器通信的信道 */ private SocketChannel mChannel; /** 远端服务器ip地址 */ private String mRemoteIp; /** 远端服务器端口 */ private int mPort; /** 是否加载过的标识 */ private boolean mIsInit = false; /** 单键实例 */ private static MySocket_2 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 MySocket_2 instance() { if ( gTcp == null ) { gTcp = new MySocket_2(); } return gTcp; } private MySocket_2() { } /** * 链接远端地址 * @param remoteIp * @param port * @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); } //删除此SelectionKey mSelector.selectedKeys().remove(sk); } } } catch ( Exception e ) { e.printStackTrace(); } } } public interface TCPClientEventListener { /** * 多线程下接收到数据 * @param read * @return */ void recvMsg(ByteArrayOutputStream read); } /** * 链接线程 * */ private class MyConnectRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub try { //打开监听信道,并设置为非阻塞模式 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 { @Override public void run() { // TODO Auto-generated method stub revMsg(); } }}
更多相关文章
- Android(安卓)自定义BaseRecyclerView.Adapter
- Android(安卓)NDK r6 下载链接
- JNI基本数据类型转换
- Android(安卓)RSA 加密(分段加密)
- Pull To Refresh for Android
- android:uploaddatatoWeb
- ch020 Android(安卓)SQLite3(第一部分)
- Android:SQLiteOpenHelper
- ch020 Android(安卓)SQLite3(第一部分)