[다중 입출력 함수 select]

I/O 처리하는 경우 non-blocking 으로 여러개의 file descriptor를 체크하면서

읽어들일 데이터가 어떤 상태인지 확인하다가, 어떤 변화가 생기면

file descriptor를 리턴하게 된다.

만일 아무런 변화가 없다면 block 상태에 걸려버리는데

이는 timeout을 지정해서 빠져나올 수 있게 한다.

이 기능을 가지고 sleep대신 select를 사용할 수도 있다.

 

개인적으로 이해가 잘 안되서 이래저래 좀 자세히 테스트 해봤다.

 

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

 

내용출처 : http://mintnlatte.tistory.com/313

- nfds : 검사 대상이 되는 fd 범위로 세개의 집합(readfds/writefds/exceptfds)중 가장 큰 파일 디스크립터 값에 1을 더해서 전달하는 매개변수

- readfds : 시스템에 의해 즉시 입력이 가능한지 확인(읽기가 block되지 않았는지 검사하기 위한 리스트/ EOF 발생 검사)

- writefds : 시스템에 의해 즉시 출력이 가능한지 확인(쓰기가 block되지 않았는지 검사하기 위한 리스트)

- exceptfds : 예외가 있는지 검사하기 위한 리스트

- timeout : select가 반환하기 전에 블럭킹될 수 있는 시간의 제한 값 (0은 즉시반환 / NULL은 무한 block)

 

 ○ 리턴값

  -1 : 오류발생 

   0 : 타임아웃

양수: 변화 발생 파일 디스크립터 수

 

: FD_SET 구조체 / 변수 조작하는데 사용되는 함수들

 

○ FD_SET 구조체

 

 typedef struct fd_set {

    u_int          fd_count;

    SOCKET    fd_array[FD_SETSIZE];

 } fd_set;

- fd_count : 설정하는 파일 디스크립터의 번호

- fd_array : 설정된 파일 디스크립터의 배열

* fd_set은 0과 1로 저장되는 비트들의 배열이다.

 

