앞의 포스팅에서 설명한 멀티플렉싱 기법을 구현하는데 가장 많이 사용되는 방법이 바로 Select함수를 이용하는 것이다. 또한 리눅스 뿐만 아니라 윈도우에도 같은 이름으로 동일한 기능을 제공하는 함수가 있기 때문에 이식성에도 좋다.

 

#include<sys/select.h>

#include<sys/time.h>

 

int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptest, const struct timeval *timeout);

->성공시 0이상 실패시 -1 반환.

 

maxfd -> 검사 대상이 되는 파일 디스크립터의 수.

 

readset -> fd_set형 변수에 수신된 데이터의 존재여부에 관심 있는 파일 디스크립터 정보를 모두

  등록해서 변수의 주소값 전달.

 

writeset -> fd_set형 변수에 블로킹 없는 데이터 전송의 가능여부에 관심 있는 파일 디스크립터 정보를

  모두 등록해서 그 변수의 주소 값을 전달한다.

 

exceptset -> fd_set형 변수에 예외상황의 발생여부에 관심이 있는 파일 디스크립터 정보를 모두

     등록해서 그 변수의 주소 값 전달.

 

 

Select 함수의 기능

 

Select 함수를 이용하면 한곳에 여러개의 파일 디스크립터를 모아놓고 동시에 관찰할 수 있다.

 

1. 수신한 데이터를 지니고 있는 소켓이 존재하는지.

2. 불로킹되지 않고 데이터의 전송이 가능한 소켓은 무엇인지.

3.예외상황이 발생한 소켓은 무엇인지.

 

를 관찰할수가 있다.

 

Select 함수 호출순서 

 

 

Select 함수를 호출하기 전에는 파일디스크립터,검사 범위, 타임아웃의 설정을 해줘야 비로소 Select함수를 호출할 수가 있다.

 

파일디스크립터 설정

 

Select 함수를 이용하여 여러개의 파일디스크립터를 관리할 수 있는데, 여러개의 파일디스크립터를 모을 떄도 목적에 따라서 구분해서 모아야 하고, 파일 디스크립터를 모을 때 사용하는 것이 fd_set형 변수이다. fd_set형은 0과 1로 표현되는, 비트단위로 이뤄진 배열이다.

 

                       <fd_set 자료형>

 

fd_set형 변수를 조작하기 위해서는 매크로 함수를 이용하면 된다.

 

FD_ZERO(fd_set *fdset)        

//인자로 전달된 주소의 fd_set형 변수의 모든 비트를 0으로 초기화.

FD_SET(int fd, fd_set *fdset)

//매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보 저장.

 

FD_CLR(int fd,fd_set *fdset)

//매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보 삭제.

 

FD_ISSET(int fd,fd_set *fdset)

//매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보가 있으면 양수반환.

'Programming > Socket' 카테고리의 다른 글

I/O 멀티플렉싱  (0) 2012.05.15
로그파일을 남기는 서버  (0) 2012.05.14
클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08

앞서 멀티프로세스 서버를 구현하였으나, 멀티프로세스 서버는 클라이언트 연결요청이 있을 때마다, 새로운 프로세스를 생성하는 기법으로, 상당히 많은 양의 연산이 요구되고, 필요한 메모리 공간도 비교적 큰 편이다.

 

따라서 프로세스의 생성을 동반하지 않고, 다수의 클라이언트들에게 서비스를 제공할 수 있는 방법으로 나온 것이 멀티 플렉싱기법이다.

 

멀티플렉싱은 물리적 장치의 효율성을 높이기 위해 최소한의 물리적인 요소만 사용해서 최대한의 데이터를 전달하기 위해 사용하는 기술이다.

 

기존의 자식프로세스를 생성해서 서비스를 하는 멀티프로세스 서버와는 달리,

 

멀티플렉싱개념을 사용하면,

 

자식프로세스가 각각의 소켓을 처리하는게 아니고, 서버하나가 각각의 소켓에 대한 서비스를 제공하는게 멀티플렉싱의 개념이다.

 

멀티플렉싱과 멀티프로세스의 차이는 서비스를 제공하는 프로세스 갯수의 차이가 각각 한개와, 클라이언트수만큼이라는 것이다.

 

 

멀티플렉싱 서버를 사용하기 적합한 경우.

1. 클라이언트와 서버간의 송수신 데이터 용량이 작은 경우 적합
2. 송수신이 연속적이지 않은 경우에 적합 
3. 멀티 프로세스 기반에 비해 많은 수의 클라이언트 처리에 적합


 

