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

 

1.mystery引入linux

 

   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, '\0', 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\n", 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\n");
            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\n", i);
                    close(client_socket[i]);
                    FD_CLR(client_socket[i], &clientfdset);
                    client_socket[i] = 0;
                }
                else
                {   
                    printf("Client[%d] msg:%s\n", 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\n", 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):向文件描述符集合中删除一个文件描述符网络