[42] Minitalk

KURTY·2023년 1월 2일
0

42_SEOUL

목록 보기
6/9

MINITALK

UNIX 신호를 이용해 데이터를 주고받는 작은 프로그램 작성하기


사용 함수 알아보기

signal()

void (*)(int) signal(int sig, void (*handler)(int));
  • 시그널 처리 방법을 설정한다
  • 어떤 시그널들은 미리 정의된 행동을 함으로써 처리되는데, 이처럼 기존에 정의된 행동을 그대로 할 지, 시그널을 그냥 무시할 지, 사용자 정의 행동을 하도록 바꿔줄 지 선택할 수 있다
  • sig는 처리해줄 시그널 번호로, 매크로 SIGABRT, SIGALL, SIGILL, SIGINT, SIGFPE, SIGIO, SIGOTHER, SIGSEGV, SIGTERM, SIGUSR1, SIGUSR2중 하나여야 하며, signal.h에 정의된다.
  • *handler는 시그널을 처리해줄 핸들러 SIG_IGN을 인자로 넘겨주면 해당 시그널을 무시한다
  • 함수포인터를 넘겨주면 시그널이 들어왔을 때 특정 함수를 호출한다.

getpid()

pid_t getpid(void)
  • 현재 프로세스의 아이디를 리턴한다
  • 아이디는 모든 프로세스가 다 고유한 값을 가지기 때문에 임시 파일명을 지을 때 좋음
  • getpid, getppid는 무조건 성공하기 때문에 실패했을 경우 반환하는 값은 존재하지 않음

sleep()

unsigned int sleep(unsigned int seconds);
  • seconds 초만큼 스레드를 일시정지한다. 만약 sleep중간에 시그널이 발생한다면 바로 복귀 후 남은시간이 반환된다.

usleep()

int usleep(useconds_t microseconds);
  • microseconds 마이크로초만큼 스레드를 일시정지한다.

pause()

int pause(void)
  • kill() 함수에 의해 시그널을 받기 전까지 현재 스레드를 일시정지시킨다.
  • pause()를 통해 스레드가 일시정지된 상태에서 시그널 처리기가 멈출 경우 pause()는 반환값을 반환한다.
  • 어떤 경우에서든 무조건 -1을 반환한다.

kill()

int kill(pid_t pid, int sig);
  • 시그널을 전달해준다.
  • pid에 시그널을 받을 프로세스의 id가 들어간다.
    • pid > 0: 해당 pid에 시그널이 보내짐
    • pid == 0: 시그널을 보낸 프로세스와 같은 그룹에 속한 모든 프로세스에 시그널이 보내짐
    • pid == -1:
      • 유저가 관리자 권한을 가지고 있다면 시스템 프로세스와 현재 시그널을 전송하려는 프로세스를 제외한 모든 프로세스에 시그널을 전송
      • 유저가 관리자 권한을 가지고 있지 않다면, 해당 유저와 같은 uid를 가진 프로세스에만 현재 시그널을 전송
  • sig에 해당 프로세스에 보낼 시그널이 들어간다. sig에 0을 넣을 경우 에러 체킹을 시도하며, pid의 유효성을 검사할 수 있다.
  • 이때 인자값으로 들어갈 signal은 위에서 말한 매크로에 이미 정의되어 있어야 함
  • 정상적으로 kill함수가 수행되면 0을 리턴, 에러 발생 시 -1 리턴 및 errno세팅됨.

sigaction

  • SA_SIGINFO

sa_flags에 SA_SIGINFO 플래그를 지정하면 시그널이 발생한 원인을 알 수 있다.
sigaction 구조체에서 시그널 핸들러를 지정할 때 sa_handler 대신 sa_sigaction을 사용한다.
시그널 핸들러는 다음과 같이 인자를 3개 받는 형태로 정의되어 진다.

void handler(int signo, siginfo_t *siginfo, ucontext_t *context);