'Programming > Socket' 카테고리의 다른 글

Select 함수  (0) 2012.05.16
로그파일을 남기는 서버  (0) 2012.05.14
클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include<stdlib.h>

 

int main(int argc,char *argv[]){

        char buf[100],ip;
        int sock_serv,sock_clin,i;
        struct sockaddr_in serv_adr,clin_adr,test;
        int str_len;
        FILE *fp;
        socklen_t adr_sz;
        pid_t pid;

 

        if(argc != 2)
                printf("Please Input <Port>");

 

        sock_serv=socket(PF_INET,SOCK_STREAM,0);
        memset(&serv_adr,0,sizeof(serv_adr));
        serv_adr.sin_family=AF_INET;
        serv_adr.sin_port=htons(atoi(argv[1]));
        serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);

 

        if(bind(sock_serv,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
                printf("bind error()");
        if(listen(sock_serv,5)==-1)
                printf("listen error()");

 

        while(1){
                adr_sz=sizeof(clin_adr);
                sock_clin=accept(sock_serv,(struct sockaddr*)&clin_adr,&adr_sz);
                memset(&test,0,sizeof(test));                
                getpeername(sock_clin,(struct sockaddr *)&test,&adr_sz);

//클라이언트의 주소정보를 getpeername함수를 이용하여 test구조체에 저장한다.


                if(sock_clin==-1){
   }
                else{
                        printf("Accept at : %s!!\n",inet_ntoa(test.sin_addr)); //클라이언트의 ip출력.
                        pid=fork();
                        if(pid==0){            //자식프로세스 영역.
                                close(sock_serv);
                                while(str_len=read(sock_clin,buf,100)!=0){
                                i=strlen(buf);
                                write(sock_clin,buf,i);
                                fp=fopen("test.txt","a");        //test.txt파일을 열고.
                                fprintf(fp,"%s\t%s",inet_ntoa(test.sin_addr),buf);  //클라이언트의 ip와 buf저장.
                                fclose(fp);
                                }
                                close(sock_clin);
                                printf("Clinet disconnected\n");
                        }
                        else
                                close(sock_clin);
                }
        }
        close(sock_serv);
        return 0;
}

 

 

test.txt를 살펴보니.

127.0.0.1과 203.x.x.x에서 접속을 했고, 입력했던 문자열이 저장되어 있다.

 

'Programming > Socket' 카테고리의 다른 글

Select 함수  (0) 2012.05.16
I/O 멀티플렉싱  (0) 2012.05.15
클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08

서버소켓에서 나에게 접속한 클라이언트 IP를 가져오는 함수가 존재한다.

로그파일을 만들 때 사용하는 건가?

 

int Getpeername( sock, struct sockaddr* name, int *size )

sock : IP를 알고자 하는 소켓의 파일 디스크립터. 
struct sockaddr* name : 소켓 정보를 저장할 sockaddr_in구조체

int *size : 구조체 사이즈.

 

ex)

struct sockaddr_in test_adr;

 

adr_sz=sizeof(clin_adr);

getpeername(sock,(struct sockaddr*)&test_adr,&adr_sz);

 

위와 같이 선언한다면, test_adr구조체 변수안에 접속한 클라이언트의 정보가 기록된다.

하지만 위와 같이 선언하고 출력해보면.

 

우리가 알수 없는 정수형으로 값이 저장되어 있다.

 

sockaddr_in 구조체의 주소멤버의 데이터 타입은 unsigned long이다.

따라서 네트워크바이트 순서인 32비트 값을 변환시켜주어야 한다.

 

이러한 경우에 사용하는 함수가 inet_ntoa()함수이다.

 

char *inet_ntoa(struct in_addr addr)

 

성공시 변환된 해당 문자열의 포인터를 반환하고, 실패할 경우에는 -1을 반환한다.

 

 


'Programming > Socket' 카테고리의 다른 글

I/O 멀티플렉싱  (0) 2012.05.15
로그파일을 남기는 서버  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08
소켓의 옵션  (0) 2012.05.07

echo_server.c

 

 

#include<stdio.h>
#include<unistd.h>              //pid_t,socklen_t,read,write,close etc...
#include<sys/socket.h>          //socket,accept,bind,listen, etc...
#include<stdlib.h>
#include<arpa/inet.h>           //INADDR_ANY,sockaddr_in
#include<string.h>

