1. Конфигурация Open socket build, в противном случае фатальная ошибка: при вызове неопределенной функции socket_create() будет сообщено об ошибке 1. Откройте конфигурационный файл php.ini и выполните поиск.dll, чтобы удалить предыдущую точку с запятой ‘;’. Перезапустите службу после внесения изменений. Примечание: Если у вас много версий php, важно отметить, какую версию файла php.ini вы хотите изменить. Чтобы открыть сокет с помощью wamp, вам нужен apache и php.ini ниже php, тогда как phpstudy нужно изменить только один php.ini. 2. Проверьте, запущена ли сборка сокета phpinfo.php чтобы убедиться, что если поддержка сокетов => включена, запуск прошел успешно.
3. Установите cmd для запуска php-файла В разделе Мой компьютер – > Свойства -> Дополнительные системные настройки – > Дополнительно – > Переменные среды добавьте путь к php в ПУТЬ к переменной пользователя (обратите внимание, что версия та же) и добавьте путь в переменную среды, как указано выше.
4. Проверьте, успешно ли настроены сокет и php, Создайте новый файл с именем start.php в рамках проекта
if(extension_loaded('sockets')){ echo "1"; }else{ echo "0"; }
Введите PHP d:\phpstudy\wwwstart.php в cmd, если вывод 1, конфигурация правильная, если вывод 0, конфигурация неправильная и нуждается в тщательной перенастройке
2. Процесс внедрения
Интерфейсная реализация относительно проста, сложна и фоновая. Его логика заключается в следующем: php в основном получает ключи шифрования и возвращает их для завершения создания сокета и операции рукопожатия
Процессы на стороне обслуживания: 1. Приостановите процесс сокета сокета и дождитесь подключения 2. Пересеките массив сокетов после подключения сокета 3. Если рукопожатия нет, выполняется операция рукопожатия, и если рукопожатие уже было выполнено, полученные данные будут проанализированы и записаны в буфер для вывода.
3. Интерфейсный код
4. Внутренний код
initSocket(); } // Establish WebSocket Connect private function initSocket() { try { //Establish socket socket $this->_master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // Set up IP And port reuse,Reuse this port after restarting the server; socket_set_option($this->_master, SOL_SOCKET, SO_REUSEADDR, 1); //Binding Address and Port socket_bind($this->_master, $this->_ip, $this->_port); //listen Functions use an active socket interface to become a connected socket interface, which allows a process to accept requests from other processes and become a server process.stay TCP Server programming listen Function to make a process a server and specify that the corresponding socket becomes a passive connection,The stored request is unknown socket Number. socket_listen($this->_master, self::LISTEN_SOCKET_NUM); } catch (Exception $e) { $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage())); } //take socket Save to socket Pool (put socket in array) places current user first by default $this->_socketPool[0] = array('resource' => $this->_master); $pid = getmypid(); $this->debug(array("server: {$this->_master} started,pid: {$pid}")); } // Suspend process traverses the socket array to receive, process, and send data public function run() { // Dead cycle until socket To break off while (true) { try { $write = $except = NULL; // Remove from Array resource column $sockets = array_column($this->_socketPool, 'resource'); /* $sockets Is an array that holds file descriptors. $write Is listening for client write data, incoming NULL is not concerned about write changes $except Is the element in $sockets to swear, passing in null is listening on all The last parameter is the time-out, where 0 immediately ends n>1 and ends at most N seconds later. If a connection has a new dynamic, null is returned in advance. If a connection has a new dynamic, null is returned */ // Receive socket numbers and listen for their status as new messages to or client connections/When disconnected, socket_select Function will return and continue executing $read_num = socket_select($sockets, $write, $except, NULL); if (false === $read_num) { $this->debug(array('socket_select_error', $err_code = socket_last_error(), socket_strerror($err_code))); return; } // Traversing socket arrays foreach ($sockets as $socket) { // If there is a new connection in if ($socket == $this->_master) { // Receive one socket Connect $client = socket_accept($this->_master); if ($client === false) { $this->debug(['socket_accept_error', $err_code = socket_last_error(), socket_strerror($err_code)]); continue; } //Connect and place socket In pool $this->connection($client); } else { //Receive Connected socket Data, returned from socket Number of bytes received in. // The first parameter is the socket resource, the second parameter is the variable that stores the received data, and the third parameter is the length of the received data. $bytes = @socket_recv($socket, $buffer, 2048, 0); // If the number of bytes received is 0 if ($bytes == 0) { // Disconnect $recv_msg = $this->disconnection($socket); } else { // Determine if there is a handshake, no handshake for handshake, handshake for disposal if ($this->_socketPool[(int)$socket]['handShake'] == false) { // Handshake $this->handShake($socket, $buffer); continue; } else { // Resolve data from client $recv_msg = $this->parse($buffer); } } // echo ""; // Business Processing, Assembling Return Client Data Formats $msg = $this->doEvents($socket, $recv_msg); // print_r($msg); socket_getpeername ( $socket , $address ,$port ); $this->debug(array( 'send_success', json_encode($recv_msg), $address, $port )); // Write data returned by the server to the socket $this->broadcast($msg); } } } catch (Exception $e) { $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage())); } } } /** * data broadcasting * @param $data */ private function broadcast($data) { foreach ($this->_socketPool as $socket) { if ($socket['resource'] == $this->_master) { continue; } // Write Socket socket_write($socket['resource'], $data, strlen($data)); } } /** * Business processing, where you can manipulate the database and return client data; assemble data in different formats depending on different types * @param $socket * @param $recv_msg Data from client * @return string */ private function doEvents($socket, $recv_msg) { $msg_type = $recv_msg['type']; $msg_content = $recv_msg['msg']; $response = []; //echo ""; switch ($msg_type) { case 'login': // Landing Online Information $this->_socketPool[(int)$socket]['userInfo'] = array("username" => $msg_content, 'headerimg' => $recv_msg['headerimg'], "login_time" => date("h:i")); // Get the latest name record $user_list = array_column($this->_socketPool, 'userInfo'); $response['type'] = 'login'; $response['msg'] = $msg_content; $response['user_list'] = $user_list; //print_r($response); break; case 'logout': // Exit Information $user_list = array_column($this->_socketPool, 'userInfo'); $response['type'] = 'logout'; $response['user_list'] = $user_list; $response['msg'] = $msg_content; //print_r($response); break; case 'user': // Messages sent $userInfo = $this->_socketPool[(int)$socket]['userInfo']; $response['type'] = 'user'; $response['from'] = $userInfo['username']; $response['msg'] = $msg_content; $response['headerimg'] = $userInfo['headerimg']; //print_r($response); break; } return $this->frame(json_encode($response)); } /** * socket Handshake * @param $socket * @param $buffer Data received by client * @return bool */ public function handShake($socket, $buffer) { $acceptKey = $this->encry($buffer); $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept: " . $acceptKey . "\r\n\r\n"; // take socket Write Buffer socket_write($socket, $upgrade, strlen($upgrade)); // Marker handshake succeeded, next time data is accepted in data frame format $this->_socketPool[(int)$socket]['handShake'] = true; socket_getpeername ( $socket , $address ,$port ); $this->debug(array( 'hand_shake_success', $socket, $address, $port )); //Send a message to inform the client that the handshake was successful $msg = array('type' => 'handShake', 'msg' => 'Successful handshake'); $msg = $this->frame(json_encode($msg)); socket_write($socket, $msg, strlen($msg)); return true; } /** * Frame Data Encapsulation * @param $msg * @return string */ private function frame($msg) { $frame = []; $frame[0] = '81'; $len = strlen($msg); if ($len < 126) { $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); } else if ($len < 65025) { $s = dechex($len); $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s; } else { $s = dechex($len); $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s; } $data = ''; $l = strlen($msg); for ($i = 0; $i < $l; $i++) { $data .= dechex(ord($msg{$i})); } $frame[2] = $data; $data = implode('', $frame); return pack("H*", $data); } /** * Resolve client data * @param $buffer * @return mixed */ private function parse($buffer) { $decoded = ''; $len = ord($buffer[1]) & 127; if ($len === 126) { $masks = substr($buffer, 4, 4); $data = substr($buffer, 8); } else if ($len === 127) { $masks = substr($buffer, 10, 4); $data = substr($buffer, 14); } else { $masks = substr($buffer, 2, 4); $data = substr($buffer, 6); } for ($index = 0; $index < strlen($data); $index++) { $decoded .= $data[$index] ^ $masks[$index % 4]; } return json_decode($decoded, true); } //extract Sec-WebSocket-Key Information and Encryption private function encry($req) { $key = null; if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) { $key = $match[1]; } // encryption return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); } /** * Connect socket * @param $client */ public function connection($client) { socket_getpeername ( $client , $address ,$port ); $info = array( 'resource' => $client, 'userInfo' => '', 'handShake' => false, 'ip' => $address, 'port' => $port, ); $this->_socketPool[(int)$client] = $info; $this->debug(array_merge(['socket_connect'], $info)); } /** * Disconnect * @param $socket * @return array */ public function disconnection($socket) { $recv_msg = array( 'type' => 'logout', 'msg' => @$this->_socketPool[(int)$socket]['userInfo']['username'], ); unset($this->_socketPool[(int)$socket]); return $recv_msg; } /** * Journal * @param array $info */ private function debug(array $info) { $time = date('Y-m-d H:i:s'); array_unshift($info, $time); $info = array_map('json_encode', $info); file_put_contents(self::LOG_PATH . 'websocket_debug.log', implode(' | ', $info) . "\r\n", FILE_APPEND); } } // Extra-class instantiation $sk = new socketServer(); // Function $sk -> run();
5. Запуск php
Создайте файл start.bat, запустите php или запустите PHP, введя команды в cmd
php ./socketServer.php pause
Результаты следующие:
Примечание: start.bat должен быть запущен постоянно. Если он выключен, это означает, что сокет также выключен и не может быть передан. Все, что нужно запустить.bat работает все время
Оригинал: "https://programmer.help/blogs/php-websocket-for-chat-rooms.html"