// signo: 시그널 핸들러를 호출할 시그널 
// siginfo: 시그널이 발생한 원인을 담은 siginfo_t 구조체 포인터
// context: 시그널이 전달될 때 시그널을 받는 프로세스의 내부 상태를 담은 ucontext_t 구조체 포인터

과제 요구사항

Mandatory

  • 클라이언트가 실행될 때에 PID를 표시해야 한다.
  • 서버는 문자열이 수신되면 해당 문자열을 표시해야 한다.
  • 서버와 클라이언트의 통신은 오지 SIGUSR1과 SIGUSR2 두 신호만 사용할 수 있다.
  • 서버는 문자열을 매우 빠른 속도로 표시할 수 있어야 한다.
  • 서버가 재시작 할 필요없이 여러 클라이언트로부터 문자열을 연속으로 수신할 수 있어야 한다.

Bonus

  • 서버와 클라이언트 간의 수신확인 시스템 추가
  • 유니코드 문자 지원

동작 흐름

1. 서버 실행

  • sigaction 구조체를 선언한다.
  • sigaction 구조체에 sa_flags 변수가 존재하는데, 시그널 처리 프로세스의 행위를 수정하는 일련의 플래그들을 명시한다.
    sig_flags 변수에 설정 가능한 플래그는다음과 같다.
    (SA_NOCLDSTOP, SA_ONESHOT, SA_RESETHAND, SA_RESTART, SA_NOMASK/SA_NODEFER, SA_SIGINFO)

Minitalk에서는 SA_SIGINFO 플래그를 설정한다.

struct sigaction sig;
sig.sa_flags = SA_SIGINFO;

SA_SIGINFO 플래그는 시그널 핸들러가 하나가 아닌 3개의 인자를 취할 경우 sa_handler 대신 sa_sigaction의 siginfo_t를 이용한다.

siginfo_t는 다음과 같이 정의된 구조체이다.

siginfo_t {
	int			si_signo; // 시그널 넘버
    int 		si_errno; // errno 값
    int 		si_code; // 시그널 코드
    pid_t 		si_pid; // 프로세스 ID 보내기
    uid_t 		si_uid; // 프로세스를 전송하는 실제 사용자 ID
    int 		si_status; // Exit 값 또는 시그널
    clock_t 	si_utime; // 소모된 사용자 시간
    clock_t 	si_stime; // 소모된 시스템 시간
    sigval_t 	si_value; // 시그널 값
    int 		si_int; // POSIX.1b 시그널
    void 		*si_ptr; // POSIX.1b 시그널
    void 		*si_addr; // 실패를 초래한 메모리 위치
    int 		si_band; // 밴드 이벤트
    int 		si_fd // 파일 디스크럽터
}
  • 설정한 sigaction 구조체를 적용시키기 위해 sigaction() 함수를 호출한다. sigaction() 함수의 원형은 다음과 같다.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

// signum: 발생한 시그널 번호
// act: 시그널 발생 시 처리해주기 위한 설정 값들이 존재하는 sigaction 구조체
// oldact: 이전에 등록되었던 sigaction 구조체 (필요하지 않다면 NULL or 0)
  • 서버는 수신한 시그널에 대해 처리할 핸들러를 지정해주어야 한다.
    sigaction 구조체에는 핸들러를 지정할 수 있는 2개의 함수 포인터가 존재한다.
void (*sa_handler)(int);
void (*sa_handler)(int, siginfo_t *, void *);

위에서 sa_flags 변수에 SA_SIGINFO 플래그를 지정해주었기 때문에 sa_sigaction 포인터 함수에 클라이언트 신호를 처리하기 위한 3개의 인자를 가지는 핸들러 함수를 지정해준다.