int main(int argc,char *argv[])
{
        struct sockaddr_in serv_adr,clnt_adr;   
        int sock_ser,sock_clnt;
        char buf[100];
        pid_t pid;
        socklen_t adr_sz,len;

 

        sock_ser = socket(PF_INET,SOCK_STREAM,0);
        memset(&serv_adr,0,sizeof(serv_adr));            //소켓구조체 초기화
        serv_adr.sin_family=AF_INET;                        //서버소켓의 주소체계 결정
        serv_adr.sin_port = htons(atoi(argv[1]));        //서버포트 결정
        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); //서버의 아이피로는 가지고 있는 아이피중 임의의 하나로 설정한다는 뜻.

 

        if(bind(sock_ser,(struct sockaddr *)&serv_adr,sizeof(serv_adr))==-1)    

//생성한 소켓에 ip,port,주소체계를 설정해준다.
                printf("bind() error\n");
 if(listen(sock_ser,5)==-1)        //서버에 연결가능도록 해줌.
            printf("listen error\n");
  while(1){
            adr_sz = sizeof(clnt_adr);
            sock_clnt = accept(sock_ser,(struct sockaddr*)&clnt_adr,&adr_sz);//클라이언트의 접속허용
            if(sock_clnt== -1){ //접속이 없다면 아무런 일도 하지않는다.

                   }

                else{                        //접속이 들어온다면,
                        printf("accept !!\n");
                        pid=fork();        //자식프로세스를 생성한 후에,
                        if(pid==0){        //서버소켓의 파일디스크립터를 닫고,
                                close(sock_ser);
                                while((len=read(sock_clnt,buf,100))!=0)    //데이터를 읽어들이고,
                                        write(sock_clnt,buf,len);                //다시클라이언트에게 보내준다.
                                close(sock_clnt);                                //클라이언트 소켓을 소멸시킴.
                                printf("disconnected socket...\n");
                    }
                  else    close(sock_clnt);                            //부모프로세스는 클라이언트의 디스크립터를 닫는다.
                }
        }
        close(sock_ser);        //서버 소켓 소멸.
        return 0;
}

 

 

echo_client.c

 

#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>

int main(int argc,char *argv[])
{
        int sock_clin;
        struct sockaddr_in serv_adr,clnt_adr;
        socklen_t len;
        char buf[100];

 

        sock_clin=socket(PF_INET,SOCK_STREAM,0);    //소켓 생성과 필요한정보할당.
        memset(&clnt_adr,0,sizeof(clnt_adr));
        clnt_adr.sin_family=AF_INET;
        clnt_adr.sin_addr.s_addr=inet_addr(argv[1]);
        clnt_adr.sin_port=htons(atoi(argv[2]));

 

        if(connect(sock_clin,(struct sockaddr *)&clnt_adr,sizeof(clnt_adr))==-1)    

//소켓에 적혀있는 아이피와 포트번호를 보고 접속을 시도함.
        printf("connected error\n");

        printf("Connected!!...\n");
        while(1){
                if((strcmp(buf,"q\n"))==0 || (strcmp(buf,"Q\n")==0))    //q,Q를 입력하면 종료됨.
                break;
                printf("Input Message : ");
                fgets(buf,100,stdin);        //문자열을 입력받고,
                write(sock_clin,buf,sizeof(buf));        //서버에 데이터를 전송,
                read(sock_clin,buf,100);                    //read를 호출하면, 서버로 부터 데이터를 읽어들일 때까지 기다리다가 다음 명령어가 실행되므로, write바로 다음에 read를 쓴다고 해서 서버로부터 응답이 늦게온다고 데이터를 받지 못하는 현상은 일어나지 않는다.
                printf("Message From server : %s\n",buf);    //출력.
        }
        close(sock_clin);            //소켓 소멸.
        return 0;
}

'Programming > Socket' 카테고리의 다른 글

로그파일을 남기는 서버  (0) 2012.05.14
클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 서버  (0) 2012.05.08
소켓의 옵션  (0) 2012.05.07
TCP server 함수호출 순서.  (0) 2012.04.25

멀티 프로세스 서버란, 한번에 하나의 클라이언트에게만 서비스를 제공하는게 아니라 동시에 둘 이상의 클라이언트에게 서비스를 제공하는 서버이다.

 

 

그림처럼 클라이언트의 서비스 요청이 있을 때마다 서버는 자식 프로세스를 생성해서 서비스를 제공한다. 즉, 서비스를 요청하는 클라이언트의 수가 다섯이라면 서버는 추가로 다섯 개의 자식 프로세스를 생성해서 서비스를 제공한다.

이때 자식프로세스를 생성할 때 fork()함수를 사용하는데, fork()함수를 사용하게 되면 부모프로세스의 값을 그대로 복사하기 때문에 파일디스크립터도 복사되서 부모에서 따로 자식에게 소켓의 파일디스크립터를 넘길 필요가 없다.

 

