PHP中正确的Socket编程是什么?

时间:2014-01-20 02:48:05

标签: php sockets

我正在使用PHP开发套接字程序。

Problems : Cannot detect to a client is disconnected using socket_recv() method.

我的源已被配置为3个文件。 2个文件是服务器文件,1个文件是客户端文件。 这是我的详细代码。

 socket.server.php
    <?php
        class SocketServer {
            public $address;    // socket address
            public $port;       // socket port

            public $handle;     // openned file handler
            public $fileurl;    // File url for logs
            public $echoflg;    // Echo or unless flag

            public $ssock;      // TCP stream server socket resource

            public $clients;    // Client array

            function __construct() {
                $this->address  = "10.76.15.2";
                $this->port     = 6666;

                $this->handle   = FALSE;
                $this->echoflg  = FALSE;

                $this->clients  = array();
            }

            /**
            * Write in log files for logging
            * @param $log
            * 
            * @return void
            */
            public function logs($log){
                if($this->handle){
                    $log = "[".date("Y-m-d H:i:s")."] ".$log."\r\n\r\n";
                    fwrite($this->handle, $log);

                    if($this->echoflg) echo $log."</br></br>";
                }
            }

            /**
            * Get Socket Last Error Message
            * 
            * @return SocketLastError
            */
            public function getSocketError($sock){
                return socket_strerror(socket_last_error($sock));
            }       

            /**
            * Close the server socket
            * 
            * @return void
            */
            public function closeServerSocket(){
                socket_close($this->ssock);
                $this->logs("---------------- SERVER CLOSED -----------------");
            }

            /**
            * Creating, Binding, Listening, Non Blocking for TCP stream socket
            * 
            * @return Bool
            */
            public function initServer(){
                $this->handle = fopen($this->fileurl, "a+");    // open log file

                $this->logs("============== SERVER STARTING ===============");

                //set_time_limit (0);       // Set time limit to indefinite execution

                /** Create a TCP Stream socket  **/
                if(($this->ssock = socket_create(AF_INET, SOCK_STREAM, 0)) === FALSE){
                    $this->logs("Fail to Create a TCP Stream socket\r\n".socket_strerror(socket_last_error()));
                    return FALSE;
                }
                else{
                    $this->logs("Success to Create a TCP Stream socket");
                }

                /** Bind the socket to an address/port  **/
                if(socket_bind($this->ssock, $this->address, $this->port) === FALSE){
                    $this->logs("Fail to bind to address\r\n".$this->getSocketError($this->ssock));

                    $this->closeServerSocket();
                    return FALSE;
                }
                else{
                    $this->logs("Success to bind to address");
                }

                /** Start listening for connections **/
                if(socket_listen($this->ssock) === FALSE){
                    $this->logs("Fail to listen for connections\r\n".$this->getSocketError($this->ssock));

                    $this->closeServerSocket();
                    return FALSE;
                }
                else{
                    $this->logs("Success to listen for connections");
                }

                /** Non block socket type   **/
                if(socket_set_nonblock($this->ssock) === FALSE){
                    $this->logs("Fail to set for Non block socket type\r\n".$this->getSocketError($this->ssock));

                    $this->closeServerSocket();
                    return FALSE;
                }
                else{
                    $this->logs("Success to set for Non block socket type");
                }

                $this->logs("+++++++++++++++ SERVER STARTED ++++++++++++++++");

                return TRUE;
            }

            /**
            * Read a data from client on socket
            * @param $csock - client socket connected to server
            * 
            * @return $msg
            */
            public function readMessage($csock, $length=1024){
                $msg = "";
                $tmp = "";
                //$this->logs("Reading");

                /*
                while(($tmp = socket_read($csock, 2048, PHP_NORMAL_READ)) > 0){
                    //$this->logs($tmp);
                    $msg .= $tmp;
                }

                if($tmp === FALSE) return FALSE;
                else return $msg;
                */

                /*
                while(socket_recv($csock, $message, 1024, 0) !== FALSE){
                    if($message != null){
                        $msg .= $message;
                    }
                };
                */


                while(($flag = socket_recv($csock, $buf, $length, 0)) > 0){

                    $asc = ord(substr($buf, -1));
                    if ($asc == 0) {
                        $msg .= substr($buf, 0, -1);
                        break;
                    }else{
                        $msg .= $buf;
                    }

                    //$msg .= $buf;
                }

                if($flag < 0){  // Error
                    $this->logs("Error on reading : ".$this->getSocketError($this->ssock));

                    return FALSE;
                }
                elseif($flag === 0){    // client disconnected
                    $this->logs("Client disconnected");

                    return FALSE;
                }
                else{
                    //$this->logs("Reading finished");

                    return $msg;
                }


                /*
                //$msg = socket_recv($csock, $message, 2048, 0);

                $this->logs("Reading finished");

                if($msg === FALSE){

                    return FALSE;
                }

                return $msg;
                */
            }

            /**
            * Write to the client on socket
            * @param $csock
            * @param $msg
            * 
            * @return Bool
            */
            public function sendMessage($csock, $msg){
                $msg = $msg."\n\r"; 
                $length = strlen($msg);

                if(socket_write($csock, $msg, $length) === FALSE){
                    return FALSE;
                }
                else{
                    return TRUE;
                }

                /*
                while(true) { 
                    $sent = socket_write($csock, $msg."\n\r", $length = strlen($msg."\n\r"));

                    if($sent === FALSE) {

                        return FALSE; 
                    } 
                    if($sent < $length) { 
                        $msg = substr($msg, $sent); 
                        $length -= $sent; 

                        $this->logs("Message truncated: Resending: $msg"); 
                    }
                    else{

                        return TRUE; 
                    } 
                } 
                return FALSE;
                */
            }

            /**
            * Re-arrange a connected clients list
            * 
            * @return void
            */
            public function resetClientList(){
                $tmp    = array();
                $i      = 0;
                if(count($this->clients)){
                    foreach ($this->clients as $k => $v){
                        $tmp[$i] = $v;

                        $i++; 
                    }

                    $this->clients = $tmp;
                }
            }

            /**
            * Detect a new client and accpet or close
            * 
            * @return void
            */
            public function acceptClient(){
                $new_index = count($this->clients);

                if($csock = socket_accept($this->ssock)){

                    $this->logs("Trying index : ".$new_index);

                    if(is_resource($csock)){
                        if(($msg = $this->readMessage($csock)) === FALSE){

                            $this->logs("Cannnot accpet a new client : ".$new_index);
                            $this->logs("Reason : ".$this->getSocketError($this->ssock));
                            $this->logs("Closed a new client : ".$new_index);

                            socket_close($csock);                       
                        }
                        else{
                            $json = json_decode($msg);
                            if(isset($json->user_id) && isset($json->user_type)){
                                if($this->sendMessage($csock, "success") === FALSE){

                                    $this->logs("Fail to send a message : ".$json->user_id);
                                    $this->logs("Reason : ".$this->getSocketError($csock));

                                    socket_close($csock);
                                }
                                else{
                                    $this->clients[$new_index]['csock']     = $csock;
                                    $this->clients[$new_index]['user_id']   = $json->user_id;
                                    $this->clients[$new_index]['user_type'] = $json->user_type;

                                    $this->logs("New Client Connected : ".$new_index);
                                    $this->logs("user_id : ".$this->clients[$new_index]['user_id']);
                                    $this->logs("user_type : ".$this->clients[$new_index]['user_type']);
                                }
                            }
                            else{                           
                                $this->logs("illegal a new client : ".$new_index." or invalid json");
                                $this->logs("Closed a new client : ".$new_index);

                                socket_close($csock);
                            }
                        }
                    }
                    else $this->logs("Not resource type");
                }
            }

            /**
            * If $user_id param is exist in client list, then return the index of list
            * @param $user_id
            * 
            * @return index/FALSE
            */
            public function searchReceiverIndex($user_id){
                for($i = 0; $i < count($this->clients); $i++){
                    if($this->clients[$i]['user_id'] == $user_id) return $i;
                }

                return FALSE;
            }
        }
    ?>

