본문 바로가기
코드이그나이터와 php7와 mysql

윈도우 + PHP 8 ( 3편, 웹소켓 통신 래칫 Ratchet )

안녕하세요. 개발 기술을 익혀가며 공유해 나가는 크레이입니다 :)

지난 시간, MYSQL 에 한글과 이모티콘을 저장, 출력하는 샘플을 다뤘었는데요.
https://itadventure.tistory.com/628

오늘은 윈도우 환경의 PHP8에서 웹소켓을 다뤄보겠습니다.
웹소켓은 웹 기반에서 실시간 통신을 하는 기술입니다.
채팅방은 물론이고 오목이나 바둑같은 실시간 대전게임에서도 활용 가능한데요.
node.js 에서 주로 쓰는 기술이지만 php에서도 가능하더라구요.

그럼 알아보러 갈까요? 레츠 고우~


컴포저 설치는 기본

php 확장 기능 대부분은 컴포저(Composer)라는 걸 먼저 설치해야 사용할 수 있습니다.
컴포저가 뭐냐구요? 간단하게 표현하자면 확장 기능 인스톨러(설치 프로그램)인데요.
정확한 말로는 PHP 의존성 관리자(A Dependency Manager for PHP)라고 합니다.
뭐.. 어려운 말은 외우지 않으셔도 됩니다 :)

컴포저 공식 사이트에서 설치 프로그램을 다운받을 수 있는데요.
아래 사이트에 접속하신 다음 Download (다운로드) 버튼을 눌러주시면 됩니다.

https://getcomposer.org/

아래 화면에서는 보통 Install for all users ( recommended ) 모든 유저에게 설치(추천) 을 선택하시구요.

대부분은 Next (다음) 버튼을 눌러서 넘기되 아래 화면이 등장하면 Next  버튼이 눌려지지 않을 겁니다.
그 때는 Add this PHP to your path ( 이 PHP 를 귀하의 path 에 추가 ) 를 체크해야 
Next 버튼이 활성화됩니다.

마지막 Finish 버튼을 누르면 컴포저 설치는 끝-


Ratchet 설치

원래는 오픈스울 ( openswoole ) 이란 걸로 웹소켓을 시도해보려 했는데
윈도우에서 설치되지 않는 문제가 있었습니다.

그래서 또 다른 웹소켓 래칫(Ratchet)을 들고 왔습니다.

XAMPP 의 홈폴더에 래칫을 설치하려면 컴포저로 설치하면 쉬운데요그 전에 먼저 PHP 설정파일에서 압축 모듈활성화해주셔야 합니다.
안그러면 아래와 같은 무시무시한(?) 오류 화면을 만나실 테니까요 :)
( 노란색 부분이 경고 문구입니다 )

XAMPP 컨트롤 패널 창을 열어 ( 지난 게시글에 패널 창을 여는 방법이 기록되어 있습니다. )

Apache 라인의 Config - PHP (php.ini) 메뉴를 선택하세요
그러면 메모장이 열리면서 PHP 설정 사항이 표시됩니다.

그 중 아래 부분을 찾아서,

;extension=zip

아래와 같이 바꿔주시면 됩니다.

extension=zip

뭐가 바뀌었냐구요? 앞의 ; 모양의 기호가 없어졌지 않습니까? 참고로 이 기호(;)를 세미콜론이라 부릅니다.
그러면 zip 이라는 압축기능 extension(확장기능)이 활성화됩니다.
PHP 에서 윈집이나 알집같은 파일 압축/해제 기능을 사용할 수 있는 거지요.
설정을 수정했으면 저장은 필수! 파일 - 저장 메뉴를 선택하여 수정한 기능을 적용해 주세요.

다음으로 아래 순서로 진행해 주세요.
윈도우키 + R 을 눌러주세요. 그러면 '실행'창이 표시될 텐데요.
cmd 를 타이핑해서 입력해 줍니다.

아래와 같은 창이 갑자기 확 튀어 나왔을텐데요. 이 창을 명령 프롬프트라고 합니다.

