Android 通过Socket 和服务器通讯
Android 通过Socket 和服务器通讯,是一种比较常用的通讯方式,时间比较紧,说下大致的思路,希望能帮到使用socket 进行通信的人
(1)开启一个线程发送消息 SocketOutputThread
消息是放在队列里的,当有消息后,进入队列,线程唤醒,发送消息,并反馈发送是否成功的回调
(2)开启一个线程接受服务器消息 SocketInputThread
为了防止一直收数据,浪费电池的电,采用NIO的方式读socket的数据,这个是本文的关键
(3)开启一个线程,做心跳,防止socket连接终断 , SocketHeartThread
(4)构建 SocketThreadManager对以上三个thread进行管理
(5)构建 TCPClient 发送socket消息
在NIO的方式实现TCP,特别是在接收服务器的数据,不用写个线程定时去读了。
DEMO 截图
主要代码如下,详细代码在附件里。
SocketOutPutThread 类
package com.example.socketblockdemo;import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;import android.os.Bundle;
import android.os.Handler;
import android.os.Message;/*** 客户端写消息线程* * @author way* */
public class SocketOutputThread extends Thread
{private boolean isStart = true;private static String tag = "socketOutputThread";private List<MsgEntity> sendMsgList;public SocketOutputThread( ){sendMsgList = new CopyOnWriteArrayList<MsgEntity>();}public void setStart(boolean isStart){this.isStart = isStart;synchronized (this){notify();}}// 使用socket发送消息public boolean sendMsg(byte[] msg) throws Exception{if (msg == null){CLog.e(tag, "sendMsg is null");return false;}try{TCPClient.instance().sendMsg(msg);} catch (Exception e){throw (e);}return true;}// 使用socket发送消息public void addMsgToSendList(MsgEntity msg) {synchronized (this){this.sendMsgList.add(msg);notify();}}@Overridepublic void run(){while (isStart){// 锁发送listsynchronized (sendMsgList){// 发送消息for (MsgEntity msg : sendMsgList){Handler handler = msg.getHandler();try{sendMsg(msg.getBytes());sendMsgList.remove(msg);// 成功消息,通过hander回传if (handler != null){Message message = new Message();message.obj = msg.getBytes();message.what =1;handler.sendMessage(message);// handler.sendEmptyMessage(1);}} catch (Exception e){e.printStackTrace();CLog.e(tag, e.toString());// 错误消息,通过hander回传if (handler != null){Message message = new Message();message.obj = msg.getBytes();message.what = 0;;handler.sendMessage(message);}}}}synchronized (this){try{wait();} catch (InterruptedException e){// TODO Auto-generated catch blocke.printStackTrace();}// 发送完消息后,线程进入等待状态}}}
}
SocketInputThread
package com.example.socketblockdemo;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;import android.content.Intent;
import android.text.TextUtils;/*** 客户端读消息线程* * @author way* */
public class SocketInputThread extends Thread
{private boolean isStart = true;private static String tag = "socket";// private MessageListener messageListener;// 消息监听接口对象public SocketInputThread(){}public void setStart(boolean isStart){this.isStart = isStart;}@Overridepublic void run(){while (isStart){// 手机能联网,读socket数据if (NetManager.instance().isNetworkConnected()){if (!TCPClient.instance().isConnect()){CLog.e(tag, "TCPClient connet server is fail read thread sleep second" +Const.SOCKET_SLEEP_SECOND );try{sleep(Const.SOCKET_SLEEP_SECOND * 1000);} catch (InterruptedException e){// TODO Auto-generated catch blocke.printStackTrace();}}readSocket();// 如果连接服务器失败,服务器连接失败,sleep固定的时间,能联网,就不需要sleepCLog.e("socket","TCPClient.instance().isConnect() " + TCPClient.instance().isConnect() );}}}public void readSocket(){Selector selector = TCPClient.instance().getSelector();if (selector == null){return;}try{// 如果没有数据过来,一直柱塞while (selector.select() > 0){for (SelectionKey sk : selector.selectedKeys()){// 如果该SelectionKey对应的Channel中有可读的数据if (sk.isReadable()){// 使用NIO读取Channel中的数据SocketChannel sc = (SocketChannel) sk.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);try{sc.read(buffer);} catch (IOException e){// TODO Auto-generated catch blocke.printStackTrace();// continue;}buffer.flip();String receivedString = "";// 打印收到的数据try{receivedString = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();CLog.e(tag, receivedString);Intent i = new Intent(Const.BC);i.putExtra("response", receivedString);MainActivity.s_context.sendBroadcast(i );} catch (CharacterCodingException e){// TODO Auto-generated catch blocke.printStackTrace();}buffer.clear();buffer = null;try{// 为下一次读取作准备sk.interestOps(SelectionKey.OP_READ);// 删除正在处理的SelectionKeyselector.selectedKeys().remove(sk);} catch (CancelledKeyException e){e.printStackTrace();}}}}// selector.close();// TCPClient.instance().repareRead();} catch (IOException e1){// TODO Auto-generated catch blocke1.printStackTrace();} catch (ClosedSelectorException e2){}}}
SocketHeartHread 心跳类
package com.example.socketblockdemo;import java.io.IOException;import android.text.TextUtils;class SocketHeartThread extends Thread
{boolean isStop = false;boolean mIsConnectSocketSuccess = false;static SocketHeartThread s_instance;private TCPClient mTcpClient = null;static final String tag = "SocketHeartThread";public static synchronized SocketHeartThread instance(){if (s_instance == null){s_instance = new SocketHeartThread();}return s_instance;}public SocketHeartThread(){TCPClient.instance();// 连接服务器// mIsConnectSocketSuccess = connect();}public void stopThread(){isStop = true;}/*** 连接socket到服务器, 并发送初始化的Socket信息* * @return*/private boolean reConnect(){return TCPClient.instance().reConnect();}public void run(){isStop = false;while (!isStop){// 发送一个心跳包看服务器是否正常boolean canConnectToServer = TCPClient.instance().canConnectToServer();if(canConnectToServer == false){reConnect();}try{Thread.sleep(Const.SOCKET_HEART_SECOND * 1000);} catch (InterruptedException e){e.printStackTrace();}}}
}
线程管理类
package com.example.socketblockdemo;import android.os.Handler;
import android.text.TextUtils;public class SocketThreadManager
{private static SocketThreadManager s_SocketManager = null;private SocketInputThread mInputThread = null;private SocketOutputThread mOutThread = null;private SocketHeartThread mHeartThread = null;// 获取单例public static SocketThreadManager sharedInstance(){if (s_SocketManager == null){s_SocketManager = new SocketThreadManager();s_SocketManager.startThreads();}return s_SocketManager;}// 单例,不允许在外部构建对象private SocketThreadManager(){mHeartThread = new SocketHeartThread();mInputThread = new SocketInputThread();mOutThread = new SocketOutputThread();}/*** 启动线程*/private void startThreads(){mHeartThread.start();mInputThread.start();mInputThread.setStart(true);mOutThread.start();mInputThread.setStart(true);// mDnsthread.start();}/*** stop线程*/public void stopThreads(){mHeartThread.stopThread();mInputThread.setStart(false);mOutThread.setStart(false);}public static void releaseInstance(){if (s_SocketManager != null){s_SocketManager.stopThreads();s_SocketManager = null;}}public void sendMsg(byte [] buffer, Handler handler){MsgEntity entity = new MsgEntity(buffer, handler);mOutThread.addMsgToSendList(entity);}}
TCPClient ,采用NIO的方式构建
package com.example.socketblockdemo;import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;/*** NIO TCP 客户端* */
public class TCPClient
{// 信道选择器private Selector selector;// 与服务器通信的信道SocketChannel socketChannel;// 要连接的服务器Ip地址private String hostIp;// 要连接的远程服务器在监听的端口private int hostListenningPort;private static TCPClient s_Tcp = null;public boolean isInitialized = false;public static synchronized TCPClient instance(){if (s_Tcp == null){s_Tcp = new TCPClient(Const.SOCKET_SERVER,Const.SOCKET_PORT);}return s_Tcp;}/*** 构造函数* * @param HostIp* @param HostListenningPort* @throws IOException*/public TCPClient(String HostIp, int HostListenningPort){this.hostIp = HostIp;this.hostListenningPort = HostListenningPort;try{initialize();this.isInitialized = true;} catch (IOException e){this.isInitialized = false;// TODO Auto-generated catch blocke.printStackTrace();} catch (Exception e){this.isInitialized = false;e.printStackTrace();}}/*** 初始化* * @throws IOException*/public void initialize() throws IOException{boolean done = false;try{// 打开监听信道并设置为非阻塞模式socketChannel = SocketChannel.open(new InetSocketAddress(hostIp,hostListenningPort));if (socketChannel != null){socketChannel.socket().setTcpNoDelay(false);socketChannel.socket().setKeepAlive(true);// 设置 读socket的timeout时间socketChannel.socket().setSoTimeout(Const.SOCKET_READ_TIMOUT);socketChannel.configureBlocking(false);// 打开并注册选择器到信道selector = Selector.open();if (selector != null){socketChannel.register(selector, SelectionKey.OP_READ);done = true;}}} finally{if (!done && selector != null){selector.close();}if (!done){socketChannel.close();}}}static void blockUntil(SelectionKey key, long timeout) throws IOException{int nkeys = 0;if (timeout > 0){nkeys = key.selector().select(timeout);} else if (timeout == 0){nkeys = key.selector().selectNow();}if (nkeys == 0){throw new SocketTimeoutException();}}/*** 发送字符串到服务器* * @param message* @throws IOException*/public void sendMsg(String message) throws IOException{ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("utf-8"));if (socketChannel == null){throw new IOException();}socketChannel.write(writeBuffer);}/*** 发送数据* * @param bytes* @throws IOException*/public void sendMsg(byte[] bytes) throws IOException{ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);if (socketChannel == null){throw new IOException();}socketChannel.write(writeBuffer);}/*** * @return*/public synchronized Selector getSelector(){return this.selector;}/*** Socket连接是否是正常的* * @return*/public boolean isConnect(){boolean isConnect = false;if (this.isInitialized){isConnect = this.socketChannel.isConnected();}return isConnect;}/*** 关闭socket 重新连接* * @return*/public boolean reConnect(){closeTCPSocket();try{initialize();isInitialized = true;} catch (IOException e){isInitialized = false;e.printStackTrace();}catch (Exception e){isInitialized = false;e.printStackTrace();}return isInitialized;}/*** 服务器是否关闭,通过发送一个socket信息* * @return*/public boolean canConnectToServer(){try{if (socketChannel != null){socketChannel.socket().sendUrgentData(0xff);}} catch (IOException e){// TODO Auto-generated catch blocke.printStackTrace();return false;}catch (Exception e){e.printStackTrace();return false;}return true;}/*** 关闭socket*/public void closeTCPSocket(){try{if (socketChannel != null){socketChannel.close();}} catch (IOException e){}try{if (selector != null){selector.close();}} catch (IOException e){}}/*** 每次读完数据后,需要重新注册selector,读取数据*/public synchronized void repareRead(){if (socketChannel != null){try{selector = Selector.open();socketChannel.register(selector, SelectionKey.OP_READ);} catch (ClosedChannelException e){e.printStackTrace();} catch (IOException e){e.printStackTrace();}}}
}
如何使用
// 发送消息,失败或者成功的handlerSocketThreadManager.sharedInstance().sendMsg(str.getBytes(), handler);
代码下载 http://files.cnblogs.com/likwo/SocketBlockDemo.zip
Android 通过Socket 和服务器通讯相关推荐
- android socket 代理服务器,Android 使用Socket进行服务器通信
·使用Socket进行简单服务器通信 简单服务器端: public class ServerThread implements Runnable{ Socket s=null; BufferedRea ...
- Android客户端与PC服务器通过socket进行交互实例
一直以来对Android socket通信都很模糊,今天终于研究了一个网上的例子,自己又修改了下,算是对Android socket通信有点了解了. 下面是具体的代码,说明都在注释中了.需要注意的是, ...
- unity android服务器端,【深圳Unity3D培训】 Android客户端与PC服务器实现Socket通信
[深圳Unity3D培训] Android客户端与PC服务器实现Socket通信 Android终端连续扫描AP信息并发送给服务器端的完成.起首基于TCP协定在Android终端和PC两头之间构成收集 ...
- python安卓开发实例_python服务器与android客户端socket通信实例
本文实例讲述了python服务器与android客户端socket通信的方法.分享给大家供大家参考.具体实现方法如下: 首先,服务器端使用python完成,下面为python代码: #server.p ...
- android 手机 与 python服务器_python服务器与android客户端socket通信实例
本文实例讲述了python服务器与android客户端socket通信的方法.分享给大家供大家参考.具体实现方法如下: 首先,服务器端使用python完成,下面为python代码: #server.p ...
- Android客户端与PC服务器实现Socket通信
Android终端持续扫描AP信息并发送给服务器端的实现.首先基于TCP协议在Android终端和PC两端之间形成网络虚拟链路.使用ServerSocket创建TCP服务器端,然后在Android客户 ...
- 远程PLC监控调试,PLC通用中转服务器,多客户端tcp中转 服务器源代码,socket多线程并发通讯
远程PLC监控调试,PLC通用中转服务器,多客户端tcp中转 服务器源代码,socket多线程并发通讯, 对接多路plc串口WIFI模块实现远程调试程序.支持各种串口服务器以及tcp以太网转发器硬件.
- 远程PLC监控调试,PLC通用中转服务器,多客户端tcp中转服务器源代码,socket多线程并发通讯,对接多路plc串口WIFI模块实现远程调试程序
远程PLC监控调试,PLC通用中转服务器,多客户端tcp中转服务器源代码,socket多线程并发通讯,对接多路plc串口WIFI模块实现远程调试程序. 支持各种串口服务器以及tcp以太网转发器硬件. ...
- Android客户端与本地服务器Socket通信
现代生活中,我们的移动设备离不开网络,我们的APP也经常需要连接到网络,所以现在,在这里完成一个简单的Socket通信程序,实现Android客户端与本地的服务器通信. 关于网络通信的知识,互 ...
最新文章
- CSS选择器和参考手册
- 基于脑电图的情绪识别BCI应用于DOC患者
- 170万奖金扶持 华为云究竟在下一盘怎样的棋?
- java+++多数据源配置,Spring Cloud + Mybatis 多数据源配置
- java ab性能测试,服务器JAVA性能测试——SPECjbb2005
- php 随机数据库,PHP实现随机一句功能
- SWMM面板基础认识
- Excel单元格引用方式
- 红米6.0系统如何无root激活xposed框架的教程
- GB/T28181之国标编码一览表,需要自取
- 【Web前端】一文带你吃透HTML(完整篇)
- 洛谷P4568 [JLOI2011] 飞行路线 题解
- 教你如何上传代码到GitHub
- k8s之HPA(Pod水平自动伸缩)
- 微信小程序使用 iconfont 彩色图标(mini-program-iconfont-cli)
- 什么高大填空四个字动人_照样子填空填四字成语什么什么什么地想
- 2、软件测试生命周期以及流程
- 丘比特 脱单攻略之女神倒追
- 创建koa2项目步骤
- 【软考软件评测师】2017年下案例分析历年真题