通信需要服務端和客戶端組成:
服務端:使用php初始化socket然後綁定一個端口,對端口進行監聽。調用accept阻塞,等待客戶端連接。
客戶端:客戶端初始化一個socket,然後連接服務器,如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束。
客戶端—服務端是可以彼此交互的應用程序。客戶端和服務端之間的交互需要連接。Socket編程負責的就是爲應用程序之間建立可進行交互的連接。
Socket連接過程
根據連接啓動的方式以及本地套接字要連接的目標,套接字之間的連接過程可以分爲三個步驟:服務器監聽,客戶端請求,連接確認。
(1)服務器監聽:是服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態。
(2)客戶端請求:是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然後就向服務器端套接字提出連接請求。
(3)連接確認:是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,連接就建立好了。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
socket原理可以參考下面的流程圖:
下面通過一個服務端--客戶端的代碼實例來簡單實現一下socket通信整個過程
1. 其服務端代碼:
<?php
set_time_limit(0); //限制執行時間 0爲不限制
$ip = '127.0.0.1';
$port = 8001;//端口
/**
socket通信整個過程
socket_create //創建一個套接字
socket_bind //給套接字綁定 ip 和端口
socket_listen //監聽套接字上的連接
socket_accept //接受一個socket連接
socket_read //接收客戶端 發送的數據
socket_write //將數據寫到 socket 緩存 向客戶端發送
socket_close //關閉套接字資源
*/
if(($sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) < 0) {
echo "socket_create() 失敗的原因是:".socket_strerror($sock)."\n";
}
if(($ret = socket_bind($sock,$ip,$port)) < 0) {
echo "socket_bind() 失敗的原因是:".socket_strerror($ret)."\n";
}
if(($ret = socket_listen($sock,4)) < 0) {
echo "socket_listen() 失敗的原因是:".socket_strerror($ret)."\n";
}
$count = 0;
do {
if (($msgsock = socket_accept($sock)) < 0) {
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
break;
} else {
//發到客戶端
$msg ="測試成功!\n";
socket_write($msgsock, $msg, strlen($msg));
echo "測試成功了啊\n";
$buf = socket_read($msgsock,8192);
$talkback = "收到的信息:$buf\n";
echo $talkback;
if(++$count >= 5){
break;
};
}
//echo $buf;
socket_close($msgsock);
} while (true);
socket_close($sock);
?>
運行php 文件,運行後 ,應該看不見結果 , 可以使用 netstat -a 查看 8001 端口是否被佔用。參見下圖。
<?php
header("content-type:text/html;charset=utf-8");
error_reporting(E_ALL);
set_time_limit(0);
echo "socket通信客戶端\n";
$port = 8001;//端口
$ip = "127.0.0.1";//ip
/***
socket連接整個過程
socket_create //建立一個socket 連接
socket_connect // 開始一個socket連接 連接服務端
socket_write //將數據寫入緩存 向服務端發送
socket_read// 讀取服務端的結果
socket_close // 關閉套接字資源
**/
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket < 0) {
echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";
}else {
echo "OK.<br/>\n";
}
echo "試圖連接 '$ip' 端口 '$port'...\n";
$result = socket_connect($socket, $ip, $port);
if ($result < 0) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
}else {
echo "連接OK<br/>\n";
}
$in = '<xml>
<TX>
<REQUEST_SN>請求序列碼</REQUEST_SN>
<CUST_ID>客戶號</CUST_ID>
<USER_ID>操作員號</USER_ID>
<PASSWORD>密碼</PASSWORD>
<TX_CODE>6W8010</TX_CODE>
<LANGUAGE>CN</LANGUAGE>
<TX_INFO>
<PAY_ACCNO>轉出賬戶號</PAY_ACCNO>
<RECV_ACCNO>轉入賬戶號</RECV_ACCNO>
<RECV_ACC_NAME>轉入賬戶名稱</RECV_ACC_NAME>
<CHK_RECVNAME>收款賬戶戶名校驗</CHK_RECVNAME>
<RECV_OPENACC_DEPT>轉入賬戶開戶機構名稱</RECV_OPENACC_DEPT>
<AMOUNT>金額</AMOUNT>
<CUR_TYPE>01</CUR_TYPE>
<CST_PAY_NO>客戶方流水號</CST_PAY_NO>
<USEOF>用途</USEOF>
<REM1>備註1</REM1>
<REM2>備註2</REM2>
</TX_INFO>
<SIGN_INFO>簽名信息</SIGN_INFO>
<SIGNCERT>簽名CA信息</SIGNCERT>
</TX>
</xml>';
$out = '';
if(!socket_write($socket, $in, strlen($in))) {
echo "socket_write() failed: reason: " . socket_strerror($socket) . "\n";
}else {
echo "發送到服務器信息成功!<br/>\n";
echo "發送的內容爲:<font color='red'>$in</font> <br>";
}
while($out = socket_read($socket, 8192)) {
echo "接收服務器回傳信息成功!<br/>\n";
echo "接受的內容爲:".$out."<br/>";
}
echo "關閉SOCKET...<br/>";
socket_close($socket);
echo "關閉OK\n";
?>
在看看服務端的窗口結果:
而此時服務器端代碼必須一直運行,否則出錯。在這方面 PHP 真的是天生的缺陷