[PHP] Socket통신을 하는 방법


Development note/PHP  2019. 11. 10. 09:00

안녕하세요. 명월입니다.


이 글은 PHP에서 Socket통신을 하는 방법에 대한 글입니다.


PHP는 웹 스크립트 언어이기 때문에 사실 Socket 통신을 할 경우는 거의 없습니다. 웹 프로토콜은 비동기식으로 처리되는 사양이기 때문에 스크립트 안에서 다른 서버와 동기식의 통신을 하게되면 시스템이 매우 느려지게 됩니다.

왜냐하면 동기식의 처리가 전부 완료가 되고 스크립트가 종료가 되야 웹 응답이 나가는 구조이기 때문에 동기처리가 사양이 많거나 느리게 되면 똑같이 응답이 느려지는 결과가 나오기 때문입니다.

참고로 다른 웹 서버의 정보를 취득할 필요가 있다면 소켓 통신이 아닌 cURL를 통해 웹 프로토콜 통신을 하기 때문에 이 Socket은 더욱 사용할 일이 없겠네요.

링크 - [PHP] PHP의 HttpConnection 도구 cURL 사용 방법


그러나 프로젝트는 어떠한 사양이 올지 모르기 때문에 소켓 통신을 구현할 경우도 있습니다.

먼저 PHP에서 소켓을 사용하기 위해서는 php.ini를 수정할 필요가 있습니다.

초기에는 extension=sockets가 (;)로 주석처리가 되어 있는데 이 부분을 제거하면 됩니다.


그리고 PHP에서 소켓 통신을 알아보기 전에 server프로그램을 만들겠습니다.

링크 - [C# 강좌 - 32] 소켓 통신 - 1

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace PHPSocketExample
{
  class Program
  {
    static void Main(string[] args)
    {
      // 소켓 생성하고 Listen까지 만들자
      IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9999);
      using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
      {
        server.Bind(ipep);
        server.Listen(20);
        Console.WriteLine("Server Start... Listen port 9999...");
        while (true)
        {
          // 클라이언트가 접속되었다.
          using (Socket client = server.Accept())
          {
            IPEndPoint ip = (IPEndPoint)client.RemoteEndPoint;
            Console.WriteLine($"Address - {ip.Address} ");
            String message = "Welcome Socket Server";
            Byte[] data = Encoding.Default.GetBytes(message);
            // 클라이언트로 메시지를 보낸다.
            client.Send(data);
            data = new Byte[1024];
            // 클라이언트로 메시지를 받는다.
            client.Receive(data);
            message = Encoding.Default.GetString(data);
            Console.WriteLine(message);
          }
        }   
      }
    }
  }
}

이제 PHP에서 위 서버를 접속하는 소스를 작성하겠습니다.

링크 - https://www.php.net/manual/en/book.sockets.php

<?php
  $address = '127.0.0.1';
  $port = 9999;
  try{
    // 소켓 오브젝트를 만듭니다.
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if ($socket === false) {
      echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    }
    // 접속을 합니다.
    $result = socket_connect($socket, $address, $port);
    if ($result === false) {
      echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
    } 
    // 소켓 서버로부터 메시지를 받는다.
    $out = socket_read($socket, 1024);
    // 소켓 서버로 메시지를 보낸다.
    $in = "hello socket server!!";
    socket_write($socket, $in, strlen($in));
  }finally{
    socket_close($socket);
  }
?>
<!DOCTYPE html>
<html>
<head>
  <title>title</title>
</head>
<body>
  <?=$out?>
</body>
</html>

먼저 서버인 C# 콘솔 프로그램을 실행합니다.

그리고 php의 웹 사이트를 접속합니다.

웹 페이지의 응답은 일단 Welcome Socket Server이라는 메시지가 왔습니다. 이 메시지는 소켓 서버로 부터 온 메시지입니다.

소켓 서버로 부터 클라인언트로부터 메시지를 받고 console out을 했네요.


PHP에서도 소켓 접속되는 것을 확인했습니다.


제가 이 글을 쓰면서 메뉴얼에 PHP에서 클라이언트 접속 뿐아니라 서버도 만들 수 있네요.

소켓 서버를 만들 수 있다는 뜻은 그럼 웹 요청에 따른 쓰레드를 분기할 수 있다라는 소리가 되는 것 같은데..

그걸 판단하는 이유는 소켓 서버가 닫히지 않는 이상 웹 응답을 하지 않을 것입니다. 브라우저에서 요청 timeout이 넘어서면 커넥션을 강제 종료를 하기 때문에 그럼 web server의 세션은 강제 종료됩니다. 그 때 아마 소켓 서버도 강제 종료가 될 것입니다.

그런 이유로 PHP의 스크립트 환경에서 쓰레드를 생성할 수 있다는 것인데 그럼 쓰레드의 메모리 해제는 어떻게 할까라는 의문이 생깁니다. PHP는 Java와 다르게 GC, 즉 가비지 컬렉션이 없기 때문에 이런 식의 코드 작성은 분명 memory leak이 발생활 확율이 높을 듯 싶습니다. 아마 PHP에 초기 설치할 때, safe thread와 non-safe thread 버전이 있었는 걸로 기억하는데..


메모리 해제는 그렇다치고 PHP에서 세션에 상관없이 class를 싱글톤 형식으로 선언이 가능하다는 소리인데.. 그럼 이 뜻은 PHP가 값 참조 구조가 아닌 메모리 참조 구조로도 생성이 가능하다는 소리인데..

솔직히 저도 잘 모르겠네요.. 소켓 서버 하나 조사한 걸로 PHP의 개념이 흔들리는 듯한... 아시는 분은 댓글 부탁드립니다. 조사를 더해야 겠습니다.


솔직히 PHP로 서버를 만드는 건 리스크가 너무 클 것 같습니다. 차라리 Tomcat을 이용한 소켓 서버가 더 효율적일 지도 모르곘네요.

링크 - [Java] Tomcat 서버에서 소켓 서버를 만드는 방법

링크 - [Java] Tomcat의 websocket을 이용해서 socket서버 만들기


여기까지 PHP에서 Socket통신을 하는 방법에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.