sig.sa_sigaction = &ft_handler;
// 3개의 인자를 가지는 함수 ft_handler()을 sig.sa_sigaction에 연결해준다.
  • sigaction 구조체의 sa_mask 변수도 설정해주어야 한다.
    sa_mask 변수는 시그널을 처리하는 동안 블록시킬 시그널을 모아놓은 변수다.
    시그널 블록이란, 운영체제에게 sa_mask에 설정된 시그널을 나중에 처리하도록 예약하는 동작 방식이다.
    따라서 sigemptyset() 함수를 통해 sa_mask 변수안의 시그널들을 비워준다면 모든 시그널이 블록킹되지 않는다.
sigemptyset(&sig.sa_mask);
  • getpid() 함수를 통해 Server PID를 터미널에 출력
  • 클라이언트의 연결 신호 대기 (SIGUSR1, SIGUSR2)

2. 클라이언트 실행 및 서버와 연결

  • 클라이언트 실행 시 2개의 인자 (server pid) (send message) 를 입력하지 않았을 경우 예외처리
  • 유효하지 않은 pid를 입력하였을 경우 예외처리
  • getpid() 함수를 통해 Client PID를 터미널에 출력
  • 서버와 연결하기 위한 모든 준비를 마쳤다면 ft_send_str()함수를 사용해서 문자열을 전송한다.
  • ft_send_str() 함수에서는 ft_send_bit() 함수를 호출하여 1비트씩 전송하며 ft_send_bit() 함수에서는 bit가 1인지 0인지 판단하여 kill() 함수를 호출하여 시그널을 전송한다.
  • 서버의 PID와 비트가 1인 경우 SIGUSR1, 0인 경우 SIGUSR2 시그널을 전송한다.
  • 시그널을 보낸 후 서버의 응답 신호를 기다리기 위해 pause(
    ) 함수를 호출한다.
    • pause() 함수는 시그널을 수신할 때 까지 대기 상태로 전환한다.

3. 서버 - 클라이언트 PID 수신

  • 서버는 시그널이 수신 되면 ft_handler 함수를 호출한다.
    함수의 2번째 매개변수인 siginfo_t 타입 siginfo 구조체 si_pid 값을 통해 클라이언트의 PID를 얻어낸다.

이후 pid를 출력하고 sigaction() 함수를 사용하여 SIGUSR1과 SIGUSR2 시그널을 받아준다. pause()를 사용하여 시그널이 들어올 때 까지 대기한다.

4. 클라이언트 - 서버에게 메시지 전달

  • 서버에게 보낼 문자열로된 메시지를 비트 단위로 보내기 위해 ft_send_str() 함수를 호출하고, ft_send_str() 함수는 ft_send_bits() 함수를 호출한다. ft_send_bit() 함수는 문자를 비트 단위로 쪼개서 최하위 비트부터 비트 값에 맞춰 비트 값이 1인 경우 SIGUSR1, 비트 값이 0인 경우 SIGUSR2 신호를 보낸다.

단, 주의할 점은 kill() 함수를 통해 서버에게 일방적으로 반복적인 신호를 전송할 것이기 때문에 약간의 딜레이가 필요하다.

클라이언트가 서버에게 kill 함수를 통해 보내는 신호의 속도보다 서버에서 signal 함수로 받는 속도가 느리기 때문에 적절한 딜레이가 필요하다.

1개의 문자를 전송할 때 8개의 비트를 보내야 한다. 즉, 8번의 kill() 함수를 사용하여 시그널을 전송시켜야 한다. 따라서 8번의 딜레이가 필요한데 usleep() 함수를 사용하면 마이크로 초 단위로 대기상태가 되므로 훨씬 빠르게 처리가 가능하다.

  • usleep() 함수와 함께 메시지의 구성 문자들을 모두 서버에게 전송완료 했다면, 마지막으로 '\0'에 대한 시그널도 서버에게 보낸다.

  • 메시지가 모두 성공적으로 수신 완료되었다는 시그널을 서버로부터 받기 위해 pause() 함수를 호출하여 대기한다.

