어떻게 PHP websocket 으로 웹 페이지 의 실시 간 채 팅 을 실현 합 니까?

머리말
최근 에 어렵 게 시간 을 내 서 예전 에 했 던 websocket'요청-그대로 돌아 가기'서버 를 완 선 했 습 니 다.js 로 클 라 이언 트 기능 을 보완 하고 과정 과 방향 을 공유 하 는 동시에 websocket 과 관련 된 지식 도 보급 시 켰 습 니 다.물론 지금 은 websocket 에 관 한 글 도 특히 많 습 니 다.이론 적 인 것 도 생략 했 습 니 다.모두 가 선택 하여 읽 을 수 있 도록 참고 문장 을 제시 하 다.
본문 이 시작 되 기 전에 먼저 채 팅 방 의 효과 도 를 붙 입 니 다.(CSS 찌꺼기 페이지 에 신경 쓰 지 마 세 요)

websocket
간단 한 소개
웹 소켓 은 기술 이 아니 라 새로운 프로 토 콜 이다.이것 은 TCP 의 Socket(소켓)을 응용 하여 네트워크 응용 에 새로운 중요 한 능력 을 정의 했다.클 라 이언 트 와 서버 측의 쌍방 향 전송 과 쌍방 향 통신 이다.자바 애플 릿,XML HttpRequest,Adobe Flash,ActiveXObject,각종 Comet 기술 에 이 어 서버 가 클 라 이언 트 메 시 지 를 푸 시 하 는 새로운 추세 입 니 다.
http 와 의 관계
네트워크 계층 에서 websocket 과 http 프로 토 콜 은 모두 응용 층 의 프로 토 콜 입 니 다.tcp 전송 층 을 기반 으로 하지만 websocket 은 연결 을 만 들 때 http 의 101 switch protocol 을 빌려 프로 토 콜 전환(Upgrade)을 이 루 었 습 니 다.HTTP 프로 토 콜 에서 WebSocket 통신 프로 토 콜 로 전환 되 었 습 니 다.이 동작 프로 토 콜 은'악수'라 고 합 니 다.
악수 에 성공 하면 웹 소켓 은 자신의 협의 에 규정된 방식 으로 통신 을 하 며 http 와 관계 가 없다.
악수 하 다.
다음은 제 브 라 우 저 에서 보 낸 전형 적 인 악수 http 머리 입 니 다.

서버 가 악수 요청 을 받 은 후 요청 헤더 에 있 는'Sec-webSocket-Key'필드 를 추출 하여 고정된 문자열 인'258 EAFA5-E914-47DA-95CA-C5AB 0DC 85B 11'을 되 찾 은 다음 에 sha 1 암호 화 를 한 다음 에 base 64 인 코딩 으로 전환 하여 key 로'Sec-webSocket-Accept'필드 로 클 라 이언 트 에 게 되 돌아 갑 니 다.클 라 이언 트 가 이 key 와 일치 하면 연결 이 됩 니 다.악수 완료 하기;
데이터 전송
웹 socket 은 자신 이 규정 한 데이터 전송 형식 이 있 는데 프레임(Frame)이 라 고 부 릅 니 다.아래 그림 은 데이터 프레임 의 구조 이 고 그 중에서 단 위 는 bit 입 니 다.
  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+
