PHP的Socket函數參考
這些Socket函數直接跟互聯網的協議進行發送信息。相對于fopensock的流來講,他們操作在一個比較底層的級別。通常,他們都是對C 函數進行封裝,并且名稱都類似。如果你有使用C進行socket編程的經驗,那么使用這些函數將是非常熟練的。我們這里不討論特別詳細的socket編程。使用這些函數能夠解決高層級別函數所不能解決的難題。使用這些函數能夠實現類似fopen的功能,你也許有很多方法來實現socket的功能,比如在PHP中使用CLI(Command-line Interface)來實現的Internet守護進程。resource socket_accept(resource socket)在你的腳本服務器端中,使用socket_accept接受一個進入的連接。你必須首先產生一個socket,綁定它到一個名字,并且設置它監聽一個端口。在塊模式中,socket_accept將產生一個唯一接受后的連接。在非塊模式中,它沒有建立連接則返回false。另外,當你有了一個新的socket資源后就能夠進行讀寫操作。下面我們將示范一個簡單的回顯服務器端。它運行在CLI(命令行)下面,它在12345端口等待客戶端的連接。socket_accept<?php set_time_limit(0); //create the socket if(($socket = socket_create(AF_INET, SOCK_STREAM, 0)) < 0){ print('Couldn't create socket: ' . socket_strerror(socket_last_error()) . 'n'); } //bind it to the given address and port if(($error = socket_bind($socket, gethostbyname($_SERVER['HOSTNAME']), 12345)) < 0){ print('Couldn't bind socket: ' . socket_strerror(socket_last_error()) . 'n'); } if(($error = socket_listen($socket, 5)) < 0){ print('Couldn't list on socket: ' . socket_strerror(socket_last_error()) . 'n'); } while(TRUE){ //wait for connection if(($accept = socket_accept($socket)) < 0){ print('Error while reading: ' . socket_strerror($message) . 'n'); break; } //send welcome message socket_write($accept, 'Connection acceptedn'); print(date('Y-m-d H:i:s') . ' STATUS: Connection acceptedn'); ob_flush(); while(TRUE){ //read line from client if(FALSE === ($line = socket_read($accept, 1024))){ print('Couldn't read from socket: ' . socket_strerror(socket_last_error()) . 'n'); break 2; } if(!@socket_write($accept, 'ECHO: $line')){ print(date('Y-m-d H:i:s') . ' STATUS: Connection interruptedn'); break; } print(date('Y-m-d H:i:s') . ' READ: $line'); ob_flush(); } socket_close($accept); }?>
bool socket_bind(resource socket, string address, integer port)這個socket_bind()把一個socket資源綁定在一個地址上。這個socket必須由socket_create()函數返回的一個資源。這個地址必須是一個IP地址或者是一個保存Unix socket的路徑。如果是運行在Internet上的socket,你還必須提供一個端口。socket_clear_error(resource socket)這個函數能夠清除制定socket的錯誤,如果沒有指定參數,那么將清除所有socket的錯誤。socket_close(resource socket)socket_close函數關閉一個socket并且清除該socket所占用的內存資源。boolean socket_connect(resource socket, string address, integer port)這個函數創建一個客戶端到一個端口或者socket的連接。你必須提供一個由socket_create產生的socket。這個address參數必須到一個socket的路徑或者是一個IP地址。如果是后者,還必須跟一個數字的端口號。下面例子演示了使用UDP協議的連接到游戲服務器然后獲取信息的過程。socket_connect<?php //create UDP socket if(($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) < 0){ print('Couldn't create socket: ' . socket_strerror(socket_last_error()) . 'n'); } //timeout after 5 seconds socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec'=>5,'usec'=>0)); //connect to the RtCW master server if(!socket_connect($socket, 'wolfmaster.idsoftware.com', 27950)){ print('Couldn't connect: ' . socket_strerror(socket_last_error()) . 'n'); } //send request for servers socket_write($socket, 'xFFxFFxFFxFFgetserversx00'); //get servers $server = array(); while(FALSE !== ($line = @socket_read($socket, 4096))){ //parse data for($i=22; ($i+5) < strlen($line); $i += 7){ $ip = ord(substr($line, $i+1, 1)) . '.' . ord(substr($line, $i+2, 1)) . '.' . ord(substr($line, $i+3, 1)) . '.' . ord(substr($line, $i+4, 1)); $port = (ord(substr($line, $i+5, 1)) * 256) + ord(substr($line, $i+6, 1)); $server[] = array('ip'=>$ip, 'port'=>$port); } } print('<h1>' . count($server) . ' Servers</h1>n'); //loop over servers, getting status foreach($server as $s){ print('<h1>{$s['ip']}:{$s['port']}</h1>n'); //connect to RtCW server if(!socket_connect($socket, $s['ip'], $s['port'])){ print('<p>n' . socket_strerror(socket_last_error()) . 'n</p>n'); continue; } //send request for status socket_write($socket, 'xFFxFFxFFxFFgetstatusx00'); //get status from server if(FALSE === ($line = @socket_read($socket, 1024))){ print('<p>n' . socket_strerror(socket_last_error()) . 'n</p>n'); continue; } $part = explode('n', $line); //settings are in second line separated by backslashes $setting = explode('', $part[1]); print('<h2>Configuration</h2>n'); print('<p>n'); for($s=1; $s < count($setting); $s += 2){ print('tt{$setting[$s]} = {$setting[$s+1]}<br>n'); } print('</p>n'); print('<h2>Players</h2>n'); $lastPlayer = count($part) - 1; for($p=2; $p < $lastPlayer; $p++){ $player = explode(' ', $part[$p]); print('{$player[2]} Score={$player[0]} ' . 'Ping={$player[1]}<br>n'); } print('</p>n'); ob_flush(); } print('</table>n'); socket_close($socket);?>resource socket_create(integer family, integer socket_type, integer protocol)socket_create初始化一個socket的結構。第一個參數是一個protocol family,或者域。你必須使用AF_INET來指定一個Internet連接,或者使用AF_UNIX來指定一個Unix socket連接。第二個參數是一個socket的類型,你可以從下面的表中選擇。一般情況下,使用SOCK_STREAM來使用TCP協議,UDP協議使用SOCK_DGRAM。第三個參數指定為一個協議。使用SOL_TCP或SOL_UDP來分別對應TCP和UDP協議。還有一個選擇是你能夠使用 getprotobyname函數來處理。Socket 類型常量 描述SOCK_DGRAM 自動尋址數據包socketSOCK_RAW RAW協議接口SOCK_RDM 可靠交換消息SOCK_SEQPACKET 順序數據包socketSOCK_STREAM 流socketresource socket_create_listen(integer port, integer backlog)使用socket_create_listen是一種比socket_create更簡單的產生一個socket進行監聽。這個產生的socket將監聽指定的端口,后面可選的參數backlog是設置允許最大的連接數。boolean socket_create_pair(integer family, integer socket_type, integer protocol, array handles)socket_create_pair函數產生一對socket連接。首先前三個參數是對一個socket_create的描述,這個handles參數是一個包含兩個socket資源的數組。該函數是對C里面socketpair函數的封裝。socket_create_pair<?php if(!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $socket)){ print('Couldn't make sockets!n'); exit(); } $child = pcntl_fork(); if($child == -1){ print('Couldn't fork!n'); exit(); } elseif($child > 0){ //parent socket_close($socket[0]); print('Parent: waiting for messagen'); $message = socket_read($socket[1], 1024, PHP_NORMAL_READ); print('Parent: got message--$messagen'); socket_write($socket[1], 'Hello, Child Process!n'); pcntl_waitpid($child, $status); }else{ //child socket_close($socket[1]); socket_write($socket[0], 'Hello, Parent Process!n'); print('Child: waiting for messagen'); $message = socket_read($socket[0], 1024, PHP_NORMAL_READ); print('Child: got message--$messagen'); exit(0); }?>value socket_get_option(resource socket, integer level, integer option)socket_get_option函數返回一個下表中所列的一個添加值,你必須提供一個由socket_create產生的socket資源和一個等級。這個獲取的socket級別,可以使用SOL_SOCKET來確定這個級別參數。另外,使用協議,比如象SOL_TCP來表示一個TCP協議。這些選項可能是由socket_set_option設置的。socket_get_options<?php $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); print('SO_BROADCAST: ' . socket_get_option($socket, SOL_SOCKET, SO_BROADCAST) . '<br>n'); print('SO_DEBUG: ' . socket_get_option($socket, SOL_SOCKET, SO_DEBUG) . '<br>n'); print('SO_DONTROUTE: ' . socket_get_option($socket, SOL_SOCKET, SO_DONTROUTE) . '<br>n'); print('SO_ERROR: ' . socket_get_option($socket, SOL_SOCKET, SO_ERROR) . '<br>n'); print('SO_KEEPALIVE: ' . socket_get_option($socket, SOL_SOCKET, SO_KEEPALIVE) . '<br>n'); print('SO_LINGER: ' . print_r(socket_get_option($socket, SOL_SOCKET, SO_LINGER), TRUE) . '<br>n'); print('SO_OOBINLINE: ' . socket_get_option($socket, SOL_SOCKET, SO_OOBINLINE) . '<br>n'); print('SO_RCVBUF: ' . socket_get_option($socket, SOL_SOCKET, SO_RCVBUF) . '<br>n'); print('SO_RCVLOWAT: ' . socket_get_option($socket, SOL_SOCKET, SO_RCVLOWAT) . '<br>n'); print('SO_RCVTIMEO: ' . print_r(socket_get_option($socket, SOL_SOCKET, SO_RCVTIMEO), TRUE) . '<br>n'); print('SO_REUSEADDR: ' . socket_get_option($socket, SOL_SOCKET, SO_REUSEADDR) . '<br>n'); print('SO_SNDBUF: ' . socket_get_option($socket, SOL_SOCKET, SO_SNDBUF) . '<br>n'); print('SO_SNDLOWAT: ' . socket_get_option($socket, SOL_SOCKET, SO_SNDLOWAT) . '<br>n'); print('SO_SNDTIMEO: ' . print_r(socket_get_option($socket, SOL_SOCKET, SO_SNDTIMEO), TRUE) . '<br>n'); print('SO_TYPE: ' . socket_get_option($socket, SOL_SOCKET, SO_TYPE) . '<br>n');?>
Socket選項表選項 描述SO_BROADCAST 允許自動尋址的socket發送和接受廣播包SO_DEBUG 打開socket調試功能,只有root才有權限打開該選項SO_DONTROUTE 不接受路由包通過網關SO_ERROR 獲取并且清除最后一次的socket錯誤,這個選項也許不用設置SO_KEEPALIVE 打開保持激活狀態的消息SO_LINGER Socket_colse和socket_shutdown的中止消息發送超時,該選項使用一個數組,包括l_onoff和l_linger兩個鍵。SO_OOBINLINE 把數據直接插入到接受緩沖SO_RCVBUF 限制接受緩沖的最大字節SO_RCVLOWAT 延遲通過接受一個最小的數據SO_RCVTIMEO 延遲報告一個接受超時報告,使用數組的兩個鍵:sec和usecSO_REUSEADDR 允許重新使用本地地址SO_SNDBUF 限制發送緩沖的最大字節SO_SNDLOWAT 延遲發送數據到這個協議當接受一個最小的字節SO_SNDTIMEO 延遲報告超時錯誤,當發送發送通過一個時間。該選項使用數組的鍵值:sec和usecSO_TYPE 獲取socket的類型,該選項可能不用設置
boolean socket_getpeername(resource socket, string address, integer port)socket_getpeername從指定的一個連接中獲取地址和端口。如果連接為Unix socket,那么將返回文件系統的路徑。boolean socket_getsockname(resource socket, string address, integer port)socket_getsockname放置一個名字到socket中,并且加上address和port參數。失敗返回false。(下面的socket_iovec_* 函數不太了解,不敢亂翻譯,保留原文)boolean socket_iovec_add(resource iovector, integer length)The socket_iovec_add unction adds an I/O vector to the scatter/gather array.resource socket_iovec_alloc(integer count, …)The socket_iovec_alloc function returns a resource for handling a collection of I/O vectors. The first argument specifies the number of vectors. Following arguments specify the length of each vector.boolean socket_iovec_delete(resource iovector, integer position)The socket_iovec_delete function removes the I/O vector at the given position.string socket_iovec_fetch(resource iovector, integer position)The socket_iovec_fetch function returns the value of the specified vector in the I/O vector resource.boolean socket_iovec_free(resource iovector)The socket_iovec_free function frees the memory used for an I/O vector resource.boolean socket_iovec_set(resource iovector, integer position, string value)The socket_iovec_set sets the value of I/O vector at the given position.integer socket_last_error(resource socket)socket_last_error函數返回操作中的任何socket函數產生的最后錯誤。你也許在上面函數中設置了socket資源的 socket選項在指定的連接上。下面的表列出了返回的錯誤代碼,你同樣可以使用soclet_strerror函數來獲取詳細的錯誤。使用 socket_clear_error函數清除socket的錯誤。Socket錯誤代碼表常量描述SOCKET_E2BIG 參數列表太長SOCKET_EACCES沒有許可權限SOCKET_EADDRINUSE地址已經被使用SOCKET_EADDRNOTAVAIL不能解析請求的地址SOCKET_EADV廣播(廣告)錯誤SOCKET_EAFNOSUPPORTAddress family不支持的協議SOCKET_EAGAIN資源暫時不能獲得SOCKET_EALREADY 操作已經在執行SOCKET_EBADE 無效的交換SOCKET_EBADF錯誤的文件描述符SOCKET_EBADFD文件描述符錯誤的狀態SOCKET_EBADMSG錯誤的消息SOCKET_EBADR無效的請求描述SOCKET_EBADRQC 無效的請求代碼SOCKET_EBADSLT無效的操作位置SOCKET_EBUSY驅動或資源繁忙SOCKET_ECHRNG 信道號碼超出范圍SOCKET_ECOMM發送通訊錯誤SOCKET_ECONNABORTED軟件原因導致通行中斷SOCKET_ECONNREFUSED連接被拒絕SOCKET_ECONNRESET連接被相同的socket重置SOCKET_EDESTADDRREQ必須需要目標地址SOCKET_EDQUOT 超出磁盤配額SOCKET_EEXIST 文件已存在SOCKET_EFAULT 錯誤的地址SOCKET_EHOSTDOWN主機已關閉SOCKET_EHOSTUNREACH沒有路由到主機SOCKET_EIDRM表示ID被刪除SOCKET_EINPROGRESS操作正在執行SOCKET_EINTR系統調用被阻止SOCKET_EINVAL無效的參數SOCKET_EIO輸入/ 輸出錯誤SOCKET_EISCONN傳輸終端已經連接SOCKET_EISDIR 是一個目錄SOCKET_EISNAM是一個指定的類型文件SOCKET_EL2HLT級別2已中止SOCKET_EL2NSYNC級別2不同步SOCKET_EL3HLT級別3已中止SOCKET_EL3RST級別3被重置SOCKET_ELNRNG 連接號超出范圍SOCKET_ELOOP太多級別的符號連接SOCKET_EMEDIUMTYPE錯誤的媒介類型(中間類型)SOCKET_EMFILE太多打開的文件SOCKET_EMLINK 太多的連接SOCKET_EMSGSIZE消息太長SOCKET_EMULTIHOP嘗試次數太多SOCKET_ENAMETOOLONG文件名太長SOCKET_ENETDOWN; 網絡已關閉SOCKET_ENETRESET; 網絡中斷,連接被重置SOCKET_ENETUNREACH網絡不可達SOCKET_ENFILE系統中太多打開的文件SOCKET_ENOANO 沒有正極SOCKET_ENOBUFS沒有可用的緩存空間SOCKET_ENOCSI沒有可用的CSI結構SOCKET_ENODATA沒有可用的數據SOCKET_ENODEV 沒有這樣的驅動SOCKET_ENOENT沒有這樣的文件或目錄SOCKET_ENOLCK沒有可用的記錄鎖SOCKET_ENOLINK已經有的服務的連接SOCKET_ENOMEDIUM沒有媒介被找到SOCKET_ENOMEM 不能分配內存SOCKET_ENOMSG沒有指定的消息類型SOCKET_ENONET 設備不在網絡上SOCKET_ENOPROTOOPT協議不可用SOCKET_ENOSPC 沒有空間在驅動器SOCKET_ENOSR超出的流資源SOCKET_ENOSTR 驅動不是一個流SOCKET_ENOSYS函數沒有執行SOCKET_ENOTBLK塊驅動是必須的SOCKET_ENOTCONN傳輸終端沒有連接SOCKET_ENOTDIR沒有一個目錄SOCKET_ENOTEMPTY目錄為空SOCKET_ENOTSOCK Socket操作在一個非socket上SOCKET_ENOTTY不相符的IO控制器SOCKET_ENOTUNIQ在網絡上名字不是唯一的SOCKET_ENXIO 沒有這樣的驅動或地址SOCKET_EOPNOTSUPP 操作不支持SOCKET_EPERM操作不允許SOCKET_EPFNOSUPPORT Protocol family不支持SOCKET_EPIPE失敗的管道SOCKET_EPROTO 協議錯誤SOCKET_EPROTONOSUPPORT協議不支持SOCKET_EPROTOTYPESocket上協議錯誤的類型SOCKET_EREMCHG遠程地址已改變SOCKET_EREMOTE對象是遠程的SOCKET_EREMOTEIO遠程I/O錯誤SOCKET_ERESTART 中斷的系統調用將要重新開始SOCKET_EROFS文件系統為只讀SOCKET_ESHUTDOWN.傳輸端點中斷不能發送SOCKET_ESOCKTNOSUPPORT Socket類型不支持SOCKET_ESPIPE不合法的檢索SOCKET_ESTRPIPE流管道錯誤SOCKET_ETIME定時器到時SOCKET_ETIMEDOUT 連接超時SOCKET_ETOOMANYREFS太多連接無法結合SOCKET_EUNATCH無法附加協議驅動SOCKET_EUSERS太多用戶SOCKET_EWOULDBLOCK 資源暫時無法獲得SOCKET_EXDEV無效的交叉驅動連接SOCKET_EXFULL 交換已滿
boolean socket_listen(resource socket, integer backlog)這個socket_listen函數等待從客戶端過來的連接,backlog參數設置允許最多等待連接的隊列數。string socket_read(resource socket, integer length, integer type)socket_read函數從特定的socket中讀取指定的字節,如果錯誤返回false。缺省下,是采用二進制安全的讀取模式。你可以外在的設置type參數為PHP_BINARY_READ來改變讀取模式。你也可以把type設置為PHP_NORMAL_READ。boolean socket_readv(resource socket, resource iovector)socket_readv函數把讀取的數據插入到iovector資源中。integer socket_recv(resource socket, string buffer, integer length, integer flags)socket_recv函數讀取數據插入到緩沖中。Length參數設置最多讀取的字節數,flag參數可以使用MSG_OOB或MSG_PEEK。函數返回讀取的字節數。integer socket_recvfrom(resource socket, string buffer, integer length, string host, integer port)socket_frcvfrom函數讀取數據插入到緩存中。Length參數設置獲取最多允許接受的字節數。設置flags參數可以為MSG_OOB 或 MSG_PEEK。PHP設置主機和端口參數適當的值能夠獲取從主機發出的數據。boolean socket_recvmsg(resource socket, resource iovector, array control, integer length, integer flags, string host, integer port)socket_recvmsg函數從socket中讀取數據并且插入到一個I/O向量資源中。PHP設置control參數是一個具有三個元素的聯合數組:cmsg_level, cmsg_type, 和 cmsg_data。Length參數是一個附加在數據中的關于獲取數據的長度參數。Flags參數是設置允許值和返回值。在寫的時間,PHP無法執行所有的輸出常量。PHP設置host和port參數適當的值是為了獲取從遠程主機中發送的數據。(Socket_slect函數沒有翻譯,因為怕詞不達意)integer socket_select(array read, array write, array exception, integer timeout_seconds, integer timeout_microseconds)The socket_select function waits for changes to sockets. PHP watches the sockets given in the read array for new data coming in. PHP watches the streams given in the write array for being ready to accept more data. PHP watches the streams given in the exception argument for errors. If the number of seconds specified in the timeout_seconds argument passes, the function returns. Use the optional timeout_microseconds argument to specify a timeout less than 1 second.The socket_select function returns the number of sockets that changed or FALSE if an error occurred. If the call timed out, this function returns zero. It also modifies the given arrays so that they include only those sockets that changed.If you have no sockets of a particular type to watch, you may pass an empty array or a variable set to NULL.integer socket_send(resource socket, string buffer, integer length, integer flags)socket_send函數把寫數據到緩沖中,然后插入到連接中。你必須指定一個緩沖最大可寫字節數。你同樣可以設置flags參數為空,或者為下面聯合常量中的一個:MSG_DONTROUTE和 MSG_OOB。函數結束返回已經寫的字節數,失敗返回false。boolean socket_sendmsg(resource socket, resource iovector, integer flags, string address, integer port)socket_sendmsg嘗試發送數據到一個socket。它適合無連接的socket。Iovector參數是一個通過 socket_iovec_alloc函數產生的資源。你必須指定flags參數為:NULL, MSG_DONTROUTE, MSG_OOB,或者是兩個聯合常量。你應當指定一個地址和一個Internet請求的端口。Socket_sendmsg函數發送數據返回true,但是不能保證數據一定到達。integer socket_sendto(resource socket, string buffer, integer length, integer flags, string address, integer port)socket_sendto函數嘗試寫數據到buffer緩沖中,并且發送給一個socket。它適合大部分無連接的socket。你必須指定 flags為:NULL,MSG_DONTROUTE,MSG_OOB或者是一個兩個聯合常量。你還應但指定地址和一個請求的端口。Socket_sendto函數數據發送出去返回true,但是不能保證數據一定到達。
boolean socket_set_block(resource socket)socket_set_block函數設置socket插入到一個塊模式中,這是缺省模式。在塊模式中,I/O操作正對于一個完成的請求。boolean socket_set_nonblock(resource socket)socket_set_nonblock函數設置socket插入到意個非塊模式中。在非塊模式中,I/O操作馬上返回,即使沒有數據。boolean socket_set_option(resource socket, integer level, integer option, integer value)socket_set_option函數給socket設置一個選項。Level參數設置一個標志級別的常量。有效的值包括:SOL_SOCKET, SOL_TCP和SOL_UDP。Option參數必須匹配文章上面的Socket選項表中的常量。boolean socket_shutdown(resource socket, integer how)socket_shutdown函數關閉一個關于I/O的socket。設置how為0則中止接受數據,設置為1則停止發送數據,設置為2則中止二者操作。string socket_strerror(integer error)socket_strerror函數返回一個錯誤號的詳細錯誤信息。integer socket_write(resource socket, string buffer, integer length)socket_write函數寫數據到buffer緩沖中然后輸出到socket中。你可以指定length參數來指定緩沖的最大字節數。這個函數通常情況下比socket_send更方便。boolean socket_writev(resource socket, resource iovector)socket_writev函數通過I/O向量寫數據到一個socket中。
