最近在做视频流相关的项目,发现 网上很少这方面的demo,很多都是长篇大论的理论知识,研究的太深,不利于小白快速 上手。所以我提炼了自己项目中这一块的内容,打包个小 demo ,只要 拿去稍作修改基本就能跑了。
基本需求是:硬件设备端有wifi模块,WiFi模块AP模式作为服务端发送信号出来,APP作为客户端,连接wifi模块的热点,形成局域网环境,再通过Socket连接服务端进行通信。
本文不做深度剖析,只介绍 Demo 中的开发方式。
先看demo结构
很简单,就几个文件。
开发步骤:
一. 创建 Service ,用于管理Socket连接、发送、接收消息。
Service中,首先定义初始化和释放Socket的 方法,再写一个子线程用来调用Socket的初始化方法开始连接,并发送心跳数据包 。
/**
* 发送心跳包 每 3 秒发送一次
*/
private Handler mHandler = new Handler();
private Runnable heartBeatRunnable = new Runnable() {
@Override
public void run() {
if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) { //如果当前时间 - 上一次发送时间 >= 3 秒 再次发送心跳包
boolean isSuccess = sendMsg("心跳指令");// 发送心跳包内容,自行修改为自己的协议指令
if (!isSuccess) { //false 发送心跳不成功
Intent intent = new Intent(SOCKET_DISCONNECT);
sendBroadcast(intent); //发送断开广播 告诉界面
mHandler.removeCallbacks(heartBeatRunnable);
mReadThread.release();
releaseLastSocket(mSocket); //释放socket
isConnect=false;
new InitSocketThread().start(); //重连
} else {
Intent intent = new Intent(SOCKET_CONNECT);
sendBroadcast(intent);
}
}
mHandler.postDelayed(this, HEART_BEAT_RATE);
}
};
/**
* 初始化socket 并开始发送心跳包
* 由心跳包控制 socket 重连机制 : 心跳发送失败会重新调用这个方法不断重连,直到连接成功
*/
private void initSocket() {
Socket socket = null;
SocketAddress socketAddress = new InetSocketAddress(HOST ,PORT);
try {
socket = new Socket();
socket.connect(socketAddress,6000); // 6秒超时
if(socket.isConnected()){
isConnect=true;
}
} catch (IOException e) {
e.printStackTrace();
}
mSocket = new WeakReference<Socket>(socket);
mReadThread = new ReadThread(socket);
mReadThread.start();
mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 准备发送心跳包
}
/**
* 释放socket
* @param mSocket
*/
private void releaseLastSocket(WeakReference<Socket> mSocket) {
try {
if (null != mSocket) {
Socket sk = mSocket.get();
if (sk != null) {
if (!sk.isClosed()) {
sk.close();
}
sk = null;
mSocket = null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 开始连接socket的线程
*/
class InitSocketThread extends Thread {
@Override
public void run() {
super.run();
initSocket();
}
}
接着再定义发送数据的方法和接收数据的子线程。
/**
* 发送消息
* 假设通信协议为 :包头 + 内容长度 + 内容
* @param msg 内容
* @return
*/
public boolean sendMsg(String msg) {
if (null == mSocket || null == mSocket.get()) {
return false;
}
Socket soc = mSocket.get();
try {
if (!soc.isClosed() && !soc.isOutputShutdown()) {
String message = msg;
byte[] b_head = MSG_HEAD.getBytes(); //包头
byte[] b_length = StringUtil.toHH(message.getBytes().length); //内容长度
byte[] b_msg = StringUtil.arrayCopy(b_head, b_length, message.getBytes()); //拼接组合成完整数据包: 包头 + 内容长度 + 内容
OutputStream os = soc.getOutputStream();
os.write(b_msg); //写入完整数据包
os.flush();
sendTime = System.currentTimeMillis(); // 每次发送成功数据,就改一下最后成功发送的时间,节省心跳间隔时间
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG, "发送成功的时间:" + dateformat.format(sendTime) + " ---msg=" + message + ",长度=" + message.getBytes().length);
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 接收消息
* 假设数据包格式为 :包头 4byte + 内容长度 4byte + 内容(裸流)
* 需要修改为自身项目中的协议 数据格式
*/
public class ReadThread extends Thread {
private WeakReference<Socket> mWeakSocket;
private boolean isStart = true;
public static final int PACKET_HEAD_LENGTH = 4;//包头长度
public static final int PACKET_CONTENT_LENGTH = 4;//内容长度
public ReadThread(Socket socket) {
mWeakSocket = new WeakReference<Socket>(socket);
}
public void release() {
isStart = false;
releaseLastSocket(mW