TCP/IP協議簡單扯蛋
它是一個複雜的協議族,但是呢經過層層封裝之後轉換為網路資料幀經過網路卡傳送出去的,當然在傳送之前會先發起一次ARP請求查詢一下對方的mac實體地址,對方響應後返回以便封裝資料傳送,一般來說網路卡的mac地址有的是寫入EEPROM暫存器裡儲存起來的。
但是它低層網路卡驅動要動的事情,那麼我們碼農只關注一下傳輸層的TCP/UDP即可,TCP傳輸層擁有自己的接收與傳送緩衝區,而UDP並沒有,每次傳送資料時,接收端必須立即接受,否則丟包。TCP的傳送端與接收端讀寫次數並不一定相等,這就是位元組流的概念,而UDP則是資料包提供不可靠傳輸。
傳送函式API
socket_write ( resource $socket , string $buffer [, int $length = 0 ] ) : int socket_send ( resource $socket , string $buf , int $len , int $flags ) : int socket_sendmsg ( resource $socket , array $message [, int $flags = 0 ] ) : int socket_sendto( resource $socket , string $buf , int $len , int $flags , string $addr [, int $port = 0 ] ) : int
接收函式API
socket_read ( resource $socket , int $length [, int $type = PHP_BINARY_READ ] ) : string socket_recv ( resource $socket , string &$buf , int $len , int $flags ) : int socket_recvfrom( resource $socket , string &$buf , int $len , int $flags, string &$name [, int &$port ] ) : int socket_recvmsg ( resource $socket , array &$message [, int $flags = 0 ] ) : int
TCP位元組流的傳送與接收
$ip = "0.0.0.0"; $port = $argv[1]; $sockefd = socket_create(AF_INET,SOCK_STREAM,0); socket_set_option($sockefd,SOL_SOCKET,SO_REUSEPORT,1); socket_bind($sockefd,$ip,$port); socket_listen($sockefd,5); while (1){ $connfd = socket_accept($sockefd); $message = "php is the best language in the world!"; $remoteIp; $remoteAddr; //獲取socket 檔案描述符繫結的埠和地址 //網路卡接收資料時執行的中斷函式會根據埠找到對應的檔案描符並寫入其緩衝區 //一般帶有緩衝的稱為IO流【使用者空間】 socket_getpeername($connfd,$remoteIp,$remoteAddr); echo "有客戶端連線,ip為:".$remoteIp.",埠為:".$remoteAddr.PHP_EOL; if ($connfd){ socket_send($connfd,$message,strlen($message),0); while (1){ $recvMessage = ""; $recvBytes = socket_recv($connfd,$recvMessage,8192,0); if ($recvBytes){ $sendBytes = socket_sendto($connfd,$recvMessage,$recvBytes,0,$remoteIp,$remoteAddr); fprintf(STDOUT,"傳送了%d 位元組\n",$sendBytes); } } } } socket_close($sockefd); socket_close($connfd);
UDP資料包的傳送與接收
UDP SEVER 端
$ip = "0.0.0.0"; $port = $argv[1]; $sockefd = socket_create(AF_INET,SOCK_DGRAM,SOL_UDP); socket_set_option($sockefd,SOL_SOCKET,SO_REUSEPORT,1); socket_bind($sockefd,$ip,$port); function resetClient() { //此結構PHP.net手冊並沒有細說,而是本人通過除錯低層核心程式碼獲取以及參考unix API //大家可以自行參考 return [ 'name' =>[],//socket 套接字地址 【有通用地址和專用地址】 協議不同 此引數內容結構不同,後面扯 'control' =>[],// 'iov' => [],//資料 可參考 struct iovec 'flags'=> 0,//接收訊息標誌位 給0正常就行 'controllen'=>8192//輔助資料的地址 ]; } while(1){ $client=resetClient(); //在此阻塞SLEEPING socket_recvmsg($sockefd,$client,0); print_r($client); if (isset($client['name']['addr'])){ socket_sendto($sockefd,"world",5,0,$client['name']['addr'],$client['name']['port']); } sleep(2); } socket_close($sockefd);
UPD CLIENT端
$ip = "127.0.0.1"; $port = $argv[1]; $socketfd = socket_create(AF_INET,SOCK_DGRAM,SOL_UDP); $msgStruct = [ 'name' =>[ 'addr'=>$ip, 'port'=>$port ], 'control' =>[], 'iov' => ["hello,world"], 'flags'=> 0 ]; while (1){ //阻塞讀取終端的輸入 $data = fread(STDIN,8192); if ($data){ $recvMsg="";$remoteIp;$remoteAddr; socket_sendto($socketfd,$data,strlen($data),0,$ip,$port); socket_recvfrom($socketfd,$recvMsg,8192,0,$remoteIp,$remoteAddr); fprintf(STDOUT,"ip:%s,port:%d,msg:%s\n",$remoteIp,$remoteAddr,$recvMsg); } } socket_close($socketfd);
啟動後netstat 觀察
大家應該看到比較明顯的地方是State它並沒有顯示什麼LISTEN
PS:是每次客戶端連線埠是系統隨機分配的
UDP通訊並不像tcp那樣連線的操作,而是大家向socket【綁架了ip和埠的檔案描述符】進行讀寫操作完成了所謂的通訊。
它的通訊只要指定ip,port和資料即可通訊,是面向報文並非連線。
本作品採用《CC 協議》,轉載必須註明作者和本文連結