구체 적 으로 각 필드 가 무슨 뜻 인지 관심 이 있 으 시 면 이 글 을 보 세 요.The WebSocket Protocol 5.데이터 프레임 은 바 이 너 리 에 대한 조작 이 아직 유연 하지 않 고 자신 이 쓴 알고리즘 분석 데 이 터 를 도전 하지 않 았 습 니 다.아래 의 데이터 프레임 분석 과 패 키 징 은 모두 인터넷 의 알고리즘 을 사용 합 니 다.
그러나 저 는 업무 중 에 결제 게 이 트 웨 이 를 쓸 때 데이터 의 진법 작업 을 자주 사용 합 니 다.이것 은 반드시 자세히 연구 하고 정리 해 야 합 니 다.네,먼저 기록 하 세 요.
PHP 웹 소켓 서버 구현
PHP 가 웹 소켓 을 실현 하면 주로 PHP 를 사용 하 는 socket 함수 라 이브 러 리 입 니 다.
PHP 의 socket 함수 라 이브 러 리 는 C 언어의 socket 함수 와 매우 유사 하 며,이전에 APUE 를 한 번 뒤 져 보 았 기 때문에 이해 하기 쉽다 고 생각 합 니 다.PHP 매 뉴 얼 에서 socket 함 수 를 한 번 보면 여러분 도 phop 의 socket 프로 그래 밍 에 대해 어느 정도 알 수 있 을 것 이 라 고 생각 합 니 다.
다음은 코드 에서 사용 하 는 함수 에 대해 간단 한 설명 을 할 것 입 니 다.
파일 설명자
갑자기'파일 설명자'를 언급 하면 이상 할 수도 있 습 니 다.
그러나 서버 로 서 연 결 된 socket 을 저장 하고 식별 해 야 합 니 다.모든 socket 은 한 사용 자 를 대표 합 니 다.사용자 정보 와 socket 의 대응 을 어떻게 연결 하고 조회 하 느 냐 가 문제 입 니 다.여기 서 파일 설명자 에 관 한 작은 기 교 를 사용 합 니 다.
우 리 는 Liux 가'만물 이 파일'이라는 것 을 알 고 있 습 니 다.C 언어의 socket 의 실현 은 바로 하나의'파일 설명자'입 니 다.이 파일 설명 자 는 보통 파일 을 여 는 순서 로 증가 하 는 int 수치 입 니 다.0 에서 계속 증가 합 니 다(물론 시스템 은 제한 이 있 습 니 다).모든 socket 은 하나의 파일 에 대응 하고 읽 기와 쓰기 socket 은 동작 에 대응 하 는 파일 이기 때문에 파일 시스템 처럼 read 와 write 함 수 를 응용 할 수 있 습 니 다.
tips:linux 에서 표준 입력 은 파일 설명자 0 입 니 다.표준 출력 에 대응 하 는 파일 설명 자 는 1 입 니 다.표준 오류 에 대응 하 는 파일 설명 자 는 2 입 니 다.그래서 우 리 는 012 쌍 의 입 출력 을 사용 하여 방향 을 바 꿀 수 있다.
그러면 C socket 과 유사 한 PHP socket 도 자 연 스 럽 게 이 점 을 계승 하 였 으 며,만 든 socket 도 int 값 이 45 와 같은 자원 형식 입 니 다.저 희 는(int)또는 intval()함 수 를 사용 하여 socket 을 유일한 ID 로 변환 하여 socket 자원 과 대응 하 는 사용자 정 보 를'클래스 색인 배열'로 저장 할 수 있 습 니 다.
결과 유사:

$connected_sockets = array(
    (int)$socket => array(
        'resource' => $socket,
        'name' => $name,
        'ip' => $ip,
        'port' => $port,
        ...
    )
)
서버 소켓 만 들 기
다음은 서버 socket 을 만 드 는 코드 입 니 다:

//      TCP socket,                    ,      
$this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//   IP     ,               ;
socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
//  IP         socket ;
socket_bind($this->master, $host, $port);
// listen                  ,    socket      socket   ,         。              socket     ,       ,          ,            。
socket_listen($this->master, self::LISTEN_SOCKET_NUM);
이렇게 하면,우 리 는 서버 socket 을 얻 을 수 있 습 니 다.클 라 이언 트 가 이 socket 에 연결 되 었 을 때,그것 은 읽 을 수 있 는 상태 로 바 뀔 것 입 니 다.그러면 다음 서버 의 처리 논 리 를 보십시오.
서버 논리
여기 서 다시 한 번 socketselect($read, $write, $except, $tv_sec [, $tv_usec]):
select 함 수 는 전통 적 인 select 모델 을 사용 합 니 다.읽 기,쓰기,이상 한 socket 은 각각$socket,$write,$except 배열 에 넣 고 상태 가 변 경 된 socket 의 수 를 되 돌려 줍 니 다.오류 가 발생 하면 함 수 는 false 로 돌아 갑 니 다.
주의해 야 할 것 은 마지막 두 시간 매개 변수 입 니 다.단위 만 다 르 고 조합 해서 사용 할 수 있 습 니 다.socket 을 표시 합 니 다.select 가 막 힌 시간 이 0 일 때 이 함 수 는 즉시 돌아 와 폴 링 메커니즘 에 사용 할 수 있 습 니 다.NULL 을 위 한 함수 가 계속 막 힙 니 다.여기에$tv 를 설치 합 니 다.sec 인 자 는 작 동 가능 한 socket 이 돌아 올 때 까지 null 입 니 다.
다음은 서버 의 주요 논리 입 니 다.

$write = $except = NULL;
$sockets = array_column($this->sockets, 'resource'); //        socket   
$read_num = socket_select($sockets, $write, $except, NULL);

foreach ($sockets as $socket) {
        //           socket,        ;            
        if ($socket == $this->master) {
            socket_accept($this->master);
            // socket_accept()       “   listen   socket(        socket )”    ,        socket,       false;
             self::connect($client);
             continue;
            }
        //             socket ,      ,       
        } else {
            //    socket_recv()   socket        len      ,     $buffer  。
            $bytes = @socket_recv($socket, $buffer, 2048, 0);

            if ($bytes < 9) {
                //          ,          8        (        ,8                    ),         ,            
                $recv_msg = $this->disconnect($socket);
            } else {
                //           ,      
                if (!$this->sockets[(int)$socket]['handshake']) {
                    self::handShake($socket, $buffer);
                    continue;
                } else {
                    $recv_msg = self::parse($buffer);
                }
            }

            //     
            $this->broadcast($msg);
        }
    }
}
여 기 는 서버 에서 메 시 지 를 처리 하 는 기본 코드 일 뿐 입 니 다.로그 기록 과 이상 처 리 는 모두 생략 되 었 습 니 다.그리고 데이터 프레임 분석 과 패 키 징 방법 도 있 습 니 다.여러분 도 사랑 을 볼 필요 가 없습니다.관심 이 있 으 시 면 github 에 가서 제 소스 코드 를 지원 하 세 요~
그 밖 에 서버 와 클 라 이언 트 의 상호작용 을 편리 하 게 하기 위해 저 는 json 형식의 메시지 형식 을 정 의 했 습 니 다.