○ FD_SET 변수 조작 함수

  - FD_SETFD_ZERO(fd_set *fdset) : fdset 포인터가 가리키는 변수의 모든 비트들을 0으로 초기화

  - FD_SET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보 설정(1로 설정)

  - FD_CLR(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보를 삭제(0으로 설정)

  - FD_ISSET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수가 fd로 전달되는 파일 디스크립터의 정보를 지니는지 확인

 

○ timeout 구조체

 

 

 struct timeval {

    long    tv_sec;

    long    tv_usec;

 }

-  tv_sec : 초 단위 설정 변수

- tv_usec : 마이크로 초 단위 설정 변수

 

 

 동작하는 방식

  FD_SET 변수에 변화가 생긴 파일 디스크립터는 1로 세팅, 변화가 없는 파일 디스크립터는 0으로 세팅한다.

 

 

- select 함수 호출 전 관찰 대상으로 fd0 과 fd3 이 설정되어 있다.

- select 함수 호출 후 fd3만 1로 설정되어있다.

=> fd3에 변화가 생겼음을 확인 & 해당 파일 디스크립터와 데이터 통신을 진행한다.

 

다음은 간단한 server/client를 구현했다. 그냥 echo 서버인데 서버쪽에서 여러개의

클라이언트를 그냥 받아들인다.

이걸 기본으로 하면 다른 것도 금방 짜겠다..

소스코드라인도 좀 기네;;;

 

==================================

 

서버측 소스코드
#include "sys/socket.h"
#include "sys/time.h"
#include "sys/types.h"
#include "sys/stat.h"
#include 
#include 
#include 
#include 
#include "netinet/in.h"
#include "arpa/inet.h"

#define FD_MAX_SIZE 1024

int main(int argc, char **argv)
{
    int server_sockfd, client_sockfd, sockfd;
    int state, client_len;
    int pid;
    int i, max_client, maxfd;
    int client[FD_MAX_SIZE];

    FILE *fp;
    struct sockaddr_in clientaddr, serveraddr;
    struct timeval tv;
    fd_set readfds, otherfds, allfds;

    int current_cli_num;
    char buf[255];
    char line[255];

    memset(line, 0x00, 255);
    state = 0;
    current_cli_num = 3;

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket error : ");
        exit(0);
    }

    memset(&serveraddr, 0x00, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(9000);

    state = bind(server_sockfd, (struct sockaddr *)&serveraddr,
            sizeof(serveraddr));
    if (state == -1) {
        perror("bind error : ");
        exit(0);
    }

    state = listen(server_sockfd, 5);
    if (state == -1) {
        perror("listen error : ");
        exit(0);
    }

    client_sockfd = server_sockfd;

    max_client = -1;
    maxfd = server_sockfd;

   for (i = 0; i < FD_MAX_SIZE; i++) {
        client[i] = -1;
    }

    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);

    printf("\nTCP Server Waiting ...");
    fflush(stdout);

    while(1)
    {
        allfds = readfds;
        state = select(maxfd + 1 , &allfds, NULL, NULL, NULL);

        if (FD_ISSET(server_sockfd, &allfds)) {
            client_len = sizeof(clientaddr);
            client_sockfd = accept(server_sockfd,
                    (struct sockaddr *)&clientaddr, &client_len);
            printf("\nconnection from (%s , %d)",
                    inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

            for (i = 0; i < FD_MAX_SIZE; i++)
            {
                if (client[i] < 0) {
                    client[i] = client_sockfd;
                    printf("\nclientNUM=%d\nclientFD=%d\n", i+1, client_sockfd);
                    break;
                }
            }

            printf("accept [%d]\n", client_sockfd);
            printf("===================================\n");
            if (i == FD_MAX_SIZE) {
                perror("too many clients\n");
            }
            FD_SET(client_sockfd,&readfds);

            if (client_sockfd > maxfd)
                maxfd = client_sockfd;

            if (i > max_client)
                max_client = i;

            if (--state <= 0)
                continue;

        }
        for (i = 0; i <= max_client; i++)
        {
            if ((sockfd = client[i]) < 0) {
                continue;
            }

            if (FD_ISSET(sockfd, &allfds))
            {
                memset(buf, 0x00, 255);

                if (read(sockfd, buf, 255) <= 0) {
                    close(sockfd);
                    perror("Close sockfd : ");
                    FD_CLR(sockfd, &readfds);
                    client[i] = -1;
                }
                printf("[%d]RECV %s\n", sockfd, buf);
                write(sockfd, buf, 255);

                if (--state <= 0)
                    break;
            }
        }
    }
}
클라이언트 측 소스코드
#include "sys/socket.h"
#include "sys/types.h"
#include "netinet/in.h"
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int sock, bytes_recieved;
    char send_data[1024],recv_data[1024];
    struct hostent *host;
    struct sockaddr_in server_addr;

    host = gethostbyname("127.0.0.1");

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9000);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    memset(&(server_addr.sin_zero), 0x00, 8);

    if (connect(sock, (struct sockaddr *)&server_addr,
                sizeof(struct sockaddr)) == -1) {
        perror("Connect");
        exit(1);
    }

    while(1) {
        printf("\nSEND : ");
        fgets(send_data, sizeof(send_data), stdin);

        if (strcmp(send_data , "q") != 0 && strcmp(send_data , "Q") != 0)
            send(sock,send_data,strlen(send_data), 0);

        else {
            send(sock,send_data,strlen(send_data), 0);
            close(sock);
            break;
        }
        bytes_recieved=recv(sock,recv_data,1024,0);
        recv_data[bytes_recieved] = '\0';

        if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0) {
            close(sock);
            break;
        }
        else
            printf("\nRECV = %s\n " , recv_data);
    }
    return 0;
}
서버측의 결과 화면
TCP Server Waiting ...
connection from (127.0.0.1 , 53672)
clientNUM=1
clientFD=4
accept [4]
===================================

connection from (127.0.0.1 , 53681)
clientNUM=2
clientFD=5
accept [5]
===================================
[4]RECV nnnnnn

[5]RECV aaaaaa
클라이언트 측의 결과 화면
./aa_client

SEND : nnnnnn

RECV = nnnnnn

 

 

 

==================================

 

 

테스트하고 구현하는 건 자유인데 요러고 나니 아주쬐금 이해가 된다.

 

반응형

'Language > C' 카테고리의 다른 글

poll 함수 예제  (8) 2012.07.26
GCC 컴파일러 에러 메세지 리스트(Error Message List)  (6) 2012.07.24
Unix Domain Socket 예제  (6) 2012.07.23
semget. semop, semctl 함수 예제  (12) 2012.07.17
mmap / munmap 함수 예제  (10) 2012.07.17

+ Recent posts