소켓프로그램에 대해 공부하였다. 자세히 들어가 공부하는 것 아니지만 서버랑 클라이언트랑 가위바위 보를 하는 프로그램을 통하여 소켓의 생성원리, fork()함수, pipe()함수에 대해 다뤄보는 소스를 분석해보자.

일단 소켓을 무엇인지 알아야 소켓을 생성를 하지 않겠는가. 난 처음에 소켓을 하나의 패킷이라고 생각했지만 그런 개념을 아니였다. 서로 멀리 떨어져 있는 매개체를 연결해주는 역활을 해야된다고 생각해야되나..? 하여튼 그런 개념을 소켓이라고 한다. 자 그럼 한번 소스를 봐볼까

------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>

#define BUFSIZE 100
void error_handling(char *message);
void z_handler(int sig);
int who_win(int a, int b);
------------------------------------------------------------------------------------------
여기까지는 함수를 사용할 헤더파일 정의 또한 밑에서 사용할 사용자 정의 함수를 각각 선언하였다.

-------------------------------------------------------------------------------------------
int
main(int argc, char **argv)
{
int fd1[2], fd2[2];

char buffer[BUFSIZE];
char intro[]="Input(gawi : 0, bawi : 1, bo : 2) : ";
char win[] = "You Win!!\n";
char lose[] = "You Lose!!\n";
char no_winner[] = "Oh same!! Re try!!\n";

int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
struct sigaction act;
int str_len, state, addr_size;
pid_t pid;
---------------------------------------------------------------------------------------------
이부분은 각각 사용할 변수와 구조체를 선언하였다. 맨 마지막에 형이 없이 pid_t를 선언한 부분이 있는데 이걸 사용자 정의 변수라 한다. 사용자가 알아보기 싶게할때 쓰거나 만약 int형이 시스템 마다 틀려서 define해서 쓸수 있게 하여 확장성 높일때 사용한다.

-------------------------------------------------------------------------------------------
if( argc != 2 )
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
-------------------------------------------------------------------------------------------
이 프로그램은 서버 프로그램이다. 서버프로그램은 실행파일명 하고 뒤에 클라이언트와 접속할수 있는 포트 번호를 써줘야 하기때문에 인수가 2가 아니면 포트를 사용하라고 에러문이 뜨게 한다.

-------------------------------------------------------------------------------------------
if( pipe(fd1) < 0 || pipe(fd2) < 0 ) error_handling("pipe() error!");
-------------------------------------------------------------------------------------------
파이프 함수를 정의 하여 사용한다. 만약 파이프 함수가 에러가 나면 -1값을 리턴하는데 이때 오류문을 출력하는 구문

------------------------------------------------------------------------------------------
act.sa_hander = z_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0);
if( state != 0 ) error_handling("sigaction() error");
-------------------------------------------------------------------------------------------
struct sigaction{
void (*sa_handler) int /* 취해질 행동 */
sigset_t sa_mask /*시그널을 처리할 동안 추가의 시그널을 봉쇄 */
int sa_flags /*시그널을 형태에 영향을 미칠 플래그들 */
void(*sa_sigaction ) (int, siginfo_t *, vodi *) /* 시그널 핸들러에 대한 포인터 */

시그널 구조체에 각각의 값을 넣는 구문이다. 두번째 sigemptyset()는 초기화 하란 뜻이다. SIGCHLD는 자식프로세스의 신호에 대한 값으로 시스템에서 SIGCHLD가 발생이 되면 &act에 걸린 핸들러를 호출하는 구문이다.


---------------------------------------------------------------------------------------------
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
---------------------------------------------------------------------------------------------
스트림형 소켓(TCP통신)을 생성한다.

-------------------------------------------------------------------------------------------
memset(&serv_addr, 0, sizeof(serv_addr));
-------------------------------------------------------------------------------------------
serv_addr주소 부터 serv_addr 주소크기까지 0으로 채운라는 뜻이다 즉 초기화시키는 뜻을 가지고 있다.

------------------------------------------------------------------------------------------
serv_addr.sin_family = AF_INET;
-------------------------------------------------------------------------------------------
아아피 버전4 주소를 사용하겠다는의미

-------------------------------------------------------------------------------------------
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-------------------------------------------------------------------------------------------
htonl ->호스트의 네트워크 바이트 순서를 32비트를 사용하겠다는 의미 리틀 엔디안과 빅 엔디안의 차이 즉 CPU계열에 메모리 저장방식이 서로 틀려도 이것에 상관없이 사용할수 있도록 한다. (INADDR_ANY)자기 아이피 주소할당

-----------------------------------------------------------------------------------------
serv_addr.sin_port = htons(atoi(argv[1]));
-----------------------------------------------------------------------------------------
htons ->포트정보를 바꿔주겠단 의미로써 16-비트 정수로 된 호스트 바이트 오더를 네트웍 바이트 오더로 변환하는 함수이다. htons 함수는 SOCKADDR_IN 구조체에 저장되기 전에 포트 번호를 네트웍 바이트 오더로 변환하는데 자주 사용 한다

-------------------------------------------------------------------------------------------
if( bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) )
error_handling("bind() error");
--------------------------------------------------------------------------------------------
bind 소켓에다 주소를 할당 해주는 함수
ipv4에 할당된 주소를 표준된 주소로 형변환 할려는 것

