前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多路IO转接服务器-select

多路IO转接服务器-select

原创
作者头像
买唯送忧
修改2021-09-16 10:23:05
8950
修改2021-09-16 10:23:05
举报
文章被收录于专栏:虚拟技术学习

一、思路

相较于前面的多线程服务器,多进程服务器一个accept监听所有客户端的连接,导致服务器的接收端异常繁忙,也就是什么事都需要服务器连接端来完成;对于多路io转接,则是服务器老板安排了一个小助手来帮忙,即对于所有请求,先由小助手进行分类,需要服务器端套接字老板的时候再联系老板,,任何老板再进行处理与客户端建立连接,或者进行通信;;

二、小助手-select函数

1、函数详解

(1)头文件---#include<sys/select.h>

(2)函数原型---int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds:监听所有文件描述符中,最大的文件描述符加一;

readfds:读文件描述符监听集合

writefds:写文件描述符监听集合

exceptfds:异常文件描述符监听集合

timeout: 大于0设置监听超时时长,NULL,阻塞监听,0,非阻塞监听,轮询

(3)select的辅助函数

void FD_CLR(int fd, fd_set *set);//将一个文件描述符从监听集合中移除;

int FD_ISSET(int fd, fd_set *set);//判断一个文件描述符是否在集合中;

void FD_SET(int fd, fd_set *set);//-----将待监听的文件符,添加到集合中;

void FD_ZERO(fd_set *set);//-----清空一个文件描述符集合

(4)select的优缺点

缺点:

监听数受文件描述符限制, 最大1024

检测满足条件的fd,自己添加业务逻辑提高小, 提高了编码难度;

优点:

跨平台。win, linux, macOS, Unix, mips, 类unix;

三、步骤

1、建立连接

(1)socket函数

这里使用的是Ipv4,TCP套接字,所以使用的接口是:lfd = socket(AF_INET, SOCK_STREAM, 0)如果是IPV6把AF_INET后面加个6,如果是UDP,那就是把第二个改成SOCK_DGRAM;

(2)setsockopt函数

主要是端口复用:固定写法就好,第一个参数是socket函数返回值套接字的文件描述符:int opt = 1; setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

(3)bind函数

bind(lfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));b这个函数主要目的就是将服务器的地址结构绑定到套接字lfd上,所以开始要设置服务器的ser_addr:ser_addr.sin_family = AF_INET, ser_addr.sin_port = htons(8888);ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);端口8888是可由自己设置的,,

(3)listen函数

设置监听的上限的函数,,并不是阻塞监听的函数listen(lfd, 128);

2、设置select函数

(1)设置fd_set

主要是初始化读文件描述符监听集合(主要使用的集合),然后设置辅助的读文件描述符监听集合 FD_ZERO(&aset);

FD_SET(lfd, &aset);

(2)select函数监听

返回的值就是这个时候有多少客户端请求服务端读取它们的信息或是连接请求或是读写请求int n = tcp.Select(maxfd + 1, &readSet, NULL, NULL, NULL);

(3)连接请求

即是由服务器端的套接字发出读请求,即要读取客户端的连接请求

代码语言:javascript
复制
if (FD_ISSET(lfd, &readSet)){
            //cfd = do_accept(lfd, tcp_select);
            cfd = tcp.Accept(lfd, (struct sockaddr*)&cli_addr, &cli_addr_len);
            FD_SET(cfd, &allSet);
            if (cfd > maxfd)maxfd = cfd;
            if (n == 1){
                continue;
            }
        }

(4)读写请求

代码语言:javascript
复制
        for (int i = lfd; i <= maxfd; i ++){
            if (FD_ISSET(i, &readSet)){
                //do_read(i);
                ret = tcp.Read(i, buf, sizeof(buf), allSet);
                
                if (ret > 0){
                    for (int j = 0; j < ret; j ++){
                        buf[j] = toupper(buf[j]);
                    }
                    tcp.Write(STDOUT_FILENO, buf, ret);
                    tcp.Write(i, buf, ret);
                }

            }
        }

附录全部代码

SelectTcp.h

代码语言:c++
复制
#ifndef __SELECTTCP_H_
#define __SELECTTCP_H_

#include<string>
#include<iostream>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
using namespace std;

class SelectTcp
{
public:
    SelectTcp(string note);
    int Socket(int domain, int type, int procotol);
    void Setsockopt(int lfd, int level, int optname, const void* optval, socklen_t optlen);
    void setAddr(struct sockaddr_in& serv_addr, int flg);
    void Bind(int lfd, const struct sockaddr* serv_addr, socklen_t size);
    void Listen(int lfd, int backlog);
    int Accept(int lfd, struct sockaddr* cli_addr, socklen_t* cli_addr_len);
    int Select(int nfds, fd_set* readfds, fd_set* writefds, fd_set*exceptfds, struct timeval* timeout);
    int Read(int fd, void* buf, size_t size, fd_set& set);
    int Write(int fd, const void* buf, size_t size);

};



SelectTcp::SelectTcp(string note){
    cout << note << endl;
}

