1. 程式人生 > >APP與終端裝置通訊經驗三(具體情形介紹)

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流建立通訊的過程。