-------------------------------------------------------------------------------------------
if( listen(serv_sock, 5)) error_handling("listen() error");
-------------------------------------------------------------------------------------------
listen 소켓연결 요청 대기 상태 5는 큐의 크기(클라이언트가 여러명들어갈수 있게

-------------------------------------------------------------------------------------------
while(1)
{
addr_size = sizeof(clnt_addr);

clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &addr_size);
if( clnt_sock == -1 ) continue;
-------------------------------------------------------------------------------------------
연결요청이 들어오면 받아들이는 함수(또하나의 소켓을 만들어준다. 클라인트 접속한 정보들이 구조체에 채워지고 에러가 나면 다시 처음으로 돌아간다.

-----------------------------------------------------------------------------------------
if( (pid = fork()) == -1 )
{
close(clnt_sock);
continue;
}
------------------------------------------------------------------------------------------
자식 프로세스 생성하다 오류나면 반복문 처음으로 돌아감

------------------------------------------------------------------------------------------
else if( pid > 0 )
{
int result;
puts("connection success");

close(clnt_sock);
//클라이언트 소켓을 닫아준다
//나중에 자식프로세스가 닫아주면 완전히 닫혀진다. (부모가 미리 닫는다.)

write(1, intro, sizeof(intro));
//첫번재 인수는 파일디스크립트(파일티스크립트는 시스템이 할당받은 미리 정해진 일련번호 라고 생각하면 된다. 보통 0은 표준입력 1은 표준출력 2는 표준에러를 뜻한다),intro 출력하라는 의미

read(0, buffer, BUFSIZE);
//사용자가 가위,바위,보를 저장한다.

read(fd1[0], &buffer[1], BUFSIZE - 1);
//클라이언트 가위바위보 낸걸 fd1[1]에서 서버 fd1[0]으로 읽어온다(파이브 함수를 통해서).

result = who_win(buffer[0], buffer[1]);
//buffer[0]에 서버 , buffer[1]에 클라이언트

if( result == 0 )
{
write(1, no_winner, sizeof(no_winner));
write(fd2[1], no_winner, sizeof(no_winner));
}
else if( result == 1 )
{
write(1, win, sizeof(win));
write(fd2[1], lose, sizeof(lose));
}
else
{
write(1, lose, sizeof(lose));
write(fd2[1], win, sizeof(win));
}
//이겼는지 졌는지 비겼는지를 출력하는 조건문이다.
}
--------------------------------------------------------------------------------------------
부모프로세스가 해야할것

---------------------------------------------------------------------------------------------
else
{
close(serv_sock);

write(clnt_sock, intro, sizeof(intro));
read(clnt_sock, buffer, BUFSIZE);
write(fd1[1], buffer, 1);
str_len = read(fd2[0], buffer, BUFSIZE);

write(clnt_sock, buffer, str_len);

puts("connection end");
close(clnt_sock);
exit(0);
}
----------------------------------------------------------------------------------------------
자식프로세스는 클라이언트에게 서버가 낸 가위바위보 정보를 전달해주는 역활을 한다.


}
return 0;
}

---------------------------------------------------------------------------------------------
void
z_handler(int sig)
{
pid_t pid;
int rtn;

pid = waitpid(-1, &rtn, WNOHANG);
//waitpid는 프로세스의 번호를 받는다. 첫번째 인자는 어떤프로세스에게 pid를 받을지 쓴다.
//(-1는 기달렸다가 먼저 종료되는 프로세스를 이미로 받는다.
//셋번째인자는 기달려도 종료된 자식프로세스가 신호를 않보내면 자동적으로 종료

printf("killed zombie pid : %d \n", pid);
printf("returned data : %d \n\n", WEXITSTATUS(rtn));
//자식프로세스가 죽었다는 신호(숫자)를 리턴값으로 전달해주는 함수
}
-------------------------------------------------------------------------------------------
자식프로세스 신호의 핸드러에 관한 함수이다.

-------------------------------------------------------------------------------------------
int
who_win(int a, int b)
{
if( a == b ) return 0;

else if( a % 3 == (b + 1) % 3) return 1;
//서버가 이길경우

else return -1;
//클라이언트가 이길 경우
}
---------------------------------------------------------------------------------------------
클라이언트와 서버간에 가위바위보 비교

--------------------------------------------------------------------------------------------
void
error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
---------------------------------------------------------------------------------------------
에러문 출력

+ Recent posts