APP與終端裝置通訊經驗三(具體情形介紹)
一、過程還原: 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流建立通訊的過程。