Perl로 게임 엔진 개발하기: 7부 - 포크

나는 내가 무엇을 하고 있는지 모른다.

처음부터 읽고 싶다면. 확인

지난 게시물에 이어 . 우리는 엔진 텔넷 서버를 분기할 필요가 있는 것을 중단했습니다.

플레이어 2가 게임에 참여했습니다!



텔넷 서버의 수준을 높이고 knify forky를 사용하여 멀티 플레이어로 만들 시간입니다.



strftime 에서 Perl's POSIX module 식별자를 추가하여 출력 시간 스탬프를 찍는 데 도움을 주었습니다. setsid 식별자는 분기된 각 프로세스에 대한 새 세션 및 그룹 ID를 시작하기 위한 것입니다. A.K.A, 자식 프로세스. :sys_wait_h waitpid()을 호출할 때 WNOHANG 플래그를 사용하여 자식 프로세스가 종료된 후 기다리지 않고 반환하기 위한 것입니다. 이는 보류 중인 모든 좀비 자식에 대해 비차단 대기를 제공합니다.

좀비 공격!!!




프로세스가 종료(종료)되면 좀비가 되어 회수해야 합니다. 자식이 중지 또는 종료되었음을 나타내는 waitpid 수신 후 부모 프로세스가 CHLD signal을 호출하면 이 작업이 수행됩니다.

좋습니다. 코드를 분석하는 동안 작업할 수 있는 충분한 정보를 제공하기를 바랍니다.

#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
use POSIX qw(setsid);
use POSIX qw(strftime);
use POSIX ":sys_wait_h";

sub timestamp {
    my $epoc_seconds = time();
    my $time = strftime "%H:%M:%S", localtime($epoc_seconds);
    my $date = strftime "%m/%d/%Y", localtime;
    my $return = $date . " " . $time;
    return ($return);
}

sub logmsg { print timestamp . " -> $0 -> PID:$$: @_ \n" }
logmsg "Begin";

my $socket = new IO::Socket::INET (
    LocalHost => '192.168.1.15',
    LocalPort => '27777',
    Proto => 'tcp',
    Listen => SOMAXCONN,
    ReuseAddr => 1
);

my $waitedpid = 0;
my $player_data;
my $player_socket;

sub REAPER {
    local $!;   # don't let waitpid() overwrite current error
    logmsg "Ending Player's Game";
    while ((my $pid = waitpid(-1, WNOHANG)) > 0 && WIFEXITED($?)) {
        logmsg "Closed Game ID:$pid : WaitPid:$waitedpid : " . ($? ? " with exit $?" : "");
    }
    $SIG{CHLD} = \&REAPER;  # loathe SysV
}
#if we get the CHLD signal call REAPER sub
$SIG{CHLD} = \&REAPER;

logmsg "Ready and waiting for connection";
while(1)
{
    next unless $player_socket = $socket->accept();
    logmsg ("Incomming Connection");
    logmsg ("Spawning Player A Game");
    my $pid = fork();

    next if $pid; #NEXT if $pid exists (parent)

    #As Child
    setsid();
    my $proc = $$;

    logmsg ("Game ID:$proc -> Ready");

    # get information about a newly connected player
    my $player_address = $player_socket->peerhost();
    my $player_port    = $player_socket->peerport();
    logmsg "Game ID:$proc -> Connection from $player_address:$player_port";

    my $response = "Welcome Player: $player_address:$player_port. Press any key to disconnect.";
    $player_socket->send($response);

    while ($player_socket->connected()) {
        $player_socket->recv($player_data, 1024);
            if ($player_data) {
                logmsg "Player Disconnecting $player_address : $player_port";
                $socket->close();
                logmsg "Player Disconnected";
                last;
            }
    }
    last;
}
exit;


이 코드를 실행하고 SyncTERM을 통해 두 명의 플레이어와 연결하면 다음이 표시됩니다.

localhost:~/ANSIGameEngine # perl forking_telnet_server.pl 
12/03/2021 18:16:58 -> forking_telnet_server.pl -> PID:15978: Begin 
12/03/2021 18:16:58 -> forking_telnet_server.pl -> PID:15978: Ready and waiting for connection 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15978: Incomming Connection 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15978: Spawning Player A Game 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15979: Game ID:15979 -> Ready 
12/03/2021 18:17:04 -> forking_telnet_server.pl -> PID:15979: Game ID:15979 -> Connection from 192.168.1.9:33422 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15978: Incomming Connection 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15978: Spawning Player A Game 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15980: Game ID:15980 -> Ready 
12/03/2021 18:17:08 -> forking_telnet_server.pl -> PID:15980: Game ID:15980 -> Connection from 192.168.1.9:33428 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15979: Player Disconnecting 192.168.1.9 : 33422 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15979: Player Disconnected 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15978: Ending Player's Game 
12/03/2021 18:17:11 -> forking_telnet_server.pl -> PID:15978: Closed Game ID:15979 : WaitPid:0 :  
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15980: Player Disconnecting 192.168.1.9 : 33428 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15980: Player Disconnected 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15978: Ending Player's Game 
12/03/2021 18:17:13 -> forking_telnet_server.pl -> PID:15978: Closed Game ID:15980 : WaitPid:0 :  




작동 원리



새로 들어오는 텔넷 요청을 수락하는 기본(상위) 프로세스는 위의 예에서 PID:15978입니다. 수신 서버를 설정한 후 연결 요청을 기다리고 새 플레이어(자식)가 연결되면 분기 프로세스를 생성합니다. 이 코드는 값 fork() 이 반환되는 자식(플레이어) 프로세스와 부모(주 대기 텔넷 서버) 프로세스를 구분합니다. 상위 프로세스는 fork() 의 반환 값으로 자식(플레이어) PID를 수신하므로 루프 백업하고 다른 플레이어가 연결될 때까지 기다립니다. 하위(플레이어) 프로세스는 0 에서 fork() 의 값을 수신하므로 코드에서 계속 아래쪽으로 이동합니다. Perl에서 if($pid)TRUE if $pid == (0 || undef)를 평가하지 않습니다. 이는 자식(플레이어) 프로세스가 fork()에서 반환된 값으로 받을 것입니다. 자식(플레이어) 프로세스에 새 세션을 제공하고 PID( $$ )를 기록하고 아무 키나 누를 때까지 기다립니다. 플레이어가 키를 누르면 소켓이 닫히고 자식(플레이어) 프로세스가 존재하며 좀비가 됩니다. 이는 상위(주) 프로세스가 CHLD 신호( $SIG{CHLD} )를 수신하고 REAPER를 호출할 때입니다.

당신은 어떤가요?



이전에 포크로 작업한 적이 있습니까? 거두는 것을 잊고 좀비 아포칼립스를 일으켰습니까? 귀하의 경험에 대해 의견을 말하십시오. 귀하의 이야기를 듣고 싶습니다.

제안이나 의견이 있으면 건설적으로 공유하십시오. 또한 소셜 미디어 페이지를 방문하여 게임 엔진이 작동하는 모습을 보여주는 재미있는 동영상과 사진을 많이 확인하세요.

ANSI Game Engine on Facebook


건배!

좋은 웹페이지 즐겨찾기