run.socket.server.php
#!/usr/local/bin/php -q
<?php
    require_once("socket.server.php");

    ini_set('error_reporting', E_ALL ^ E_NOTICE); 
    ini_set('display_errors', 1); 

    // Set time limit to indefinite execution 
    set_time_limit (0); 

    $ss = new SocketServer();

    /**
    * Setting parameters for Log
    */
    $ss->echoflg = TRUE;    // Enable Debug Method with echo
    $ss->fileurl = $_SERVER['DOCUMENT_ROOT']."/socket.server.logs.txt";     // Set the url for server logs

    /**
    * Now Starting Socket Server
    */
    if($ss->initServer() === TRUE){
        while(TRUE){
            $ss->resetClientList();

            $ss->acceptClient();            

            /**
            * Repeatedly check a new message on client list,
            * if cannot read then the client will be removed in client list.            
            */
            if(count($ss->clients) > 0){
                foreach($ss->clients as $k=>$v){
                    if(($msg = $ss->readMessage($ss->clients[$k]['csock'])) === FALSE){

                        $ss->logs("Cannnot read a data from client : ".$ss->clients[$k]['user_id']);
                        $ss->logs("Reason : ".$ss->getSocketError($ss->clients[$k]['csock']));
                        $ss->logs("Close a client : ".$ss->clients[$k]['user_id']);

                        socket_close($ss->clients[$k]['csock']);

                        unset($ss->clients[$k]);    // Remove from client list
                    }
                    else{
                        $json = json_decode($msg);

                        if(isset($json->receivers) && isset($json->messages)){
                            if(($receiverIndex = $ss->searchReceiverIndex($json->receivers)) === FALSE){                                

                                $ss->logs("The receiver is not exist on the client list");
                            }
                            else{
                                /**
                                * Create a JSON message                             
                                */
                                $a = array();

                                $a['from']      = $ss->clients[$k]['user_id'];
                                $a['messages']  = $json->messages;

                                $json = json_encode($a, JSON_UNESCAPED_UNICODE);

                                if($ss->sendMessage($ss->clients[$receiverIndex]['csock'], $json) === FALSE)
                                    $ss->logs("Cannot send a message to : ".$ss->clients[$receiverIndex]['user_id']);
                            }
                        }
                    }
                }
            }

            sleep(1); 
        }

        $ss->closeServerSocket();
    }
    else{
        $ss->logs("Cannot Start Socket Server");
    }