5. 서버 - 클라이언트 메시지 수신]

  • 서버는 클라이언트가 메시지에 대한 시그널을 보낼 때마다 ft_handler() 함수가 호출된다. 클라이언트의 일방적이고 반복적으로 보내는 시그널을 해독하기 위해서 정적 변수를 이용해 bit수와 문자를 저장하여 클라이언트가 보내고자 하는 문자 값이 무엇인지를 알아낸다.

핸들러 함수는 시그널이 발생할 때마다 호출되어진다.

즉, 정적 변수를 사용하지 않는다면 시그널이 발생할 때마다 핸들러 함수안의 변수들의 값은 유지되지않고 초기화 된다.

또한 8bit 만큼의 시그널을 받았다면 해당 문자 값을 저장하기 위한 변수도 정적으로 선언한다.

클라이언트로부터 보내온 시그널이 SIGUSR1이라면 1을 해당 비트만큼 shift left 연산을 한 뒤 or 연산을 수행한다. 시그널이 SIGUSR2라면 pass하여 자동적으로 0을 or 연산하는 것과 같은 연산이 된다.

총 8bit가 채워졌다면 bit와 문자를 저장하는 변수 tmp를 초기화 시켜준다.

6. 통신 및 연결 종료

  • 서버의 handler 함수에서 8비트가 모두 채워졌을 때 해당 문자가 '\0'이 아니면 문자를 출력시켜 준다.
  • 서버의 handler 함수에서 8비트가 모두 채워졌을 때 해당 문자가 '\0'이라면 클라이언트에게 SIGUSR1 시그널을 전송하여 모든 메시지를 성공적으로 수신받았고, 정상적으로 출력되었다는 의미를 전달한다.
  • 클라이언트는 서버로부터 수신받은 시그널이 SIGUSR1 인 것을 확인한 후, 클라이언트 프로그램을 정상적으로 종료한다.

유니코드

따로 코드 작성은 필요 없다.

맥에서 UTF-8로 인코딩을 실행하므로 자동으로 유니코드를 변환시켜 준다. 즉, Mandatory를 통해 1비트씩 전송할 수 있도록 했다면 유니코드 관련 코드는 작성할 필요가 없다.


코드 분석

utils.c

void	ft_putchar_fd(char c, int fd)
{
	write(fd, &c, 1);
}

void	ft_putstr_fd(char *s, int fd)
{
	int	i;

	i = 0;
	while (s[i])
		write(fd, &s[i++], 1);
}

void	rec_putnbr(int nb, int fd)
{
	char	c;

	if (nb == 0)
		return ;
	c = '0' + nb % 10;
	rec_putnbr(nb / 10, fd);
	ft_putchar_fd(c, fd);
}

void	ft_putnbr_fd(int n, int fd)
{
	char	c;

	if (n < 0)
	{
		write(fd, "-", 1);
		c = '0' - n % 10;
		rec_putnbr(-(n / 10), fd);
	}
	else
	{
		c = '0' + n % 10;
		rec_putnbr(n / 10, fd);
	}
	ft_putchar_fd(c, fd);
}

int	ft_atoi(const char *str)
{
	int	i;
	int	num;
	int	sign;

	i = 0;
	num = 0;
	sign = 1;
	while (str[i] == ' ' || (str[i] >= 9 && str[i] <= 13))
		i++;
	if (str[i] == '+' || str[i] == '-')
	{
		if (str[i] == '-')
			sign *= -1;
		i++;
	}
	while (str[i] >= '0' && str[i] <= '9')
	{
		num = num * 10 + (str[i] - '0') * sign;
		i++;
	}
	return (num);
}

client.c