부모 프로세스에서 자식프로세스를 생성하고 파일디스크립터를 넘겨받은 직후의 모습은.

 

이러한 식으로 하나의 소켓에 두개의 파일 디스크립터가 존재하는 경우, 두개의 파일 디스크립터가 모두 소멸되어야 소켓도 소멸된다. 따라서 위와 같은 형태를 유지하면, 자식프로세스가 클라이언트와 연결되어 있는 소켓을 소멸하려 해도 소멸되지 않고 남아있게 된다. 따라서 fork()함수 호출이 후에는 서로에게 상관이 없는 소켓의 파일 디스크립터를 닫아주어야 함.

 

 

 

'Programming > Socket' 카테고리의 다른 글

클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
소켓의 옵션  (0) 2012.05.07
TCP server 함수호출 순서.  (0) 2012.04.25
Socket 함수 구조체  (0) 2012.04.24

 

 

위 표에서 보이듯이 소켓의 옵션은 계층별로 분류된다. IPPROTO_IP 레벨의 옵션들은 IP프로토콜에 관련되며 IPPROTO_TCP레벨의 옵션은 TCP프로토콜에 관련된 사항이다. SOL_SOCKET옵션들은 소켓에 대한 가장 일반적인 옵션들이다.

 

각각의 옵션을 참조하거나 설정할 때에는 함수를 이용해야 하는데 이떄 이용하는 함수가

 

getsockopt & setsockopt이다.

 

#include<sys/socket.h>에 선언되어 있다.

 

int getscokopt(int sock,int level, int optname, void *optval, socklen_t *optlen);

성공시 0, 실패시 -1 반환

 

sock -> 옵션 확인을 위한 소켓의 디스크립터 전달.

level -> 확인할 옵션의 프로토콜 레벨 전달.

optname -> 확인할 옵션의 이름 전달.

optval -> 확인결과의 저장을 위한 버퍼의 주소값 전달.

optlen -> 네번째 매개변수 optval 로 전달된 주소 값의 버퍼크기를 담고있는 변수의 주소 값 전달, 함수호출이 완료되면 이 변수에는 네번째 인자를 통해 반환된 옵션정보의 크기가 바이트 단위로 계산되어 저장된다.

 

int setsockopt(int sock, int level, int optname, const void* optval, socklen_t optlen);

성공시 0 실패시 -1반환.

 

sock -> 옵션변경을 위한 소켓의 파일 디스크립터 전달.

level -> 변경할 옵션의 프로토콜 레벨 전달.

optname -> 변경할 옵션의 이름 전달.

optval -> 변경할 옵션정보를 저장한 버퍼의 주소 값 전달.

optlen -> 네번째 매개변수 optval로 전달된 옵션정보의 바이트 단위 크기 전달.

 

몇가지 살펴보면, SO_SNDBUF와 SO_RCVBUF는 입출력 버퍼를 제어할 때 사용하는 함수이고, SO_REUSEADDR 함수는 서버측에서 time-wait 상태가 발생하였을떄 이 값을 1(TURE)로 바꾸어 주면 bind error가 뜨지 않고 바로 주소를 재할당해 줄 수가 있다.

또한 TCP소켓에 기본설정으로 Nagle 알고리즘이 설정되어 있는데 이는 때때로 옵션을 풀어줘야 하는 경우가 생긴다. 이때 사용하는 함수가 TCP_NODELAY이다. 이때는 반대로 값을 1로 바꿔 주어야 한다.

 

<Nagle 알고리즘 >

'Programming > Socket' 카테고리의 다른 글

클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08
TCP server 함수호출 순서.  (0) 2012.04.25
Socket 함수 구조체  (0) 2012.04.24

 

TCP 서버의 함수호출 순서.

소켓프로그래밍 짜다가 너무 헷갈려서 한번올려본다. 원래는 이런거 올릴생각없었는데 ㅠㅠ

 

함수하나하나 살펴보자.

 

#include<sys/socket.h>에 정의되어 있다.

 

int socket(int domain, int type, int protocol);    //성공시 파일 디스크립터,실패시 -1반환.

 

domain -> 소켓이 사용할 프로토콜 체계(Protocol Family)정보 전달.

type -> 소켓의 데이터 전송방식에 대한 정보 전달.

protocol -> 두 컴퓨터간 통신에 사용되는 프로토콜 정보 전달.

 

int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);    //성공시 0,실패시 -1반환.

 