흥미로운 사실을 하나 알려드리자면 1990년대 시절에는 이런 화면이 컴퓨터 모니터를 가득 메웠고
이 화면 안에서 모든 프로그램을 개발했습니다. 그 당시에는 윈도우 같은게 없었거든요.
크레이도 그 시절를 거쳐와 크레이에게는 매우 친숙한 창입니다 :)

이제 명령 프롬프트 창에서 아래 순서로 래칫(Ratchet) 을 설치하면 됩니다.

첫째, 내 컴퓨터의 홈페이지 기본 폴더로 이동합니다.

cd c:\xampp\htdocs

툴째, 컴포저를 사용하여 래칫을 설치합니다. 컴퓨터 속도에 따라 설치 시간이 소요됩니다. ( 2 ~ 5분 )

composer require cboden/ratchet

위와 같은 화면이 등장하면 설치가 완료된 것입니다.
이 것으로 래칫 설치가 끝났습니다.
그러면 여러분 안녕히~!

독자 한분 : 저기... 어떻게 쓰는지 알려주셔야지요!

아.. 죄송합니다. 샘플 코드를 깜박했군요. 흠흠...


래칫을 사용하는 샘플 코드

웹소켓은 근본적으로 홈페이지 서비스와는 다릅니다. 다를 수 밖에 없는데요.
통신 방식도 다르고 항상 실행상태를 유지하는 서버 코드를 작성하고,
이와 통신할 수 있는 클라이언트 코드를 작성해야 합니다.

하지만 이런 원리를 이해하지 않아도 샘플 코드를 작동하시는데는 무리가 없습니다.
다만 왜 이런 코드를 작성해야 하는지 궁금해 하시는 분들을 위한 하나의 팁입니다 :)

먼저 서버코드를 작성해 주세요.
익숙하신 편집 프로그램 파일을 이용하셔서 아래 이름의 파일로 코드를 복붙해 저장해 주세요.

C:\xampp\htdocs\server.php

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
require __DIR__ . '/vendor/autoload.php';
class WebSocketsServer implements MessageComponentInterface {
    protected $clients;
    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }
    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        echo "New connection! ({$conn->resourceId})\n";
    }
    public function onMessage(ConnectionInterface $from, $msg) {
        foreach ($this->clients as $client) {
            if ($from !== $client) {
                $client->send($msg);
            }
        }
    }
    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }
    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }
}
$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new WebSocketsServer()
        )
    ),
    8089
);
$server->run();

이어서 클라이언트 코드를 아래 파일명으로 코드를 복붙해 저장해 주세요.

해외 소스 코드인데 일부는 한글로 수정하였습니다. 원작자는 공개가 안되어 있더라구요.

C:\xampp\htdocs\client.htm

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
  <title>웹소켓 클라이언트</title>