static void	ft_send_bit(pid_t pid, char input)
{
	int	bit;

	bit = 0;
	while (bit < 8)
	{
    	// 가장 오른쪽 비트부터 왼쪽으로 가면서 전송함
        // 1을 bit만큼 left shift 연산 한 값을 input과 &연산
		if ((input & (1 << bit)) != 0)
			kill(pid, SIGUSR1); // 해당 값이 1일 경우 SIGUSR1 신호 전송
		else
			kill(pid, SIGUSR2); // 해당 값이 0일 경우 SIGUSR2 신호 전송
		usleep(100); // 딜레이를 위해
		bit++;
	}
}

static void	ft_send_str(pid_t pid, char input[])
{
	int	i;

	i = 0;
	while (input[i] != '\0') // 한 문자씩 전송함
	{
		ft_send_bit(pid, input[i]);
		i++;
	}
	ft_send_bit(pid, '\n');
	ft_send_bit(pid, '\0'); // 문자열 전송을 끝냈다는 것을 알리기 위한 널문자 전송
}

int	main(int argc, char **argv)
{
	pid_t	pid;

	if (argc == 3 && argv[2][0] != '\0') // 인자가 정확할 경우
	{
		pid = ft_atoi(argv[1]); // 입력한 pid 받아오기
		if (pid < 100 || pid > 99999) // pid의 범위처리 100 ~ 99999
		{
			ft_putstr_fd("Error: wrong pid.\n", 1);
			return (0);
		}
		ft_send_str(pid, argv[2]); // 문자열을 한 번에 전달
	}
	else // 인자가 유효하지 않은 경우
	{
		ft_putstr_fd("Error: wrong format.\n", 1);
		ft_putstr_fd("Try: ./client [PID] [MESSAGE]\n", 1);
	}
	return (0);
}

server.c

static void	ft_handler(int signal)
{
	static int	bit; // 비트를 얼마나 받았는 지 확인하는 정적 변수
	static char	tmp; // 문자값을 저장하는 정적 변수

	if (signal == SIGUSR1) // SIGUSR2 일 때는 어차피 0이므로 pass 가능
		tmp |= (1 << bit); // 가장 오른쪽 비트부터 추가해줌
	bit++;
	if (bit == 8) // 비트가 8이 될 경우 저장된 문자를 출력하고 정적변수 초기화
	{
		ft_putchar_fd(tmp, 1);
		bit = 0;
		tmp = 0;
	}
}

static void	ft_pid_print(pid_t pid) // pid 출력
{
	ft_putstr_fd("PID -> ", 1);
	ft_putnbr_fd(pid, 1);
	ft_putchar_fd('\n', 1);
	ft_putstr_fd("Waiting for a message...\n", 1);
}

int	main(int argc, char **argv)
{
	pid_t	pid;

	(void)argv;
	if (argc != 1) // 인자가 1개일 경우 예외처리
	{
		ft_putstr_fd("Error: worng format.\n", 1);
		ft_putstr_fd("Try: ./server\n", 1);
		return (0);
	}
	pid = getpid(); // pid를 받아오기
	ft_pid_print(pid); // pid 출력
    // SIGUSR1신호와 SIGUSR2 신호 입력시 ft_handler함수 호출
	signal(SIGUSR1, ft_handler);
	signal(SIGUSR2, ft_handler);
	while (argc == 1)
		pause(); // 신호가 입력될 때 까지 대기
	return (0);
}

Bonus 코드

client_bonus.c

// 문자열 전송이 완료되었을 때 서버에서 SIGUSR1 전송했을 경우 해당 문자열 출력 후 client 종료
static void	ft_confirm(int signal)
{
	if (signal == SIGUSR1)
		ft_putstr_fd("Server received message\n", 1);
	exit (0);
}

static void	ft_send_bit(pid_t pid, char input)
{
	int	bit;

	bit = 0;
	while (bit < 8)
	{
		if ((input & (1 << bit)) != 0)
			kill(pid, SIGUSR1);
		else
			kill(pid, SIGUSR2);
		usleep(100);
		bit++;
	}
}