$msg = [
    'type' => $msg_type, //      ,     ,     
    'from' => $msg_resource, //     
    'content' => $msg_content, //     
    'user_list' => $uname_list, //              
    ];
클 라 이언 트
클 라 이언 트 만 들 기
전단 에서 우 리 는 js 를 사용 하여 Websocket 방법 을 호출 하면 간단하게 웹 socket 연결 을 만 들 수 있 습 니 다.서버 는 우리 에 게 연결,악수 작업 을 완성 해 줄 것 입 니 다.js 는 이벤트 체 제 를 사용 하여 브 라 우 저 와 서버 의 상호작용 을 처리 합 니 다.

//      websocket   
var ws = new WebSocket("ws://127.0.0.1:8080");

// websocket       
ws.onopen = function () {
};

// websocket        
ws.onmessage = function (e) {
    var msg = JSON.parse(e.data);
}

// websocket     
ws.onerror = function () {
};
메 시 지 를 보 내 는 것 도 간단 하 다.뉴스 send(msg)방법 을 직접 호출 하면 된다.
페이지 기능
페이지 부분 은 주로 사용 자 를 편리 하 게 하 는 것 입 니 다.여기 서 메시지 상자 textarea 에 키보드 모니터링 사건 을 추가 하여 사용자 가 Enter 키 를 눌 렀 을 때 직접 메 시 지 를 보 냅 니 다.

function confirm(event) {
    var key_num = event.keyCode;
    if (13 == key_num) {
        send();
    } else {
        return false;
    }
}
그리고 사용자 가 클 라 이언 트 를 열 때 기본 사용자 이름 을 만 듭 니 다.
그 다음 에 데이터 에 대한 분석 구조 로 클 라 이언 트 페이지 의 업데이트 에 대해 여 기 는 더 이상 잔소리 하지 않 고 관심 이 있 는 것 은 소스 코드 를 볼 수 있 습 니 다.
사용자 이름 비동기 처리
사용자 가 로그 인 할 때 사용자 이름 을 확인 하 는 작은 문 제 를 제기 해 야 합 니 다.저 는 클 라 이언 트 가 연결 을 만 든 후에 사용자 이름 을 서버 에 직접 보 내 려 고 했 지만 콘 솔 에서'웹 소켓 이 연결 중이 거나 닫 혔 습 니 다'라 는 오류 메 시 지 를 보 냈 습 니 다.
Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
연결 이 처리 되 지 않 았 을 수도 있다 는 점 을 감안 하여 저 는 sleep 방법 을 1 초 만 기 다 렸 다가 사용자 이름 을 보 냈 지만 오류 가 여전히 존재 합 니 다.
나중에 갑자기 js 의 단일 스 레 드 차단 체 제 를 생각 하고 sleep 를 사용 하 는 것 도 소 용이 없다 는 것 을 알 게 되 었 습 니 다.js 의 사건 체 제 를 잘 이용 하 는 것 이 정도 입 니 다.그래서 서버 에서 논 리 를 추가 하고 악수 에 성공 한 후에 클 라 이언 트 에 게 악수 가 성공 했다 는 메 시 지 를 보 냅 니 다.클 라 이언 트 는 먼저 사용자 이름 을 전역 변수 에 저장 하고 서버 의 악수 성공 알림 메 시 지 를 받 은 후에 사용자 이름 을 보 내 서 첫 번 째 시간 에 사용자 이름 을 업데이트 하 는 데 성공 했다.
총결산
채 팅 방 확장 방향
간이 채 팅 방 은 이미 완성 되 었 으 니,당연히 희망 을 가 진 아름 다운 미래 를 주어 야 하 며,누군가가 실현 하 기 를 바란다.
페이지 미화(정보 색상 추가 등)
  • 서버 는'@'문 자 를 식별 하고 특정한 socket 에 만 데 이 터 를 써 서 채 팅 방 의 개인 채 팅 을 실현 합 니 다
  • 다 중 프로 세 스(redis 등 캐 시 데이터 베 이 스 를 사용 하여 자원 공 유 를 실현)
  • 메시지 기록 데이터베이스 지속 화(log 로 그 는 분석 하기 불편 함)
  • 이상 은 어떻게 PHP websocket 으로 웹 페이지 의 실시 간 채 팅 을 실현 하 는 지 에 대한 상세 한 내용 입 니 다.더 많은 PHP websocket 으로 웹 페이지 의 실시 간 채 팅 을 실현 하 는 데 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기