运维最新【Linux】select多路转接,真服了

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 前言
  • select的定位与接口分析
  • select的特点
  • select的缺点
  • 基于select的echo服务器代码实现
  • telnet测试

前言

对于服务器来讲,需要一刻不停地监听请求,提供服务
在这里插入图片描述

我们不希望服务器主程序卡住

服务器监听的本质是:while 1 { 等待数据的到来->数据到来后读取数据 }

由于recv是阻塞等待,所以我们想尽一切办法来处理这个阻塞问题

  1. 多线程,把每一个链接交给一个新线程处理,新线程recv阻塞等待对应套接字的时候不会干扰到主线程;这时,阻塞等待+数据读取由线程全权承担
  2. 我们让内核帮助我们检测指定的套接字的状态。一旦套接字有数据就绪,就会把某个数据结构的状态设置为就绪,主程序检测到就绪,就会读取数据。这时,主程序只要不停地去检测一批套接字的状态就行。这时,等待由内核帮我们承担,主程序只要承担数据读取的任务

select的定位与接口分析

select的定位:只负责等待,是就绪事件通知机制,它等待IO就绪;唯一和read,write,recv,send不同的是,select可以等待多个文件描述符

来看select接口:

int select(
	int nfds, 
	fd_set \*readfds,
	fd_set \*writefds, 
	fd_set \*exceptfds,
	struct timeval \*timeout
);

  1. timeval结构体:秒+微秒
    nullptr:永久等待,直到某个fd就绪才返回
    3,0:等待3秒,没有就绪就返回
    0,0:没有就绪立马返回
  2. fd_set:比特位的位置代表文件描述符的编号,比特位的内容代表是否关心
    作用:所有关心X事件的文件描述符,都应该添加在这个集合里
    输入:用户告诉内核,你要帮我检测一下在这个集合中的fd的X事件
    输出:内核告诉用户,你关心的fd,有哪些文件描述符已经就绪了不具有保存sock的功能,只具有通知的能力
  3. fd_set:位图
 /\* fd\_set for select and pselect. \*/
 typedef struct         
   {
     /\* XPG4.2 requires this member name. Otherwise avoid the name
 from the global namespace. \*/
 #ifdef \_\_USE\_XOPEN
     __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
 # define \_\_FDS\_BITS(set) ((set)->fds\_bits)
 #else
     __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
 # define \_\_FDS\_BITS(set) ((set)->\_\_fds\_bits)
 #endif
   } fd_set;

fd_set的作用是:比特位的位置代表哪一个文件描述符,比特位0/1代表文件描述符是否就绪

  1. 返回
    读就绪代表该文件描述符的底层数据已经就绪
    写就绪代表文件描述符可以继续写了
    异常就绪代表哪个文件描述符异常了

so

轮询检测有没有关心的fd就绪,覆盖式地写入位图,返回。所以select需要我们花一点点第三方变量把fds保存起来,一般是数组

在我们通知了一轮就绪的sock之后,可能还需要select帮助我们进行监测其他的fd,所以要对fd_set重新设置,故要提前保存

其中,监听套接字只关心读事件

获取新的fd之后我们不读它,因为我们不知道这个新的fd里面有没有读写事件就绪,所以要等下一轮循环

select的特点

  • select只能等待确定数量的文件描述符,有上限,可监控的fd的数量取决于fd_set的大小
  • 需要第三方数组存储fd
  • 每次循环都需要更新nfds的值

select的缺点

  • 每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小

基于select的echo服务器代码实现

  1. socket.hpp
#pragma once 
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>

namespace ns_sock
{
  static const int BACKLOG = 5;

  enum {
    SOCKET_ERROR = 2,
    BIND_ERROR,
    LISTEN_ERROR,
    ACCEPT_ERROR
  };

  class Sock {
  public:
    static int Socket() {
      int sock = socket(AF_INET, SOCK_STREAM, 0);
      if(sock == -1) {
        std::cerr << "create socket failed" << std::endl;
        exit(SOCKET_ERROR);
      }
      int opt = 1;
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
      return sock;
    }
    
    static void Bind(int sock, uint16\_t port) {
      sockaddr_in local;
      bzero(&local, sizeof(local));
      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = INADDR_ANY;
      if(bind(sock, (struct sockaddr\*)&local, sizeof(local)) == -1) {
        std::cerr << "bind error" << std::endl;
        exit(BIND_ERROR);
      }
    }

    static void Listen(int sock) {
      if(listen(sock, BACKLOG) == -1) {
        std::cerr << "listen failed" << std::endl;
        exit(LISTEN_ERROR);
      }
    }

    static void Accept(){}

  };
}//ns\_sock

  1. select.hpp
#pragma once 
#include "sock.hpp"
#include <sys/select.h>
namespace ns_select_server 
{
  static const uint16\_t g_default_port = 8081;
  static const int SELECT_ERROR = 6;
  static const int BUFFER_SIZE = 4096;

  class SelectServer {
    public:
      SelectServer(int port = g_default_port):\_listen\_sock(-1), \_port(port) {
        _listen_sock = ns_sock::Sock::Socket();
        ns_sock::Sock::Bind(_listen_sock, _port);
        ns_sock::Sock::Listen(_listen_sock);
        // 初始化第三方数组
        for(int i = 0; i < FD_SETSIZE; i++) {
          _fd_array[i] = -1;
        }


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.youkuaiyun.com/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值