一、过程还原:
1.1.P2P模型
P2P(Peer to Peer)点对点通信模型,终端与手机是对等网络,由于终端IP和端口固定,最初由手机建立信令报文传输链路(常链路),之后码流报文传输链路(短链路)由终端根据手机端的相关的信令报文建立。
1.2.手机连接终端WIFI
终端释放一个固定名称热点(如,Wisdom_Wifi),IP为固定192.168.10.16,服务端口固定为9999
手机端打开WIFI,手动连接(Wisdom_Wifi)
手机WIFI连接成功,终端为其分配IP地址192.168.10.xx
1.3.信令链路建立
手机端主动 connect 192.168.10.16:9999,建立信令传输链路TCP0,用于手机和终端之间信令的接收和发送
手机端周期性(如,10秒)发送设备状态查询指令到终端,终端应答之(相当于长连接,维持通信链路)
备注: 到此,手机与终端已经正常连接,手机端能正常获取终端设备的各项状态,如 果不需要传输视频和文件,就一直这么运行。如果手机端需要请求视频和文件 传输,则进入1.3流程。
1.4.码流链路建立
说明:以请求实时视频流程来描述
用户在手机端操作请求第1通道实时视频
手机通过TCP0链路发送请求实时视频指令0x9101,该指令包含手机端处理视频流的IP、PORT
终端在TCP0链路上收到手机端实时视频传输请求,提取出消息中的IP、PORT,建立码流传输链路TCP1
TCP1码流传输链路建立成功,终端在链路TCP1上按RTP协议传输视频码流到手机
手机端接收解析TCP1链路上的码流协议包,提取出视频流,渲染出视频画面。
二、
第一步建立 一个连接socket,连接终端设备的iP和端口号
//实例化一个Socket对象
socket = new Socket();
//与对应的ip、端口进行连接,先要把桥建好
socket.connect(new InetSocketAddress(ip, port), 3000);
Log.i("socketThread","@@@connected");
if (socket.isConnected()) {
client = new SocketThreadTM.Client(socket,mMessageListener);
//打开对应的输入/输出流监听
client.start();
isStart=true;
connectedState.IsConnected(true);
}else{
isStart=false;
connectedState.IsConnected(false);
}
} catch (IOException e) {
Log.v("socketThread", "@@@E");
e.printStackTrace();
isStart=false;
connectedState.IsConnected(false);
}
定义的接口用于监听socket连接成功与否
public interface ConnectedState {
public void IsConnected(boolean conn);
}
当连接成功后,会打开输入输出流的线程
public class Client {
private ClientInputThreadTM in;
private ClientOutputThread out;
public Client(Socket socket, MessageListener mMessageListener) {
//用这个监听输入流线程来接收信息
in = new ClientInputThreadTM(socket);
in.setMessageListener(mMessageListener);
//以后就用这个监听输出流的线程来发送信息了
out = new ClientOutputThread(socket);
}
public void start() {
in.setStart(true);
out.setStart(true);
in.start();
out.start();
}
// 得到读消息线程
public ClientInputThreadTM getIn() {
return in;
}
// 得到写消息线程
public ClientOutputThread getOut() {
return out;
}
}
里面的构造方法里注册数据监听的方法
在这里启动了输入输出线程
看看输入输出线程对应实现细节
输入线程用于监听终端发给app的数据
public ClientInputThreadTM(Socket socket) {
this.socket = socket;
try {
ois = socket.getInputStream();
dis = new DataInputStream(new BufferedInputStream(ois));
} catch (IOException e) {
e.printStackTrace();
}
}
拿到socket的输入流ois,同时包装下DataInputStream
同时线程的run方法,
int result = dis.read(b);
msg=“9102”;
messageListener.Message(msg);
msg = “”;
写出for循环表明一直监听终端数据
当退出for循环监听也随之结束。
输出线程用于app主动给终端发数据
public ClientOutputThread(Socket socket) {
this.socket = socket;
try {
dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
// 这里处理跟服务器是一样的
public void sendMsg(byte[] msg) {
this.msg = msg;
synchronized (this) {
notifyAll();
}
}
@Override
public void run() {
try {
while (isStart) {
if (msg != null) {
Log.i(Tag,"sendmsg:"+ StringUtils.bytesToHexString(msg));
dos.write(msg);
dos.flush();
msg=null;
synchronized (this) {
wait();// 发送完消息后,线程进入等待状态
}
}
}
dos.close();// 循环结束后,关闭输出流和socket
if (socket != null)
socket.close();
} catch (InterruptedException e) {
e.printStackTrace();
messageListener.Message("outOline");
} catch (IOException e) {
e.printStackTrace();
messageListener.Message("outOline");
// }catch (ErrnoException e){
// e.printStackTrace();
// messageListener.Message("outOline");
//
}catch (Exception e){
e.printStackTrace();
messageListener.Message("outOline");
}
}
这里使用了Thread 的notify和wait的方式,所错了是object的。居然是所有类的方法,查阅资料
发现:
1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写;
2、notify方法用于唤醒线程。我们看到输出线程的发送方法就只调用了notifyall方法。应该是唤醒这个线程。
3、notify方法只是唤醒线程。还没有释放锁。当遇到wait方法才会释放锁。
总结:本片介绍了手机和终端通过socket流建立通信的过程。