linux网络编程之IO复用select模式

utili.h:

#ifndef _UTILI_H
#define _UTILI_H

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<pthread.h>

#define BUFFER_MAX_SIZE 256

#define LISTEN_QUEUE_SIZE 5
//#define LISTEN_PORT       8080
//#define LISTEN_IP         "127.0.0.1"
#define COMMAND_MAX_SIZE 10
typedef enum {ADD, SUB, MUL, DIV, MOD, QUIT} CMD_TYPE;
typedef struct oper_st
{
    int op1;
    int op2;
    CMD_TYPE oper;
}oper_st;
int startup(const char *ip, short port)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket.");
        exit(1);
    }
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    //address.sin_addr.s_addr = INADDR_ANY;
    address.sin_addr.s_addr = inet_addr(ip);

    int yes = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    socklen_t addrlen = sizeof(struct sockaddr);
    int ret = bind(sockfd, (struct sockaddr*)&address, addrlen);
    if(ret == -1)
    {
        perror("bind.");
        close(sockfd);
        exit(1);
    }

    ret = listen(sockfd, LISTEN_QUEUE_SIZE);
    if(ret == -1)
    {
        perror("listen.");
        close(sockfd);
        exit(1);
    }

    return sockfd;
}


#endif // _UTILI_H

ser.c:


#include"../utili.h"
#include<sys/select.h>

#define MAX_CLIENT_NUM 5

void data_handler(int sockConn);

int main(int argc, char *argv[])
{
    int sockSer = startup(argv[1], atoi(argv[2]));

    fd_set set;

    int client_fd[MAX_CLIENT_NUM] = {0};
    int client_connect_num = 0;
    int max_sock = sockSer;

    int i;
    while(1)
    {
        FD_ZERO(&set);
        FD_SET(sockSer, &set);

        for(i=0; i<MAX_CLIENT_NUM; ++i)
        {
            if(client_fd[i] != 0)
            {
                FD_SET(client_fd[i], &set);
            }
        }

        int ret = select(max_sock+1, &set, NULL, NULL, NULL);
        if(ret == -1)
        {
            //error
            perror("select.");
            continue;
        }
        else if(ret == 0)
        {
            //tiemout
            printf("Server Time Out.\n");
            continue;
        }
        else
        {
            //1  accept
            if(FD_ISSET(sockSer, &set))
            {
                struct sockaddr_in addrCli;
                socklen_t addrlen = sizeof(struct sockaddr);
                int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
                if(sockConn == -1)
                {
                    perror("accept.");
                    continue;
                }
                if(client_connect_num >= MAX_CLIENT_NUM)
                {
                    char *msg = "Server Over Load.";
                    send(sockConn, msg, strlen(msg)+1, 0);
                    close(sockConn);
                    continue;
                }
                client_fd[client_connect_num++] = sockConn;
                if(sockConn > max_sock)
                    max_sock = sockConn;
            }
            //2  recv send
            else
            {
                for(i = 0; i<MAX_CLIENT_NUM; ++i)
                {
                    if(FD_ISSET(client_fd[i], &set))
                    {
                        data_handler(client_fd[i]);
                    }
                }
            }
        }

    }

    close(sockSer);
    return 0;
}


void data_handler(int sockConn)
{
    char msg[256];
    recv(sockConn, msg, 256, 0);
    printf("from client msg:> %s\n",msg);
    send(sockConn, msg, strlen(msg)+1, 0);
}

cli.c:


#include"utili.h"

int main(int argc, char *argv[])
{
    int sockCli = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addrSer;
    addrSer.sin_family = AF_INET;
    addrSer.sin_port = htons(atoi(argv[2]));
    addrSer.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t addrlen = sizeof(struct sockaddr);
    connect(sockCli, (struct sockaddr*)&addrSer, addrlen);

    char msg[256];
    char recvmsg[256];
    while(1)
    {
        printf("input msg:>");
        scanf("%s",msg);
        send(sockCli, msg, strlen(msg)+1, 0);
        recv(sockCli, recvmsg, 256, 0);
        printf("from server self msg:> %s\n",recvmsg);
    }

    close(sockCli);
    return 0;
}

该程序实现了IO复用下的,回显服务器模型(客户端发送消息给服务器,服务器收到信息后,再发送给客户端),用select模式服务器可以同时为多个客户端提供服务。本程序后续仍然需要改进,当一个客户端退出的时候,向服务器发送一个信号,服务器就退出服务了,那么这个服务器的健壮性就太差了。

以下的程序,实现了客户端选择想进行的操作和两个操作数,并以结构体的形式发送给服务器,服务器计算完成之后,再把计算结果返回到客户端。

utili.h:

#ifndef _UTILI_H
#define _UTILI_H

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<pthread.h>

#define BUFFER_MAX_SIZE 256