int SelectTcp::Socket(int domain, int type, int procotol){
    int ret = socket(domain, type, procotol);
    if (ret == -1){
        cout << "socket() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }
    return ret;
}

void SelectTcp::Setsockopt(int lfd, int level, int optname, const void* optval, socklen_t optlen){
    int ret = setsockopt(lfd, level, optname, optval, optlen);
    if (ret == -1){
        cout << "setsockopt() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }    
}

void SelectTcp::setAddr(struct sockaddr_in& serv_addr, int flg){
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8888);
    if (flg == 1){ //服务端
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } else { //客户端
        inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    }
}

void SelectTcp::Bind(int lfd, const struct sockaddr* serv_addr, socklen_t size){
    int ret = bind(lfd, serv_addr, size);
    if (ret == -1){
        cout << "bind() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }  
}

void SelectTcp::Listen(int lfd, int backlog){
    int ret = listen(lfd, backlog);
    if (ret == -1){
        cout << "listen() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }    
}

int SelectTcp::Accept(int lfd, struct sockaddr* cli_addr, socklen_t* cli_addr_len){
    int ret = accept(lfd, cli_addr, cli_addr_len);
    if (ret == -1){
        cout << "accept() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }
    return ret;  
}

int SelectTcp::Select(int nfds, fd_set* readfds, fd_set* writefds, fd_set*exceptfds, struct timeval* timeout){
    int ret = select(nfds, readfds, writefds, exceptfds, timeout);
    if (ret == -1){
        cout << "select() occurr fail   " << strerror(errno) << endl;
        exit(1);
    }
    return ret;    
}

int SelectTcp::Read(int fd, void* buf, size_t size, fd_set& set){
    int ret = read(fd, buf, size);
    if (ret == 0){
        close(fd);
        FD_CLR(fd, &set);
    } else if (ret > 0){
        return ret;
    } else if (ret == -1) {
        cout << "read() occurr fail  " << strerror(errno) << endl;
        exit(1);
    }
}

int SelectTcp::Write(int fd, const void* buf, size_t size){
    int ret = write(fd, buf, size);
    if (ret == -1){
        cout << "write() occurr fail  " << strerror(errno) << endl;
        exit(1);
    }
    return ret;
}



#endif

server_select.cpp

代码语言:c++
复制
#include "selectTcp.h"
#include<ctype.h>

int initSocketConnect(SelectTcp& tcp, struct sockaddr_in& serv_addr){
    int opt = 1;
    int lfd = tcp.Socket(AF_INET, SOCK_STREAM, 0);
    tcp.Setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    tcp.setAddr(serv_addr, 1);
    tcp.Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    tcp.Listen(lfd, 128); 
    return lfd;
}

void initSelectSet(fd_set& aset, int lfd){
    FD_ZERO(&aset);
    FD_SET(lfd, &aset);
}

int do_accept(int lfd, SelectTcp& tcp){
}

void do_read(int cfd){
}

int main(){
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t cli_addr_len;
    int n, ret, maxfd, cfd;
    char buf[BUFSIZ];
    fd_set readSet, allSet;
    SelectTcp tcp("the server is ready!!!");

    int lfd = initSocketConnect(tcp, serv_addr);  //初始化阻塞监听前的信息;;;

    initSelectSet(allSet, lfd);                          //初始化select之前的信息;
    maxfd = lfd;

    cout << "the server start listening the connection!!!!" << endl;
    while(1){
        cli_addr_len = sizeof(cli_addr);
        readSet = allSet;
        n = tcp.Select(maxfd + 1, &readSet, NULL, NULL, NULL);
        if (FD_ISSET(lfd, &readSet)){
            //cfd = do_accept(lfd, tcp_select);
            cfd = tcp.Accept(lfd, (struct sockaddr*)&cli_addr, &cli_addr_len);
            FD_SET(cfd, &allSet);
            if (cfd > maxfd)maxfd = cfd;
            if (n == 1){
                continue;
            }
        }

        for (int i = lfd; i <= maxfd; i ++){
            if (FD_ISSET(i, &readSet)){
                //do_read(i);
                ret = tcp.Read(i, buf, sizeof(buf), allSet);
                
                if (ret > 0){
                    for (int j = 0; j < ret; j ++){
                        buf[j] = toupper(buf[j]);
                    }
                    tcp.Write(STDOUT_FILENO, buf, ret);
                    tcp.Write(i, buf, ret);
                }

            }
        }


    }

}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、思路
  • 二、小助手-select函数
    • 1、函数详解
      • (1)头文件---#include<sys/select.h>
      • (2)函数原型---int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
      • (3)select的辅助函数
      • (4)select的优缺点
  • 三、步骤
    • 1、建立连接
      • (1)socket函数
    • (2)setsockopt函数
      • (3)bind函数
      • (3)listen函数
    • 2、设置select函数
      • (1)设置fd_set
      • (2)select函数监听
      • (3)连接请求
      • (4)读写请求
  • 附录全部代码
    • SelectTcp.h
      • server_select.cpp
      相关产品与服务
      云服务器
      云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档