?>

您可以在浏览器上运行run.socket.server.php。然后它将无限循环。 并且,创建了DOCUMENT_ROOT / socket.server.logs.txt。请在socket.server.logs.txt文件中检查服务器是否已成功启动。

继续运行以下客户端文件:

socket.client.php
<?php
//The Client
error_reporting(E_ALL);

function logs($handle, $log){
    $log = date("Y-m-d H:i:s")."\r\n".$log."\r\n\r\n";
    fwrite($handle, $log);
}

$handle = fopen($_SERVER['DOCUMENT_ROOT']."/client.txt", "a+");

logs($handle, "============= STARTED ==============");

$address = "10.76.15.2";
$port = 6666;

/* Create a TCP/IP socket. */
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
 if ($socket === false) {
    logs($handle, "socket_create() failed: reason: " . socket_strerror(socket_last_error()));
     echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
 } else {
    logs($handle,  "socket successfully created.\n");
     echo "socket successfully created.\n";
 }

logs($handle,  "Attempting to connect to '$address' on port '$port'...");
echo "Attempting to connect to '$address' on port '$port'...";
$result = socket_connect($socket, $address, $port);
 if ($result === false) {
    logs($handle,  "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)));
     echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
 } else {
    logs($handle,  "successfully connected to $address.");
     echo "successfully connected to $address.\n";
 }

 $a = array();
 $a['user_id'] = "12345678901234567x";
 $a['user_type'] = "9";
 $json = json_encode($a, JSON_UNESCAPED_UNICODE);

 $sent = socket_write($socket, $json."\n\r", strlen($json."\n\r"));
 logs($handle,  "Writing resutls: $sent");

 $input = socket_read($socket, 2048);
 logs($handle,  "Response from server is: $input");
 echo "Response from server is: $input\n";

//echo "Closing socket...";
//socket_close($socket);
//logs($handle, "============= FINISHED ==============");
?> 

然后,您可以看到一些结果文本。 在浏览器上运行客户端文件后,请参阅socket.server.logs.txt 嗯,目前,客户端已连接但未断开连接。 顺便说一句,在服务器上,日志文件被记录为已断开连接。 所以我认为。 可能是我错过了检测客户端断开连接的时间? 我认为socket.server.php readMessage()功能运行不正常。 我怎么能正确地知道要断开连接?

0 个答案:

没有答案