#define LISTEN_QUEUE_SIZE 5
//#define LISTEN_PORT       8080
//#define LISTEN_IP         "127.0.0.1"
#define COMMAND_MAX_SIZE 10
typedef enum {ADD, SUB, MUL, DIV, MOD, QUIT} CMD_TYPE;
typedef struct oper_st
{
    int op1;
    int op2;
    CMD_TYPE oper;
}oper_st;
int startup(const char *ip, short port)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket.");
        exit(1);
    }
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    //address.sin_addr.s_addr = INADDR_ANY;
    address.sin_addr.s_addr = inet_addr(ip);

    int yes = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    socklen_t addrlen = sizeof(struct sockaddr);
    int ret = bind(sockfd, (struct sockaddr*)&address, addrlen);
    if(ret == -1)
    {
        perror("bind.");
        close(sockfd);
        exit(1);
    }

    ret = listen(sockfd, LISTEN_QUEUE_SIZE);
    if(ret == -1)
    {
        perror("listen.");
        close(sockfd);
        exit(1);
    }

    return sockfd;
}


#endif // _UTILI_H

ser.c:



#include"utili.h"
#include<sys/select.h>

#define MAX_CLIENT_NUM 5

void data_handler(int sockConn);

int main(int argc, char *argv[])
{
    int sockSer = startup(argv[1], atoi(argv[2]));

    fd_set set;

    int client_fd[MAX_CLIENT_NUM] = {0};
    int client_connect_num = 0;
    int max_sock = sockSer;

    int i;
    while(1)
    {
        FD_ZERO(&set);
        FD_SET(sockSer, &set);

        for(i=0; i<MAX_CLIENT_NUM; ++i)
        {
            if(client_fd[i] != 0)
            {
                FD_SET(client_fd[i], &set);
            }
        }

        int ret = select(max_sock+1, &set, NULL, NULL, NULL);
        if(ret == -1)
        {
            //error
            perror("select.");
            continue;
        }
        else if(ret == 0)
        {
            //tiemout
            printf("Server Time Out.\n");
            continue;
        }
        else
        {
            //1  accept
            if(FD_ISSET(sockSer, &set))
            {
                struct sockaddr_in addrCli;
                socklen_t addrlen = sizeof(struct sockaddr);
                int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
                if(sockConn == -1)
                {
                    perror("accept.");
                    continue;
                }
                if(client_connect_num >= MAX_CLIENT_NUM)
                {
                    char *msg = "Server Over Load.";
                    send(sockConn, msg, strlen(msg)+1, 0);
                    close(sockConn);
                    continue;
                }
                client_fd[client_connect_num++] = sockConn;
                if(sockConn > max_sock)
                    max_sock = sockConn;
            }
            //2  recv send
            else
            {
                for(i = 0; i<MAX_CLIENT_NUM; ++i)
                {
                    if(FD_ISSET(client_fd[i], &set))
                    {
                        data_handler(client_fd[i]);
                    }
                }
            }
        }

    }

    close(sockSer);
    return 0;
}


void data_handler(int sockConn)
{
    oper_st op;
    int result;
    recv(sockConn, &op, sizeof(op), 0);
    if(op.oper == ADD)
        result = op.op1 + op.op2;
    else if(op.oper == SUB)
        result = op.op1 - op.op2;
    else if(op.oper == MUL)
        result = op.op1 * op.op2;
    else if(op.oper == QUIT)
    {
        printf("game over.");
    }
    send(sockConn, &result, sizeof(int), 0);
}

cli.c:



#include"utili.h"

int main(int argc, char *argv[])
{
    int sockCli = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addrSer;
    addrSer.sin_family = AF_INET;
    addrSer.sin_port = htons(atoi(argv[2]));
    addrSer.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t addrlen = sizeof(struct sockaddr);
    connect(sockCli, (struct sockaddr*)&addrSer, addrlen);

    char cmd[COMMAND_MAX_SIZE];
    oper_st op;
    int result;
    while(1)
    {
        printf("input cmd:>"); //add sub mul div mod quit
        scanf("%s",cmd);
        if(strcmp(cmd, "add") == 0)
            op.oper = ADD;
        else if(strcmp(cmd, "sub") == 0)
            op.oper = SUB;
        else if(strcmp(cmd, "mul") == 0)
            op.oper = MUL;
        else if(strcmp(cmd, "div") == 0)
            op.oper = DIV;
        else if(strcmp(cmd, "mod") == 0)
            op.oper = MOD;
        else if(strcmp(cmd, "quit") == 0)
        {
            op.oper = QUIT;
            send(sockCli, &op, sizeof(op), 0);
            break;
        }
        printf("input op1 and op2:>");
        scanf("%d %d", &op.op1, &op.op2);

        send(sockCli, &op, sizeof(op), 0);
        recv(sockCli, &result, sizeof(int), 0);
        printf("result = %d\n",result);
    }

    close(sockCli);
    return 0;
}

本程序和上面的程序存在一样的问题,但程序设计的基本功能可以实现,可以为不同的客户端提供计算服务。问题的关键在于select模式如何对客户端的离开做出分支处理,保证服务器不退出,进而继续为其他客户端提供服务。


网络编程真的博大精深,Unix卷一、卷二、linux高性能服务器都是需要好好啃的书。

猜你喜欢

转载自blog.csdn.net/qq_35353824/article/details/89313272