• 【Socket】linux网络多路复用IO技术


     

    1.mystery引入

     

       1)Select是一种多路复用IO输入输出模式,在linux的输入输出编程中通过select的轮询机制,发现可用/可读或可写的接口。
       2)低级socket程序中有一个共同点:都是基于阻塞式的编程方式
       3)非阻塞式是函数调用时不阻塞,不管函数执行成功与否,都会立即返回。
       4)优点:程序效率提升
       5)缺点:返回的结果往往是错误的类型码
       6)解决方案:Select机制。


    2.实例操

     

       1)基于Select模式实现一个网络echo的服务程序,即客户端向服务端发送信息,服务器接收到信息后,再将信息原样转发给客户端
       2)需要设置Select函数
       3)若当前有新连接,则加入到客户端套接字集合,若数量过载,则断开本次连接,并发送提示信息:sorry overload
       4)源代码
    //selectsocket.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #define SERVER_PORT 5555  
    #define QUEUE_LENGTH 5   
    #define BUF_SIZE 200
    int main(void)
    {
        int server_socket, new_socket;
        struct sockaddr_in server_addr;  
        struct sockaddr_in client_addr;
        socklen_t sin_size;
        int client_socket[QUEUE_LENGTH];  
        int conn_num;  
        int yes = 1;
        char buf[BUF_SIZE];
        int ret;
        int i;
        if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            perror("socket");
            return 0;
        }
        if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
        {
            perror("setsockopt");
            return 0;
        }
        server_addr.sin_family = AF_INET;       
        server_addr.sin_port = htons(SERVER_PORT);   
        server_addr.sin_addr.s_addr = INADDR_ANY;
        memset(server_addr.sin_zero, '', sizeof(server_addr.sin_zero));
        if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
        {
            perror("bind");
            return 0;
        }
        if (listen(server_socket, 5) == -1)
        {
            perror("listen");
            return 0;
        }
        printf("listen port %d
    ", SERVER_PORT);
        fd_set clientfdset;
        int maxsock;
        struct timeval tv;
        conn_num = 0;
        sin_size = sizeof(client_addr);
        maxsock = server_socket;
        while (1)
        {
            // initialize file descriptor set
            FD_ZERO(&clientfdset);
            FD_SET(server_socket, &clientfdset);
            // timeout setting
            tv.tv_sec = 15;
            tv.tv_usec = 0;
            // add active connection to fd set
            for (i = 0; i < QUEUE_LENGTH; i++)
            {
                if (client_socket[i] != 0)
                {
                    FD_SET(client_socket[i], &clientfdset);
                }
            }
            ret = select(maxsock + 1, &clientfdset, NULL, NULL, &tv);
            if (ret < 0)
            {
                perror("select");
                break;
            }
            else if (ret == 0)
            {
                printf("waitting timeout
    ");
                continue;
            }
            // check every fd in the set
            for (i = 0; i < conn_num; i++)
            {
                if (FD_ISSET(client_socket[i], &clientfdset))
                {
                    ret = recv(client_socket[i], buf, sizeof(buf), 0);
                    if (ret <= 0)
                    {      
                        printf("client[%d] close
    ", i);
                        close(client_socket[i]);
                        FD_CLR(client_socket[i], &clientfdset);
                        client_socket[i] = 0;
                    }
                    else
                    {   
                        printf("Client[%d] msg:%s
    ", i, buf);
                        send(client_socket[i], buf, sizeof(buf), 0);
                    }
                }
            }
            if (FD_ISSET(server_socket, &clientfdset))
            {
                new_socket = accept(server_socket, (struct sockaddr *)&client_addr, &sin_size);
                if (new_socket <= 0)
                {
                    perror("accept");
                    continue;
                }
                if (conn_num < QUEUE_LENGTH)
                {
                    client_socket[conn_num++] = new_socket;
                    printf("new client[%d] %s:%d
    ", conn_num,
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                    if (new_socket > maxsock)
                        maxsock = new_socket;
                }
                else
                {
                    send(new_socket, "sorry overload!", sizeof("sorry overload!"), 0);
                    close(new_socket);
                    break;
                }
            }
        }
        for (i = 0; i < QUEUE_LENGTH; i++)
        {
            if (client_socket[i] != 0)
            {
                close(client_socket[i]);
            }
        }
    }


    3.mystery注

     

       1)设置高级socket属性参数中的应用参数SO_REUSERADDR,实现地址的可重复利用
       2)FD_SET(int fd, fd_set *fdset):向文件描述符集合中增加一个新的文件描述符

       3)FD_CLR(int fd, fd_set *fdset):向文件描述符集合中删除一个文件描述符

     

     
     
  • 相关阅读:
    C programming course
    关于时间管理的培训心得
    吴老师,一路好走!
    自己实现Int32Collection(.Net 1.1),以及效率问题的体会
    《C陷阱与缺陷》和《C专家编程》两本书又翻印了
    暂时闲一会,写一点点面试体会吧
    人类没有一件事是值得烦恼的
    EP0N系统中简便可行的光纤保护方法
    越来越不想写代码了
    [转]PON关键技术-通用成帧协议研究
  • 原文地址:https://www.cnblogs.com/lcw/p/3159512.html
Copyright © 2020-2023  润新知