이 기사의 예에서는 웹소켓을 기반으로 PHP로 간단한 채팅방을 구축하는 방법을 설명합니다. 참고할 수 있도록 모든 사람과 공유하세요. 세부 내용은 다음과 같습니다.
1. 서문
회사 게임에 간단한 채팅방이 있다는 걸 알고나서 node+websocket으로 만든 줄 알았더니 PHP를 이용해 간단한 채팅방을 만들어볼까 하는 생각이 들었습니다. 그래서 다양한 정보를 수집하고, 문서를 읽고, 사례를 찾아보고, 간단한 채팅방을 직접 작성했습니다.
http 연결은 짧은 연결과 긴 연결로 구분됩니다. 짧은 연결은 일반적으로 ajax를 사용하여 구현할 수 있으며 긴 연결은 웹소켓입니다. 짧은 연결은 비교적 구현이 간단하지만 너무 많은 리소스를 소비합니다. Websocket은 효율적이지만 호환성에 몇 가지 문제가 있습니다. websocket은 html5의 리소스입니다
2. 프런트엔드
웹소켓의 프런트엔드 구현은 매우 간단하고 간단합니다
//连接websocket var ws = new WebSocket("ws://127.0.0.1:8000"); //成功连接websoc的时候 ws.onopen = function(){} //成功获取服务端输出的消息 ws.onmessage = function(e){} //连接错误的时候 ws.onerror = function(){} //向服务端发送数据 ws.send();
3. 백스테이지
웹소켓의 어려움은 주로 그 배경에 있습니다
3.1웹소켓 연결과정
웹소켓 통신 다이어그램 이것은 클라이언트와 서버 간의 간단한 통신 다이어그램입니다. PHP가 주로 하는 일은 암호화 키를 수락하고 이를 반환하여 소켓 생성 및 핸드셰이크 작업을 완료하는 것입니다.
아래 사진은 웹소켓을 처리하는 서버의 세부 흐름도입니다
3.2 코드 연습
서버에서 수행되는 프로세스는 대략 다음과 같습니다.
다음은 샘플 코드입니다(클래스를 작성했기 때문에 코드가 기능별로 나누어져 있습니다). 텍스트 하단에는 github 주소와 제가 겪은 몇 가지 함정이 나와 있습니다.
1. 먼저 소켓을 생성합니다
//建立套接字 public function createSocket($address,$port) { //创建一个套接字 $socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //设置套接字选项 socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); //绑定IP地址和端口 socket_bind($socket,$address,$port); //监听套接字 socket_listen($socket); return $socket; }
2. 소켓을 배열에 넣습니다
public function __construct($address,$port) { //建立套接字 $this->soc=$this->createSocket($address,$port); $this->socs=array($this->soc); }
3. 정지된 프로세스가 소켓 배열을 순회하며 여기서 주요 작업이 완료됩니다
public function run(){ //挂起进程 while(true){ $arr=$this->socs; $write=$except=NULL; //接收套接字数字 监听他们的状态 socket_select($arr,$write,$except, NULL); //遍历套接字数组 foreach($arr as $k=>$v){ //如果是新建立的套接字返回一个有效的 套接字资源 if($this->soc == $v){ $client=socket_accept($this->soc); if($client <0){ echo "socket_accept() failed"; }else{ // array_push($this->socs,$client); // unset($this[]); //将有效的套接字资源放到套接字数组 $this->socs[]=$client; } }else{ //从已连接的socket接收数据 返回的是从socket中接收的字节数 $byte=socket_recv($v, $buff,20480, 0); //如果接收的字节是0 if($byte<7) continue; //判断有没有握手没有握手则进行握手,如果握手了 则进行处理 if(!$this->hand[(int)$client]){ //进行握手操作 $this->hands($client,$buff,$v); }else{ //处理数据操作 $mess=$this->decodeData($buff); //发送数据 $this->send($mess,$v); } } } } }
4. 핸드셰이크 프로세스는 웹소켓 콘텐츠를 수신하고 Sec-WebSocket-Key에서 키를 얻은 다음 암호화 알고리즘을 통해 이를 버퍼에 쓰는 것입니다(자동 확인에는 처리가 필요하지 않습니다. )
public function hands($client,$buff,$v) { //提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key) $buf = substr($buff,strpos($buff,'Sec-WebSocket-Key:')+18); //去除换行空格字符 $key = trim(substr($buf,0,strpos($buf,"\r\n"))); //固定的加密算法 $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); $new_message = "HTTP/1.1 101 Switching Protocols\r\n"; $new_message .= "Upgrade: websocket\r\n"; $new_message .= "Sec-WebSocket-Version: 13\r\n"; $new_message .= "Connection: Upgrade\r\n"; $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n"; //将套接字写入缓冲区 socket_write($v,$new_message,strlen($new_message)); // socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0))); //标记此套接字握手成功 $this->hand[(int)$client]=true; }
5. 클라이언트 데이터를 구문 분석합니다. (여기에서는 암호화하지 않았습니다. 필요한 경우 직접 암호화할 수 있습니다.)
//解析数据 public function decodeData($buff) { //$buff 解析数据帧 $mask = array(); $data = ''; $msg = unpack('H*',$buff); //用unpack函数从二进制将数据解码 $head = substr($msg[1],0,2); if (hexdec($head{1}) === 8) { $data = false; }else if (hexdec($head{1}) === 1){ $mask[] = hexdec(substr($msg[1],4,2)); $mask[] = hexdec(substr($msg[1],6,2)); $mask[] = hexdec(substr($msg[1],8,2)); $mask[] = hexdec(substr($msg[1],10,2)); //遇到的问题 刚连接的时候就发送数据 显示 state connecting $s = 12; $e = strlen($msg[1])-2; $n = 0; for ($i=$s; $i<= $e; $i+= 2) { $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2))); $n++; } //发送数据到客户端 //如果长度大于125 将数据分块 $block=str_split($data,125); $mess=array( 'mess'=>$block[0], ); return $mess; }
6. 버퍼에 소켓 쓰기
//发送数据 public function send($mess,$v) { //遍历套接字数组 成功握手的 进行数据群发 foreach ($this->socs as $keys => $values) { //用系统分配的套接字资源id作为用户昵称 $mess['name']="Tourist's socket:{$v}"; $str=json_encode($mess); $writes ="\x81".chr(strlen($str)).$str; // ob_flush(); // flush(); // sleep(3); if($this->hand[(int)$values]) socket_write($values,$writes,strlen($writes)); } }
7. 운영방법
github 주소 git@github.com:rsaLive/websocket.git
①server.php를 콘솔에서 실행하는 것이 가장 좋습니다
server.php 스크립트 디렉터리로 이동합니다(먼저 php -v를 실행하여 php가 구성되어 있는지 확인할 수 있습니다. Linux 구성이 없으면 bash 창에서 경로를 구성하세요)
php -f 서버.php
오류가 있으면 메시지가 표시됩니다
②서버를 통해 html 파일에 접근
8. 밟힌 함정에 대해서는 디버깅을 열어 오류를 확인하세요
①server.php는 중단된 프로세스에서 출력을 인쇄할 수 있습니다. 문제가 발생하면 코드에 인쇄를 추가하여 디버깅할 수 있습니다
각 판단을 표시하고 코드가 어느 섹션에서 실행되고 있는지 콘솔에서 확인할 수 있습니다
단, 코드를 수정한 후에는 매번 php server.php 스크립트를 다시 실행해야 합니다
②이 오류가 발생하면
1. 서버와 소켓 초기화 시 데이터를 보냅니다. (서버와의 첫 번째 확인 핸드셰이크 중에는 콘텐츠를 보낼 수 없습니다.)
2. 확인되었으나 클라이언트가 전송하지 않았거나 보낸 메시지가 비어 있는 경우에도 마찬가지입니다
그럼 연결된 소켓의 데이터를 확인해보세요
③ 브라우저가 지원하지 않거나 서버가 소켓을 열지 않을 수도 있습니다. 시작하기 전에 확인하는 것이 가장 좋습니다
if (window.WebSocket){ console.log("This browser supports WebSocket!"); } else { console.log("This browser does not support WebSocket."); }
위 글의 내용은 모두의 공부에 도움이 되길 바랍니다.