2012年4月1日日曜日

[C 语言] 系统调用 select 函数

select() 函数主要是建立在fd_set类型的基础上

1. 操作宏
fd_set set;
FD_ZERO(&set);       将 set 清零使集合中不含任何fd
FD_SET(fd, &set);    将 fd 加入 set 集合
FD_CLR(fd, &set);    将 fd 从 set 集合中清除
FD_ISSET(fd, &set);  测试 fd 是否在 set 集合中

2. 函数说明

int select(int nfds, fd_set *readset, fd_set *writeset, fd_set* exceptset, struct timeval *timeout);
测试指定的 fd 可读?可写?有异常条件待处理?

参数
nfds     : 需要检查的文件描述字个数, 三组 fd_set 中所含的最大 fd 值加 1
readset  : 结构体指针, 检查可读性的一组文件描述字
writeset : 结构体指针, 检查可写性的一组文件描述字
exceptset: 结构体指针, 检查是否有异常条件出现的文件描述字 (错误不包括在异常条件之内)
timeout  : 结构体指针, 三种可能
           1. timeout=NULL (阻塞, 直到有一个 fd 状态变化函数才返回)
           2. timeout=结构体设为非零 (阻塞, 等待固定时间, 有一个 fd 状态变化或者时间耗尽, 函数均返回)
           3. timeout=结构体时间设为 0 (非阻塞, 函数检查完每个 fd 后立即返回))

返回值
< 0: 错误
= 0: 超时
> 0: 返回状态变化的 fd 总数

3. 使用实例

3-1. 服务器
--
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<unistd.h>

char buffer[1024];
void *pthread_function(void *arg);
pthread_mutex_t work_mutex;

int main()
{
    int result;
    struct sockaddr_un server_address, client_address;
    int client_len;
    int server_socketfd, client_socketfd;
    pthread_t a_thread;

    result = pthread_mutex_init(&work_mutex, NULL);
    if(result != 0) {
        perror("pthread_mutex_init");
        exit(EXIT_FAILURE);
    }

    server_socketfd = socket(AF_UNIX, SOCK_STREAM, 0);

    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "server_socket");

    unlink("server_socket");

    result = bind(server_socketfd, (struct sockaddr*)&server_address, sizeof(server_address));
    if(result != 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    result = listen(server_socketfd, 5);
    if(result != 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    client_len = sizeof(client_address);
    while(1) {
        client_socketfd = accept(server_socketfd, (struct sockaddr*)&client_address, &client_len);
        result = pthread_create(&a_thread, NULL, pthread_function, (void *)client_socketfd);
        if(result != 0) {
            perror("pthread_create");
            exit(EXIT_FAILURE);
        }
    }
}

void *pthread_function(void *arg)
{
    int fd = (int) arg;
    int result;
    fd_set read_fds;
    int max_fds;
    struct timeval timeout = {3, 0};

    printf("%d id has connected!!\n", fd);
    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(0, &read_fds);
        FD_SET(fd, &read_fds);
        max_fds = fd + 1;

        pthread_mutex_lock(&work_mutex);
        printf("[server local] %d has get the lock\n", fd);
        result = select(max_fds, &read_fds, (fd_set *)NULL, (fd_set *)NULL, &timeout);
        if(result < 1) {
            printf("select");
        }
        if(FD_ISSET(0, &read_fds)) {
            memset(buffer, '\0', sizeof(buffer));
            fgets(buffer, sizeof(buffer), stdin);
            if((strncmp("quit", buffer, 4))==0) {
                printf("[server local] You have terminaled the chat!\n");
                pthread_mutex_unlock(&work_mutex);
                break;
            } else {
                result = write(fd, buffer, sizeof(buffer));
                if(result == -1) {
                    perror("write");
                    exit(EXIT_FAILURE);
                }
            }
        }
        if(FD_ISSET(fd, &read_fds)) {
            memset(buffer, '\0', sizeof(buffer));
            result = read(fd, buffer, sizeof(buffer));
            if(result == -1) {
                perror("read");
                exit(EXIT_FAILURE);
            } else if(result == 0) {
                printf("[from client] client has terminal the chat!\n");
                pthread_mutex_unlock(&work_mutex);
                break;
            } else {
                printf("[from client] receive message: %s", buffer);
            }
        }
        pthread_mutex_unlock(&work_mutex);
        usleep (1000 * 100 * 5);
    }
    close(fd);
    pthread_exit(NULL);
}
================================================================================
3-2. 客户端
--
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<string.h>
#include<sys/types.h>
#include<sys/time.h>

int main()
{
    int result;
    int socketfd;
    int len;
    struct sockaddr_un address;
    fd_set read_fds, test_fds;
    int fd;
    int max_fds;
    char buffer[1024];

    socketfd = socket(AF_UNIX, SOCK_STREAM, 0);

    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, "server_socket");
    len = sizeof(address);

    result = connect(socketfd, (struct sockaddr*)&address, len);
    if(result == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    FD_ZERO(&read_fds);
    FD_SET(0, &read_fds);
    FD_SET(socketfd, &read_fds);
    max_fds = socketfd +1;

    printf("Chat now!!\n");

    while(1) {
        test_fds = read_fds;
        result = select(max_fds, &test_fds, (fd_set *)NULL, (fd_set *)NULL, (struct timeval*)NULL);
        if(result < 1) {
            perror("select");
            exit(EXIT_FAILURE);
        }

        if(FD_ISSET(0, &test_fds)) {
            memset(buffer, '\0', sizeof(buffer));
            fgets(buffer, sizeof(buffer), stdin);
            if((strncmp("quit", buffer, 4))== 0) {
                printf("\n[client local] You are going to quit!\n");
                break;
            }
            result = write(socketfd, buffer, sizeof(buffer));
            if(result == -1) {
                perror("write");
                exit(EXIT_FAILURE);
            }
        }
        if(FD_ISSET(socketfd, &test_fds)) {
            memset(buffer, '\0', sizeof(buffer));
            result = read(socketfd, buffer, sizeof(buffer));
            if(result == -1) {
                perror("read");
                exit(EXIT_FAILURE);
            } else if(result == 0) {
                printf("[from server] server has termianl chat!\n");
                break;
            } else {
                printf("[from server] recieve message: %s", buffer);
            }
        }
    }
    close(socketfd);
    exit(EXIT_SUCCESS);
}

0 件のコメント:

コメントを投稿