程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 使用 PHP 消息隊列實現 Android 與 Web 通信

使用 PHP 消息隊列實現 Android 與 Web 通信

編輯:關於PHP編程

使用 PHP 消息隊列實現 Android 與 Web 通信


需求描述很簡單:Android 發送數據到 Web 網頁上。

系統: Ubuntu 14.04 + apache2 + php5 + Android 4.4

思路是 socket + 消息隊列 + 服務器發送事件,下面的講解步驟為 Android 端,服務器端,前端。重點是在於 PHP 進程間通信。

Android 端比較直接,就是一個 socket 程序。需要注意的是,如果直接在活動主線程裡面創建 socket 會報一個 android.os.NetworkOnMainThreadException, 因此最好的方法是開個子線程來創建 socket,代碼如下

    
private Socket socket = null;
private boolean connected = false;
private PrintWriter out;
private BufferedReader br;

private void buildSocket(){
        if(socket != null)
            return;
        try {
            socket = new Socket("223.3.68.101",54311); //IP地址與端口號
            out = new PrintWriter(
                    new BufferedWriter(
                            new OutputStreamWriter(
                                    socket.getOutputStream())), true);
            br = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        connected = true;
  }

然後是發送消息

 

    public void sendMsg(String data){
        if(!connected || socket == null) return;
        synchronized (socket) {
            try {
                out.println(data);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

 

完成後還需要關閉 socket

 

    private void closeSocket(){
        if(  socket == null) return;
        try {
            socket.close();
            out.close();
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        socket = null;
        connected = false;
    }

注意這些方法都不要在主線程執行。

 

 

下面是服務器 PHP 端。

首先要運行一個進程來接收信息。

 

function buildSocket($msg_queue){

	$address = "223.3.68.101";
	$port = 54321; 

	if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false){
		echo "socket_create() failed:" . socket_strerror(socket_last_error()) . "/n";
		die;
	}
	echo "socket create\n";

	if (socket_set_block($sock) == false){
	 echo "socket_set_block() faild:" . socket_strerror(socket_last_error()) . "\n";
	 die;
	}

	if (socket_bind($sock, $address, $port) == false){
		echo "socket_bind() failed:" . socket_strerror(socket_last_error()) . "\n";
		die;
	}

	if (socket_listen($sock, 4) == false){
		echo "socket_listen() failed:" . socket_strerror(socket_last_error()) . "\n";
		die;
	}
	echo "listening\n";
 
	if (($msgsock = socket_accept($sock)) === false) {  
		echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error()) . "\n";  
		die;  
	}  

	$buf = socket_read($msgsock, 8192);  
	while(true){
		if(strlen($buf) > 1)
			handleData($buf,$msg_queue); //見後文
		$buf = socket_read($msgsock, 8192);  
		
		//看情況 break 掉
	}
	socket_close($msgsock);  
	
}

也比較簡單。這個進程是獨立運行的,那麼打開網頁請求數據,需要從另一段腳本接入,下面就需要用到進程間通信,我選擇消息隊列,也就是上面的 $msg_queue 變量。

 

腳本主程序這麼寫。

 

$msg_queue_key = ftok(__FILE__,'socket'); //__FILE__ 指當前文件名字
$msg_queue = msg_get_queue($msg_queue_key); //獲取已有的或者新建一個消息隊列
buildSocket($msg_queue);
socket_close($sock);
其中的 ftok() 函數就是生成一個隊列的 key,以區分。

 

那麼handleData() 的任務就是把收到的消息放到隊列裡面去

 

function handleData($dataStr, $msg_queue){
	msg_send($msg_queue,1,$dataStr);
}
Socket 進程腳本骨架

 

 

這樣一來,其他進程就可以通過 key 找到這個隊列,從裡面讀取消息了。使用這樣可讀

 

function redFromQueue($message_queue){
	msg_receive($message_queue, 0, $message_type, 1024, $message, true, MSG_IPC_NOWAIT);
	echo $message."\n\n";
}

$msg_queue_key = ftok("socket.php", 'socket'); //第一個變量為上方socket進程的文件名。
$msg_queue = msg_get_queue($msg_queue_key, 0666);

while(true){
	$msg_queue_status = msg_stat_queue($msg_queue); //獲取消息隊列的狀態
	if($msg_queue_status["msg_qnum"] == 0) //如果此時消息隊列為空,那麼跳過,否則會讀取空行。
		continue;
	redFromQueue($msg_queue);
}


現在就差最後一步,如何主動把數據發往前端?這要用到 HTML5 的新特性:服務器發送事件(要使用較新的非 IE 浏覽器,具體查看這裡)。直接看JS代碼

 

 

var source = new EventSource("php/getData.php"); //Web 服務器路徑
source.onmessage = function(event){ //消息事件回調
	var resData = event.data;	
	document.getElementById("res").innerHTML=resData;
};

那麼這個 getData.php 就是上面那個從消息隊列獲取數據的腳本。只是為了讓它被識別為服務器事件,需要加一點格式上的說明,具體如下。

 

 

 

下面就可以開始運行,首先運行服務器

php socket.php

打印了 listening 就可以使用 Android 設備連接了。

然後再用 Web 上 JS 請求 getData 腳本,請求後前台可以不斷地獲得新的數據。需要注意的是消息隊列可能會阻塞(消息量達到上限),再有就是 JS 本身消息機制的限制,因此丟失,延遲等現象頻發。

Web 通信的老問題就是穩定性。以前老是怨恨 Web QQ 掉包,其實整個 Web 革命尚未成功。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved