server에서는 먼저 server의 pid를 출력한 뒤, 클라이언트로부터 전송되는 SIGUSR1, SIGUSR2 시그널을 받아 10진수로 변환한 뒤 이 문자열을 출력하는 역할을 한다.
클라이언트로부터 서버로 오는 SIGUSR1과 SIGUSR2 시그널을 이용해 이 시그널을 2진수로 가정하고, 이를 10진수로 변환하여 클라이언트로부터 수신한 문자를 출력해야 한다.
우리는 시그널을 다룰 시그널 핸들러를 사용해야 하는데, 자동적으로 정의된 default 핸들러를 사용해도 된다. 혹은 아래처럼 받은 시그널을 2진수로 가정하고, 이 2진수를 10진수로 변환하여 문자열을 출력하는 핸들러 등 사용자 정의 핸들러를 이용할 수도 있다.
void serverhandler(int signum, siginfo_t *siginfo, void *context)
{
(void)siginfo;
(void)context;
binary_to_decimal(signum == SIGUSR2, siginfo->si_pid);
}
이렇듯 서버에서는 받은 2진수(SIGUSR1, SIGUSR2)를 10진수로 바꾸는 binaryToDecimal 함수를 호출하는 핸들러를 구현할 수 있다. binaryToDecimal은 받은 2진수를 이용해 8bit의 2진수를 1개의 char형 문자로 변환할 수 있다. 즉, 시그널이 총 8개가 들어오면 8번의 binaryToDecimal이 호출되어 1개의 char가 만들어지는 것이다.
binaryToDecimal은 항상 0 또는 1이 인자로 들어가야 하므로, 인자로 signum == SIGUSR2를 넣어준다. 우리는 SIGUSR1을 0, SIGUSR2를 1로 가정하기로 했으므로 위의 signum == SIGUSR2로 해주면 0, 1로 처리하는 게 가능해진다. 또, pid를 인자로 받아 한 글자가 출력되면 client로 하나의 신호를 보내준다.
int main(void)
{
struct sigaction catch;
ft_putstr_fd("Server PID : ", 1);
ft_putnbr_fd(getpid(), 1);
write(1, "\\n", 2);
catch.sa_flags = SA_SIGINFO;
catch.sa_sigaction = serverhandler;
if (sigaction(SIGUSR1, &catch, NULL) == -1)
exit(1);
if (sigaction(SIGUSR2, &catch, NULL) == -1)
exit(1);
while (1)
pause();
return (0);
}
catch라는 sigaction 구조체가 있고, 이 구조체의 sa_flags에 SA_SIGINFO를 넣어준다.
signal()함수만 쓰면 서버에서 클라이언트로 시그널을 주지 못하기 때문에(signal()만 쓰면 서버는 서버로 전송된 시그널을 누가 보냈는지 알지 못한다. sigaction을 쓰면 sigaction의 siginfo 구조체에 송신 pid 정보가 있다) sigaction()함수를 이용한다.
siginfo로 flag가 선언되면 catch.handler 대신 catch.sa_sigaction에 핸들러를 등록한다. siginfo 구조체를 사용하려면 sa_sigaction에 핸들러를 등록해야 하기 때문이다. 더 자세한 내용은 아래에 있다.
sigaction 함수로 SIGUSR1과 SIGUSR2에 siginfo와 핸들러 정보가 담겨 있는 sigaction 구조체를 등록한다. 이 구조체에 정의되어 있는 act로 행동할 것이다. 뒤의 NULL은 old act인데, 신경쓰지 않아도 된다.
sigaction 함수의 리턴값은 등록에 성공했으면 0, 실패했으면 -1을 반환한다. 따라서 -1이 반환되면 프로그램을 종료시킨다.
server가 실행되면, 먼저 서버의 pid를 출력해줘야 한다. 이를 이용해 클라이언트가 서버로 시그널을 전송하기 때문이다.
이후, 시그널에 대한 핸들러를 정의한 뒤 while루프를 돌면서 시그널이 전송될 때까지 대기한다.(pause)
이 함수는 인자로 받은 binary를 8개 모아서 10진수로 변환해 출력하는 함수이다. index와 ch 모두 static으로 선언하여 초기화되지 않도록 했다.