2016年12月13日 星期二

PHP socket,client 端傳送、接收資料範例

假設 server 端和 client 端,約定傳輸資料的前 4 個 bytes 為緊接著要傳輸資料的長度。
所以,client 端傳送的資料為 => 4 bytes 的數字(資料本體長度) + 資料本體
同樣,client 端接收到資料為 => 4 bytes 的數字(資料本體長度) + 資料本體
//client 傳送:前4個bytes為要傳送的資料本體長度,後面緊接著資料本體
//server 回應:前4個bytes為回應的資料本體長度,後面緊接著資料本體
$data = "這是要傳送的資料本體";
//socket
$url = "http://example.com:8888";
$url_part = parse_url($url);
$host = $url_part["host"];
$address = gethostbyname($host);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket) {
    $err_msg = socket_strerror(socket_last_error($socket));
    socket_close($socket);
    throw new Exception("socket_create() failed:" . $err_msg);
}
$conn = socket_connect($socket, $address, $api_def->getSocketPort());
if (!$conn) {
    $err_msg = socket_strerror(socket_last_error($socket));
    socket_close($socket);
    throw new Exception("socket_connect() failed:" . $err_msg);
}

//傳送資料
$bin_body = pack("a*", $data);
//$bin_body = $data;
$body_len = strlen($bin_body);
$bin_head = pack("N", $body_len); //unsigned long (always 32 bit, big endian byte order)=>數字轉成一個 4 bytes 的big endian byte order
$bin_data = $bin_head . $bin_body; //4bytes資料本體長度 + 資料本體
$res = socket_write($socket, $bin_data, strlen($bin_data)); //返回成功寫入的bytes數 or false
if (!$res) {
    $err_msg = socket_strerror(socket_last_error($socket));
    socket_close($socket);
    throw new Exception("socket_write() failed:" . $err_msg);
}

//接收資料
$buf = "";
//先取得 4bytes 回應資料本體長度
if (false === ($bytes = socket_recv($socket, $buf, 4, MSG_WAITALL))) { //返回接收到的長度 or false。MSG_WAITALL:阻塞模式,若過程沒異常,一直等到讀到指定長度
    $err_msg = socket_strerror(socket_last_error($socket));
    socket_close($socket);
    throw new Exception("讀取資料長度失敗 socket_recv() failed:" . $err_msg);
}
$len_data = unpack("Nlen", $buf); //N:unsigned long (always 32 bit, big endian byte order)=>解析頭 4 個bytes

//再用取得的資料本體長度,抓回應資料本體
if (false === ($bytes = socket_recv($socket, $buf, $len_data["len"], MSG_WAITALL))) { //返回接收到的長度 or false。MSG_WAITALL:阻塞模式,若過程沒異常,一直等到讀到指定長度
    $err_msg = socket_strerror(socket_last_error($socket));
    socket_close($socket);
    throw new Exception("讀取回應資料本體失敗 socket_recv() failed:" . $err_msg);
}
socket_close($socket);
var_dump($buf);//回應的資料本體



參考:
PHP: 深入pack/unpack - 陈亦的个人页面 - 开源中国社区
[转帖]pack/unpack用法----心得筆記 - Perl Forum
PHP: pack - Manual
PHP: socket_recv - Manual
bytearray - String to byte array in php - Stack Overflow
java的int和byte數組的相互轉換_我們關註網
IO模式设置网络编程常见问题总结—IO模式设置,阻塞与非阻塞的比较,recv参数对性能的影响—O_NONBLOCK(open使用)、IPC_NOWAIT(msgrcv)、MSG_DONTWAIT(re - houlaizhe221的专栏 - 博客频道 - CSDN.NET


沒有留言:

張貼留言