[다중 입출력 함수 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 함수 예제  (0) 2012.07.26
GCC 컴파일러 에러 메세지 리스트(Error Message List)  (0) 2012.07.24
select 함수 예제  (0) 2012.07.23
Unix Domain Socket 예제  (0) 2012.07.23
semget. semop, semctl 함수 예제  (0) 2012.07.17
mmap / munmap 함수 예제  (0) 2012.07.17

+ Recent posts