</head>
<body>
    <div id="wrapper">
        <div id="container">
            <h1>웹소켓 클라이언트</h1>
            <div id="chatLog">
            </div><!-- #chatLog -->
            <p id="examples">채팅 문구를 입력하세요.</p>
            <input id="text" type="text" />
            <button id="disconnect">연결해제</button>
        </div><!-- #container -->
    </div>
    <script>
    $(document).ready(function() {
      if (!("WebSocket" in window)) {
          $('#chatLog, input, button, #examples').fadeOut("fast");
          $('<p>Oh no, you need a browser that supports WebSockets. How about <a href="https://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
      } else {
          //The user has WebSockets 
          connect();
          function connect(){
              var socket;
              var host = "ws://localhost:8089";
              try{
                  var socket = new WebSocket(host);
                  message('<p class="event">소켓 상태: '+socket.readyState);
                  socket.onopen = function(){
                      message('<p class="event">소켓 상태: '+socket.readyState+' (연결)');
                  }
                  socket.onmessage = function(msg){
                 	 message('<p class="message">받은 메시지: '+msg.data);
                  }
                  socket.onclose = function(){
                  	message('<p class="event">소켓 상태 '+socket.readyState+' (끊김)');
                  }			
              } catch(exception){
                 message('<p>오류 '+exception);
              }
              function send(){
                  var text = $('#text').val();
                  if(text==""){
                      message('<p class="warning">메시지를 입력하세요');
                      return ;
                  }
                  try{
                      socket.send(text);
                      message('<p class="event">보낸 메시지 : '+text)
                  } catch(exception){
                     message('<p class="warning">');
                  }
                  $('#text').val("");
              }
              function message(msg){
                $('#chatLog').append(msg+'</p>');
              }
              $('#text').keypress(function(event) {
                  if (event.keyCode == '13') {
                    send();
                  }
              });	
              $('#disconnect').click(function(){
                 socket.close();
              });
          }//End connect 
      }//End else 
    });
    </script>
</body>
</html>

다음으로 XAMPP 에서 아파치(Apache) 서비스를 작동시켜 주세요.
작동중이면 왼쪽 Apache 배경색이 녹색으로 표시되며 정지중이면 Start 버튼을 선택하시면 됩니다.

Apache 서비스 말고도 웹소켓 서버도 별도로 시작해야 하는데요.
웹소켓 서버는 바로 방금 작성한 서버 코드를 말하는 것입니다.

어떻게 시작하냐구요?

명령 프롬프트 창에서 아래 명령어를 타이핑하시면 되는데요
시작 후에 명령 프롬프트 창은 닫지 말고 그대로 두어야 합니다. 닫으면 서버 연결이 끊기거든요.

php server.php

참고로 명령 프롬프트 창을 닫았었다면 명령 프롬프트를 열고 나서 아래 명령어를 먼저 입력해 주셔야 합니다.
그 다음 php server.php 명령을 입력하시면 됩니다.

cd c:\xampp\htdocs

 

이제 웹소켓이 잘 작동하는지 확인해 볼까요?아래 URL 을 복사하신 다음.

http://localhost/client.htm

웹 브라우저를 4개 정도 실행, 주소 창에 해당 URL을 붙여넣기해서 열어 주세요.

그리고 나서 아무 브라우저에서든 채팅 메시지를 입력해 주시면
다른 웹브라우저들에도 메시지가 실시간 표시되는 걸 확인할 수 있습니다.
이걸 이용하면 실시간 채팅방이든 뭐든 실시간으로 작동하는 것을 개발할 수 있지요.
흥미롭지 않으신가요? :)

Ratchet 의 자세한 정보는 아래 사이트(영문)에서 확인해 보실 수 있습니다.
비록 영문이긴 하지만 문서가 꽤 자세히 정리되어 있더라구요.
http://socketo.me/

데모 페이지 : http://socketo.me/demo


- 크레이의 프롤로그 -

10년전에 개인 사이드 프로젝트로 PHP 웹소켓 서버 개발에 도전해 본적이 있었습니다.
아내의 제의에 따라 기독교 채팅방을 만들어 보려는게 목표였었는데요.

마땅한 PHP 라이브리가 없던 시절이라, 천신만고 끝에 채팅방을 개발하긴 했는데 통신이 이유 없이 끊기는 문제는 그 당시 결국 해결을 보지 못했었습니다.
결정적으로 브라우저, 브라우저 버전마다 각기 다른 핸드세이크라는 보안 기술에서 '아, 이건 내가 혼자 할게 아니다.'라고 생각했었지요.

이제 다시 비슷한 꿈을 꿔볼까 생각하는 크레이입니다 :)


다음 게시글 : https://itadventure.tistory.com/631

 

윈도우 + PHP 8 + unity ( 4편, 유니티와 웹소켓하라! )

독자님들 안녕하세요~ 크레이입니다. 이번 시간에는 지난 시간 다루었던 PHP 웹소켓에 이어서 ( 아래 링크 ) https://itadventure.tistory.com/629 유니티3D 게임엔진과 PHP 웹소켓 서버가 소통(통신)하는 법

itadventure.tistory.com