관리 메뉴

Value Creator의 IT(프로그래밍 / 전자제품)

Chapter 12 IO 멀티플렉싱 본문

1. 프로그래밍/4) Network

Chapter 12 IO 멀티플렉싱

valuecreatort 2019. 11. 6. 19:00
반응형

* 멀티 프로세스 기반 서버와 차이점

멀티 프로세스 : 여러개의 프로세스가 여러개의 소켓을 핸들링(각 소켓에 대응하는 프로세스를 각각 만들어준다)

멀티플렉싱 : 하나의 프로세스가 여러개의 소켓을 핸들링

 

 

1. Select 함수를 이용하면 멀티플렉싱 서버 구현이 가능하다.

Select.c

select 함수 사용 예제

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 30

int main(int argc, char *argv[])
{
	fd_set reads, temps; //fd_set 타입의 변수 선언 
	int result, str_len; //결과값, 길이값 변수 
	char buf[BUF_SIZE]; //문자열 
	struct timeval timeout; // timeout 시간? 

	FD_ZERO(&reads); //reads 변수 0으로 만들기 
	FD_SET(0, &reads); // 0 is standard input(console)

	/*
	timeout.tv_sec=5;
	timeout.tv_usec=5000;
	*/

	while(1)
	{
		temps=reads; //select 함수 호출 전에 reads의 원본 데이터를 temps에 저장. select 함수 호출이 끝나면 변화가 생긴 파일 디스크립터를 제외하고는 모든 비트가 0으로 초기화됨.
		timeout.tv_sec=5; //매번 반복해야함. 
		timeout.tv_usec=0;
		result=select(1, &temps, 0, 0, &timeout); //select함수는 변화된 파일 디스크립터의 개수를 반환 
		if(result==-1) //select 함수에 에러 발생 
		{
			puts("select() error!");
			break;
		}
		else if(result==0) //select 함수의 반환값이 없음 
		{
			puts("Time-out!");
		}
		else  //select함수가 0이 아닌 값을 반환. 변화를 보인 파일 디스크립터가 표준입력이 맞는지 확인 후 표준입력으로부터 데이터를 읽어서 콘솔로 데이터 출력
		{
			if(FD_ISSET(0, &temps)) 
			{
				str_len=read(0, buf, BUF_SIZE);
				buf[str_len]=0;
				printf("message from console: %s", buf);
			}
		}
	}
	return 0;
}

 

 

 

2. 멀티플렉싱 서버 구현

echo_selectserv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 100
void error_handling(char *buf);

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;
	struct sockaddr_in serv_adr, clnt_adr;
	struct timeval timeout;
	fd_set reads, cpy_reads;

	socklen_t adr_sz;
	int fd_max, str_len, fd_num, i;
	char buf[BUF_SIZE];
	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	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");

	FD_ZERO(&reads);
	FD_SET(serv_sock, &reads); //reads에 서버 소켓을 등록하고 있다. 서버 소켓의 데이터 수신 여부를 관찰할 것이다. 서버소켓에 수신된 데이터가 있다는 것은 연결요청이 있다는 뜻이다. 
	fd_max=serv_sock;

	while(1)
	{
		cpy_reads=reads;
		timeout.tv_sec=5;
		timeout.tv_usec=5000;

		if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1)
			break;
		
		if(fd_num==0)
			continue;

		for(i=0; i<fd_max+1; i++) //상태변화가 있는 파일디스크립터를 찾는다. 
		{
			if(FD_ISSET(i, &cpy_reads))
			{
				if(i==serv_sock)     // connection request!
				{
					adr_sz=sizeof(clnt_adr);
					clnt_sock=
						accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
					FD_SET(clnt_sock, &reads); //reads에 클라이언트와 연결된 소켓의 파일 디스크립터를 등록한다. 
					if(fd_max<clnt_sock)
						fd_max=clnt_sock;
					printf("connected client: %d \n", clnt_sock);
				}
				else    // read message!
				{
					str_len=read(i, buf, BUF_SIZE);
					if(str_len==0)    // close request!
					{
						FD_CLR(i, &reads);
						close(i);
						printf("closed client: %d \n", i); //파일 디스크립터 번호를 출력한다. 
					}
					else //수신한 데이터가 EOF인지 아닌지 확인한다. 
					{
						write(i, buf, str_len);    // echo!
					}
				}
			}
		}
	}
	close(serv_sock);
	return 0;
}

void error_handling(char *buf)
{
	fputs(buf, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

위는 서버의 소스코드이고,  Chapter 4 에서 했던 echo_client.c를 클라이언트 소스코드로 활용해서 상호간 통신을 확인해보면 된다.

 

[서버]

a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter12$ gcc echo_selectserv.c -o selserv
a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter12$ ./selserv 9190
connected client: 4 
closed client: 4 
connected client: 4 
connected client: 5 

 

[첫 번째 클라이언트]

a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter12$ ./client 127.0.0.1 9190
Connected...........
Input message(Q to quit): 123123
Message from server: 123123
Input message(Q to quit): 34432
Message from server: 34432
Input message(Q to quit): ^C

 

[두 번째 클라이언트]

a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter12$ ./client 127.0.0.1 9190
Connected...........
Input message(Q to quit): asdasdasd
Message from server: asdasdasd
Input message(Q to quit): 

 

 

 

 

반응형
Comments