使用select实现I/O复用

实验条件:在使用一个服务器,多个客户端的情况下,实现回声效果,即客户端传什么内容给服务器,服务器将该内容返回。

具体实现:每个客户端将自己的进程号传到服务器端,服务器端将该内容传给客户端。

运行方法:

使用select实现I/O复用

server.cpp

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

#define MAX_BUFF_SIZE 1024

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

int main(int argc, char **argv) {
    if(argc < 2) handlError("<port>");

    int sock = socket(PF_INET, SOCK_STREAM, 0);
    if(-1 == sock) handlError("socket() error");

    struct sockaddr_in sockAdr;
    sockAdr.sin_family = AF_INET;
    sockAdr.sin_addr.s_addr = INADDR_ANY;
    sockAdr.sin_port = htons(atoi(argv[1]));

    socklen_t lenSock = sizeof(sockAdr); 

    if(bind(sock, (struct sockaddr *)&sockAdr, lenSock)) {
        handlError("bind() error");
    }

    if(listen(sock, 5)) {
        handlError("listen() error");
    }

    fd_set readSets, cpySets;
    FD_ZERO(&readSets);
    FD_SET(sock, &readSets);
    int maxFd = sock;
    struct timeval time;

    char message[MAX_BUFF_SIZE] = {0};

    while(true) {
        cpySets = readSets;
        time.tv_sec = 5;
        time.tv_usec = 0;

        printf("select start\n");

        int status = select(maxFd + 1, &cpySets, 0, 0, &time);
        if(-1 == status) {
            handlError("select() error");
        }

        printf("select end\n");
        
        if(0 == status) continue;

        printf("find read fd\n");

        for(int i = 0; i < maxFd + 1; i++) {
            if(FD_ISSET(i, &cpySets)) {
                if(sock == i) {
                    int clntSock = accept(sock, (struct sockaddr *)&sockAdr, &lenSock);
                    if(-1 == clntSock) handlError("accept() error");

                    FD_SET(clntSock, &readSets);
                    if(maxFd < clntSock) maxFd = clntSock;
                } else {
                    int length = read(i, message, sizeof(message));
                    if(length < 0) {
                        handlError("read() error");
                    } else if(0 == length) {
                        FD_CLR(i, &readSets);
                        close(i);
                    } else {
                        printf("client socket %d receive :%s\n", i, message);
                        write(i, message, sizeof(message));
                        memset(message, 0, sizeof(message));
                    }
                }
            }
        }
    }

    close(sock);

    return 0;
}

client.cpp

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

#define MAX_BUFSIZE 1024

int gSock = -1;

void handlError(char* msg) {
    fputs(msg, stderr);
    fputc('\n', stderr);

    exit(1);
}

void timeout(int sig) {
    if(SIGALRM == sig) {
        printf("quit pid :%d", getpid());
        exit(1);
    }
}

// 运行命令格式: ./cilent 127.0.0.1 9193
int main(int agrc, char **argv) {
    if(agrc < 3) handlError("<IP> <PORT>");

    int sock = gSock = socket(PF_INET, SOCK_STREAM, 0);

    if(-1 == sock) handlError("socket() error");

    struct sockaddr_in sockAdr;
    memset(&sockAdr, 0, sizeof(sockAdr));
    sockAdr.sin_family = AF_INET;
    sockAdr.sin_port = htons(atoi(argv[2]));
    inet_aton(argv[1], &sockAdr.sin_addr);

    if(connect(sock, (struct sockaddr *)&sockAdr, sizeof(sockAdr))) {
        handlError("connect() error");
    }

    // struct sigaction action;
    // action.sa_handler = timeout;
    // action.sa_flags = 0;
    // sigemptyset(&action.sa_mask);
    // sigaction(SIGALRM, &action, 0);
    // alarm(10);

    pid_t pid = getpid();
    char msgSend[MAX_BUFSIZE] = {0};
    sprintf(msgSend, "%d", pid);

    char msgRecv[MAX_BUFSIZE] = {0};
    while(true) {
        if(-1 == write(sock, msgSend, sizeof(msgSend))) {
            handlError(strcat(msgSend, " write() error"));
        }

        printf("write end\n");

        ssize_t len = read(sock, msgRecv, sizeof(msgRecv));
        if(0 == len) break;
        if(-1 == len) handlError(strcat(msgSend, " read() error"));

        printf("recv :%s\n", msgRecv);

        memset(msgRecv, 0, sizeof(msgRecv));
    }

    close(sock);

    return 0;
}

 

上一篇:I/O复用select、poll和epoll


下一篇:10.为客户端添加输入线程(Thread)