- 다중 접속 에코 서버의 구현
- 멀티 프로세스기반의 다중접속 에코서버 예제 이다 Chapter04의 에코 클라이언트와 함께 실행하자
- echo_mpserv.c 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(const char* message);
void read_childproc(int sig);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
pid_t pid;
struct sigaction act;
socklen_t adr_sz;
int str_len, state;
char buf[BUF_SIZE];
if( argc != 2 ){
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
// 좀비 프로세스의 생성을 막기 위한 코드 구성이다.
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0);
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr =htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr)) == -1)
error_handling("bind error!");
if(listen(serv_sock, 5) == -1)
error_handling("listen() error");
while(1)
{
adr_sz = sizeof(clnt_adr);
// 이 라인에서 accept 함수를 호출한 이후에 52행에서 fork함수를 호출하고있다.
// 때문에 이 라인을 통해서 만들어진 소켓(클라이언트의 연결요청 수락과정에서 만들어진)
// 의 파일 디스크립터를 부모 프로세스와 자식 프로세스가 동시에
// 하나씩 갖게 된다.
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
if(clnt_sock == -1)
continue;
else
puts("new client connected ...");
pid = fork();
if( pid == -1)
{
close(clnt_sock);
continue;
}
// 자식 프로세스에 의해 실행되는 영역이다. 이부분에 의해서 클라이언트에게 에코
// 서비스가 제공된다. 그런데 아래 if문안 close함수호출로 위에서 만든 서버 소켓을
// 닫고있다.이는 자식 프로세스로 서버 소켓의 파일 디스크립터까지 복사되기 때문인데,
// 이와 관련해서 잠시후에 별도로 설명한다.
if(pid == 0) /* 자식 프로세스 */
{
close(serv_sock);
while((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0)
write(clnt_sock, buf, str_len);
close(clnt_sock);
puts("client disconnected...");
return 0;
}
else
close(clnt_sock); // accept함수호출을 통해서 만들어진 소켓의 파일 디스크립터가 자식
// 프로세스에게 복사되었으니 서버는 자신이 소유하고있는 파일 디스크립터를
// 소멸시켜야 한다. 이와 관련해서도 잠시 후에 별도로 설명한다.
}
close(serv_sock);
return 0;
}
void read_childproc(int sig)
{
pid_t pid;
int status;
pid = waitpid(-1, &status, WNOHANG);
printf("removed proc id : %d \n", pid);
}
void error_handling(const char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
- 위의 예제를 통해 서버를 실행시키고 둘이상의 클라이언트를 생성해서 접속해보면 다수의 클라이언트에게 에코 서비스가 동시에 제공됨을 확인할 수 있을 것이다.
- fork 함수 호출을 통한 파일 디스크립터의 복사
- 예제 echo_mpserv.c에서는 fork함수호출을 통한 파일 디스크립터의 복사를 보여준다. 부모 프로세스가 지니고 있던 두 소켓(하나는 서버 소켓, 또 하나는 클라이언트와 연결된 소켓)의 파일 디스크립터가 자식 프로세스에게 복사되었다.
- 파일 디스크립터만 복사된건가요? 소켓도 복사가 되는것은 아닌가요?
- 소켓은 프로세스의 소유가 아니다 엄밀히 말해서 소켓은 운영체제의 소유이다. 다만 해당 소켓을 의미하는 파일 디스크립터만 이 프로세스의 소유인것이다.
- 또한 소켓이 복사되 동일한 port에 할당된 소켓이 둘 이상이 됨으로 두개의 파일 디스크립터가 모두 종료(소멸)되어야 소켓은 소멸된다. 때문에 위의 그림과 같은 형태를 유지하면 이후에 자식 프로세스가 클라이언트와 연결되어 있는 소켓을 소멸하려 해도 소멸되지 않고 계속 남아있게 된다(이는 서버 소켓도 마찬가지이다) 그래서 fork함수 호출 후에는 다음 그림에서 보이듯이 서로에게 상관없는 소켓의 파일 디스크립터를 닫아줘야 한다.
'책 > 윤성우 TCPIP' 카테고리의 다른 글
10-05 TCP의 입출력 루틴(Routine)분할 (2) (0) | 2020.12.23 |
---|---|
10-05 TCP의 입출력 루틴(Routine)분할 (0) | 2020.12.22 |
10-03 : 시그널 핸들링 (3) ~ 10-04 멀티태스킹 기반의 다중접속 서버(1) (0) | 2020.12.20 |
10-03 : 시그널 핸들링 (2) (0) | 2020.12.19 |
10-01 프로세스의 이해와 활용 (4) ~ 10-03 : 시그널 핸들링 (1) (0) | 2020.12.18 |