static void	ft_send_str(pid_t pid, char input[])
{
	int	i;

	i = 0;
	while (input[i] != '\0')
	{
		ft_send_bit(pid, input[i]);
		i++;
	}
	ft_send_bit(pid, '\n');
	ft_send_bit(pid, '\0');
}

int	main(int argc, char **argv)
{
	pid_t	pid;

	if (argc == 3 && argv[2][0] != '\0')
	{
		signal(SIGUSR1, ft_confirm); // server에서 SIGUSR1 신호를 전송한다면 ft_confirm() 함수를 호출한다.
		pid = ft_atoi(argv[1]);
		if (pid <= 100 || pid >= 99999)
		{
			ft_putstr_fd("Error: wrong pid.\n", 1);
			return (0);
		}
		ft_send_str(pid, argv[2]);
		while (1)
			pause(); // 신호가 입력될 때 까지 대기
	}
	else
	{
		ft_putstr_fd("Error: wrong format.\n", 1);
		ft_putstr_fd("Try: ./client [PID] [MESSAGE]\n", 1);
	}
	return (0);
}

server_bonus.c

static void	ft_handler(int signal, siginfo_t *info, void *s)
{
	static int	bit;
	static char	tmp;
	pid_t		pid;

	(void)s;
	pid = info->si_pid; // info의 si_pid를 통해서 pid를 가져옴
	if (signal == SIGUSR1)
		tmp |= (1 << bit);
	bit++;
	if (bit == 8)
	{
		if (tmp != '\0') // 널 문자가 아닐 경우 문자 출력
			ft_putchar_fd(tmp, 1);
		else // 널 문자일 경우 SIGUSR1 신호 전송
			kill(pid, SIGUSR1);
		bit = 0;
		tmp = 0;
	}
}

static void	ft_pid_print(pid_t pid)
{
	ft_putstr_fd("PID -> ", 1);
	ft_putnbr_fd(pid, 1);
	ft_putchar_fd('\n', 1);
	ft_putstr_fd("Waiting for a message...\n", 1);
}

int	main(int argc, char **argv)
{
	struct sigaction	sig;
	pid_t				pid;

	(void)argv;
	if (argc != 1)
	{
		ft_putstr_fd("Error: worng format.\n", 1);
		ft_putstr_fd("Try: ./server\n", 1);
		return (0);
	}
	sig.sa_flags = SA_SIGINFO;
	sig.sa_sigaction = &ft_handler;
	sigemptyset(&sig.sa_mask);
	pid = getpid();
	ft_pid_print(pid);
    // 각각 SIGUSR1, SIGUSR2신호가 입력될 경우 sigaction으로 연결된 ft_handler() 함수를 호출
	sigaction(SIGUSR1, &sig, NULL);
	sigaction(SIGUSR2, &sig, NULL);
	while (1)
		pause();
	return (0);
}

평가지

Introduction

다음 규칙을 준수하십시오:

  • 평가 과정 내내 예의바르고, 예의바르고, 공손하고, 건설적인 태도를 유지하십시오. 공동체의 안녕이 그것에 달려 있다.

  • 프로젝트에서 발생할 수 있는 기능 장애를 평가한 학생 또는 그룹과 식별합니다. 확인된 문제에 대해 토론하고 토론하는 시간을 갖습니다.

  • 동료가 프로젝트의 지침과 기능 범위를 이해하는 방식에 약간의 차이가 있을 수 있다는 점을 고려해야 합니다. 항상 열린 마음을 유지하고 가능한 한 정직하게 평가하세요. 교육학은 동료 평가가 진지하게 이루어질 때에만 유용하다.

