详解Android 通过Socket 和服务器通讯(附demo)
作者:Likwo 发布时间:2023-05-03 01:08:22
标签: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();
}
}
@Override
public void run()
{
while (isStart)
{
// 锁发送list
synchronized (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 block
e.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;
}
@Override
public 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 block
e.printStackTrace();
}
}
readSocket();
// 如果连接服务器失败,服务器连接失败,sleep固定的时间,能联网,就不需要sleep
CLog.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 block
e.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 block
e.printStackTrace();
}
buffer.clear();
buffer = null;
try
{
// 为下一次读取作准备
sk.interestOps(SelectionKey.OP_READ);
// 删除正在处理的SelectionKey
selector.selectedKeys().remove(sk);
} catch (CancelledKeyException e)
{
e.printStackTrace();
}
}
}
}
// selector.close();
// TCPClient.instance().repareRead();
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.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 block
e.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 block
e.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();
}
}
}
}
如何使用
// 发送消息,失败或者成功的handler
SocketThreadManager.sharedInstance().sendMsg(str.getBytes(), handler);
代码下载:demo
来源:http://www.cnblogs.com/likwo/p/3641135.html


猜你喜欢
- 本文实例讲述了Android编程使WebView支持HTML5 Video全屏播放的解决方法。分享给大家供大家参考,具体如下:1)需要在An
- 这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下:最终效果图接下来一步一步的实现整
- watch机制Zookeeper watch是一种监听通知机制,可以随时监听一些数据的变化,从而实现数据的及时性。Zookeeper所有的读
- 前言哎呀,妈呀,又出异常了!俗话说:“代码虐我千百遍,我待代码如初恋”。小Alan最近一直在忙着工作,已经很久没有写写东西来加深自己的理解了
- 使用Aspose.Cells创建和读取Excel文件,供大家参考,具体内容如下1. 创建ExcelAspose.Cells.License
- Zookeeper和Eureka哪个更好?1、CAP理论一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求C:数据一致性
- 最近一段时间在研究OAuth2的使用,想整个单点登录,从网上找了很多demo都没有实施成功,也许是因为压根就不懂OAuth2的原理导致。有那
- 本文实例讲述了Java数据结构之链表、栈、队列、树的实现方法。分享给大家供大家参考,具体如下:最近无意中翻到一本书,闲来无事写几行代码,实现
- 本文实例讲述了C#使用GDI绘制直线的方法。分享给大家供大家参考。具体实现方法如下:Point p1=new Point(200,200);
- Java 最初版本只为常用的数据结构提供了很少的一组类:Vector、Stack、Hashtable、BitSet 与 Enumeratio
- 1.什么是PDF/UA文件PDF/UA,即Universally Accessible PDF,该格式的PDF文件是于2012年8月以ISO
- 将字母全部转换为大写或小写,在C#编程中是一个非常常见的功能。在开发过程中,经常需要验证用户登录,用户在输入用户名时可能不区分大小写,如果我
- 一、什么是JSONJSON: JavaScript Object Notation JS对象简谱,是一种类似于XML的语言。相比于XML,它
- 本文实例为大家分享了Android实现图像切换器的具体代码,供大家参考,具体内容如下java代码:private int[] imageId
- 前言这篇文章主要介绍Spring Boot的统一功能处理模块,也是AOP的实战环节。1.用户登录权限效验在学习Spring AOP之前,用户
- 1.介绍关机闹钟为Android中默认支持的功能,实现起来则需要满足一定的条件:自动开机、开机后响铃。对于自动开机来说,自动关机可以在应用层
- 1、xml代码:<?xml version="1.0" encoding="utf-8"?&g
- Android 中启动自己另一个程序的activity如何实现可以使用action,举例: 1. 比如建立activity4,我
- 有这样一道面试题:如何将SQLite数据库(dictionary.db文件)与apk文件一起发布? 答: 把这个文件
- 一直以为这个方法是java8的,今天才知道是是1.7的时候,然后翻了一下源码。这片文章中会总结一下与a.equals(b)的区别,然后对源码