sockfd -> 주소정보를 할당할 소켓의 파일 디스크립터.

myaddr -> 할당하고자 하는 주소정보를 지니는 구조체 변수의 주소 값.

addrlen -> 두 번째 인자로 전달된 구조체 변수의 길이정보.

 

int accept(int sock, struct sockaddr * addr, socklen_t * addrlen);    //성공시 생성된 소켓의 파일 디스크립터, 실패시 -1 반환.

 

sock -> 서버 소켓의 파일 디스크립터 전달.

addr -> 연결요청 한 클라이언트의 주소정보를 담을 변수의 주소 값 전달, 함수호출이 완료되면 인자로 전달된 주소의 변수에는 클라이언트의 주소정보가 채워진다.

addrlen -> 두번째 매개변수 addr에 전달된 주소의 변수 크기를 바이트 단위로 전달, 단 크기정보를 변수에 저장한 다음에 변수의 주소 값을 전달한다. 그리고 함수호출이 완료되면 크기정보로 채워져 있던 변수에는 클라이언트의 주소정보 길이가 바이트 단위로 계산되어 채워진다.

 

int listen(int sockfd, int backlog);    //성공시 0, 실패시 -1반환

 

sockfd -> 서버 소켓의 파일 디스크립터 전달.

backlog -> 연결요청대기큐 허용 갯수 설정하는 정수가 들어감.

 

 

 

'Programming > Socket' 카테고리의 다른 글

클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08
소켓의 옵션  (0) 2012.05.07
Socket 함수 구조체  (0) 2012.04.24

Socket Programming을 할때에 소캣을 생성하고 connect ,bind 등등 할때에 소캣에 IP, PORT를 넣어주어야 하는데 이때 미리 선언되어 있는 Socket 구조체에 넣어준다.

 

struct sockaddr_in

{

sa_family_t    sin_family;        //주소체계를 저장함.

uint16_t         sin_port;          //16 비트 TCP/UDP PORT 번호

struct in_addr  sin_addr;        //32비트 IP주소

char            sin_zero[8];      //사용되지 않음.

};

 

struct in_addr

{

in_addr_t    s_addr;        //32비트 IPv4 인터넷 주소

};

 

위의 자료형중에 uint16_t라던지 in_addr_t와 같은 자료형은 POSIX(Portable Operating System Interface)에 나와있는데, POSIX란, 유닉스 계열의 운영체제에 적용하기 위한 표준을 의미한다. 

따로 자료형을 정의해 놓은 이유는 확장성을 고려한 결과이다. int32_t라는 자료형은 이후에 int가 64비트로 표현되는 경우에도 4바이트 자료형임을 보장받을 수 있다. 즉 어떠한 경우에도 int32_t는 4바이트로 표현됨.

 

구조체 멤버 분석

 

sin_family

멤버sin_family에 적용할 주소체계 정보를 저장.

 

 

sin_port

16비트 PORT주소 정보를 저장한다. '네트워크 바이트 순서'대로 저장해야 한다.(빅 에디안)

 

sin_addr

32비트 IP주소 정보를 저장한다.  이역시 빅에디안 순서대로 저장.

 

sin_zero

특별한 의미없음.sockaddr 구조체와 일치시키기 위해 삽입된 멤버.

struct sockaddr{

sa_family_t    sin_family;     //주소체계 저장.

char             sa_data[14]; //주소 정보.

};

 

위의 구조체 멤버 sa_data에 저장되는 주소정보에는 IP주소와 PORT번호가 포함되어야 하고,이 두가지 정보를 담고 남은 부분은 0으로 채울 것을 요구한다.

sockaddr_in에 sin_zero가 존재하는 이유

하지만 이는 주소정보를 담기에 매우 불편한 사항이기 때문에 sockaddr_in이 등장한 것이다.

sockaddr_in구조체 멤버를 채우고 난뒤에 이때 형성되는 구조체 변수의 바이트 열이 요구하는 바이트 열이 되기 때문에 인자전달을 위한 형변환만 시켜주면 바로 요구사항을 채워줄수 있다.

따라서 sockaddr_in에 필요한 정보를 채우고 bind함수를 호출할 때에는 (struct sockaddr *)로 형변환을 시켜주면 간단하게 해결됨.

 

'Programming > Socket' 카테고리의 다른 글

클라이언트 IP 가져오기  (0) 2012.05.14
멀티프로세스 소스  (0) 2012.05.10
멀티프로세스 서버  (0) 2012.05.08
소켓의 옵션  (0) 2012.05.07
TCP server 함수호출 순서.  (0) 2012.04.25

+ Recent posts