Guidelines

  • 평가된 학생 또는 그룹의 Git 저장소에서 반환된 작업만 채점합니다.
  • Git 저장소가 학생의 것인지 다시 확인합니다. 프로젝트가 예상되는 프로젝트인지 확인하십시오. 또한 "git 클론"이 빈 폴더에서 사용되는지 확인하십시오.
  • 사용자를 속이고 공식 리포지토리의 내용이 아닌 항목을 평가하도록 만들기 위해 악의적인 별칭이 사용되지 않았는지 주의 깊게 확인하십시오.
  • 예상치 못한 상황을 방지하고 해당되는 경우 채점을 용이하게 하는 데 사용된 스크립트(테스트 또는 자동화를 위한 스크립트)를 함께 검토합니다.
  • 평가할 과제를 완료하지 않은 경우 평가 프로세스를 시작하기 전에 주제 전체를 읽어야 합니다.
  • 사용 가능한 플래그를 사용하여 빈 리포지토리, 작동하지 않는 프로그램, Norm 오류, 부정행위 등을 보고합니다. 이 경우 평가 과정이 종료되고 최종 성적은 0점, 부정행위의 경우 -42점이다. 하지만, 부정행위를 제외하고, 학생들은 제출된 작품을 함께 검토하고, 미래에 반복되어서는 안 되는 실수를 식별하도록 강하게 권장된다.

Preliminaries

부정행위가 의심될 경우 평가는 여기서 종료됩니다. "부정행위" 플래그를 사용하여 보고합니다. 침착하고 현명하게 이 결정을 내리십시오. 그리고 부디 이 버튼을 주의 깊게 사용하십시오.

Preliminary tests

  • 방어는 평가된 학생 또는 그룹이 있는 경우에만 발생할 수 있습니다.
    이런 식으로 모든 사람들은 지식을 공유함으로써 배운다.
  • 작업이 제출되지 않은 경우(또는 잘못된 파일, 잘못된 디렉토리 또는 잘못된 파일 이름) 등급은 0이고 평가 프로세스는 종료됩니다.
  • 비어 있는 git이 없습니다(= git 저장소에 없음).
  • 표준 오차가 없습니다.
  • 부정행위(= -42)
  • 컴파일 오류 또는 재링크가 없습니다.

이러한 모든 점이 유효한 경우 예를 선택하고 수정을 계속합니다.
체중계 끝에 적절한 플래그를 사용하지 않는 경우!

General instructions

general instructions

  • Makefile은 두 실행 파일을 모두 컴파일합니다 -> 1포인트
  • 서버 이름은 'server'이며 시작 시 PID 표시 -> 2포인트
  • 클라이언트 이름은 'client'이며 다음과 같이 시작됩니다:
    '//client PID_SERVER 문자열_TO_PASS' -> 2점

Mandatory part

이 프로젝트는 신호에 대한 소개 신호의 코드와 사용 방법을 확인하여 통신하는 것입니다.

Transmission of messages

크기에 상관없이 메시지를 전달할 수 있습니다

수신된 메시지는 서버에 의해 표시되어야 하며, 정확해야 합니다!
서버가 막히거나 잘못된 문자를 인쇄하면 안 됩니다.

Simple setup

  • 서버가 다시 시작되지 않고 다중 문자열을 수신할 수 있음 -> 1점
  • 정당성(또는 글로벌 없음)을 사용하여 프로그램당 하나의 글로벌만 지원 -> 1점
  • 통신은 신호 SIGUSR1 및 SIGUSR2를 통해서만 이루어집니다 -> 3점

수신된 메시지는 서버에 의해 표시되어야 하며, 정확해야 합니다!

Bonus

필수 부품이 완전히 완벽하게 완성되었고 오류 관리가 예상치 못한 사용 또는 잘못된 사용을 처리하는 경우에만 보너스 부품을 평가합니다. 수비 중 필수 포인트가 모두 통과되지 않은 경우 보너스 포인트는 무시해야 한다.

Unicode

유니코드 문자는 클라이언트 및 서버에서 지원됩니다.

Acknoledgment

클라이언트는 다음 신호를 보내기 위해 서버의 승인을 기다립니다.

profile
진짜 공부하자

0개의 댓글