[mini_talk] mini_talk 구현

J_JEON·2022년 7월 5일
0

mini_talk

목록 보기
2/2

Mini_talk

클라이언트가 서버로 문자열을 전송하면 서버에서 해당 문자열을 출력하도록 하는 프로그램을 만들어보는 프로젝트
UNIX의 signal을 활용하여 제작

구현방법

  • 서버가 실행되면 서버의 pid(getpid())를 출력하고 signal(signal())함수를 사용해 시그널을 받기위해 대기(pause())
  • 클라이언트 실행시 서버의 pid와 보낼 메시지를 받으며 시작
  • 클라이언트는 보낼 메시지의 문자 하나하나의 아스키코드값을 2진수로 변환
  • 변환한 2진수값 하나하나를 kill(kill())함수를 사용해 서버로 시그널을 전송
  • SIGUSR1시그널은 2진수의 0, SIGUSR2시그널은 1로 간주하여 일정 시간텀을 두고 서버로 전송
  • 서버는 수신한 시그널을 8번(1byte = 8bit)마다 끊어 2진수로 받은 뒤 10진수로 변환하여 문자의 아스키코드를 바탕으로 출력

Server

main함수

  • getpid() 함수를 사용해 현재 프로세스의 id를 가져온 뒤 pid를 출력해줌.
    이후 signal함수를 사용해 SIGUSR1, SIGUSR2 시그널을 수신했을때 어떤 함수를 호출할지 등록함.
    pause함수는 시그널이 들어올때까지 프로세스를 종료하지않고 대기상태로 유지해주기때문에 while(1)을 사용해 무한 루프를 돌게하여 지속적으로 시그널을 수신할 수 있도록 해줌
int	main(void)
{
	set_data(getpid()); //메시지를 받는데 사용할 저장공간들을 초기화
	if (g_data.pid == 0)
		return (0);
	write(1, "pid : ", 6);
	write(1, g_data.pid, strlen(g_data.pid)); // 서버의 pid를 출력
	write(1, "\n", 1);
	free(g_data.pid);
	signal(SIGUSR1, sig_usr1); // SIGUSR1시그널 수신시 sig_usr1 실행
	signal(SIGUSR2, sig_usr2); // SIGUSR2시그널 수신시 sig_usr2 실행
	while (1)
		pause(); // 프로세스가 종료되지않도록 멈춰줌
	return (0);
}

sig_usr1 함수

  • 클라이언트로부터 SIGUSR1 시그널을 받았을때에 실행
    클라이언트로부터 받은 시그널을 0 으로 생각하고 문자를 이루는 8개의 비트중 현재 카운트에 해당하는 자리에 0을 넣어줌.
    이후 카운트를 1 증가시키는데 이떄 카운트가 8 이라면 1바이트가 완성된것이므로 문자를 출력하고 카운트를 초기화시켜줌
    또한 문자가 '\0'이라면 문자열을 모두 받은것이기에 개행을 출력해줌
void	sig_usr1(int signum) // SIGUSR1 시그널을 0에 대응
{
	char	a;

	(void)signum;
	g_data.bit[g_data.count] = '0';
    //실행시 문자 2진수의 현재 자릿수를 0으로 생각하고 넣어줌
	g_data.count++; // 자릿수를 증가
	if (g_data.count == 8) //8 비트를 가득 채울시
	{
		g_data.dec = change_dec(g_data.bit); // 10진수로 변환해줌
		a = (char)g_data.dec;
		if (a == '\0') // 문자라 \0이라면 문자의 끝이므로 줄바꿈 출력
			write(1, "\n", 1);
		else
			write(1, &a, 1); // 완성된 문자를 출력
		g_data.count = 0; // 자릿수 처음으로 초기화
	}
}

sig_usr2 함수

  • 클라이언트로부터 SIGUSR2 시그널을 받았을때에 실행
    클라이언트로부터 받은 시그널을 1 로 생각하고 문자를 이루는 8개의 비트중 현재 카운트에 해당하는 자리에 1을 넣어줌.
    이후 카운트를 1 증가시키는데 이떄 카운트가 8 이라면 1바이트가 완성된것이므로 문자를 출력하고 카운트를 초기화시켜줌
void	sig_usr2(int signum) // SIGUSR2 시그널을 1에 대응
{
	char	a;

	(void) signum;
	g_data.bit[g_data.count] = '1';
    //실행시 문자 2진수의 현재 자릿수를 1로 생각하고 넣어줌
	g_data.count++; // 자릿수 증가
	if (g_data.count == 8) // 8비트를 가득 채울시
	{
		g_data.dec = change_dec(g_data.bit); // 10진수로 변환
		a = (char)g_data.dec;
		write(1, &a, 1); // 완성한 문자 출력
		g_data.count = 0; // 자릿수 초기화
	}
}

Client

int	main(int argc, char **argv)
{
	char	*str;
	char	*save;

	if (argc != 3) //시작시 pid와 문자를 받아왔는지 확인
		return (0);
	if (check_str(ft_atoi(argv[1], argv[2])) 
    // 받아온 pid값이 정상범위인지, 숫자로 이루어져 있는지 확인
    // 받아온 문자열이 아스키코드로 이루어져있는지 확인
		return (0);
	while (*argv[2])
	{
		str = change_bin((int)*argv[2]); //현재 문자를 2진수로 변환
		if (str == 0)
			return (0);
		save = str;
		while (*str) //2진수가 저장된 문자열 반복
		{
			send_msg(ft_atoi(argv[1]), *str);
            // 현재 저장된 2진수에따라 서버로 0 또는 1을 전송
			str++;
			usleep(200); 
             // 쉬지않고 보낼시 서버가 다 받지 못할 수 있으므로 서버가 수신할 수 있도록 200마이크로초 대기
		}
		argv[2]++; // 다음 문자로
		free(save); // 방금까지 봤던 문자열 저장공간 해제
	}
	send_end(ft_atoi(argv[1])); // 다 보냈으므로 끝이라는뜻의 \0 전송
	exit(0);
	return (0);
}

void	send_msg(pid_t pid, char a)
{
	if (a == '0') // 0이면 SIGUSR1시그널 전송
		kill(pid, SIGUSR1);
	else if (a == '1') // 1이면 SIGUSR2시그널 전송
		kill(pid, SIGUSR2);
}

void	send_end(pid_t pid)
{
	int	i;

	i = 0;
	while (i != 8)
	{
		kill(pid, SIGUSR1); // 끝이기때문에 0을 8번 전송해서 \0임을 알림
		i++;
		usleep(200);
	}
	return ;
}
profile
늅늅

0개의 댓글