select基本介绍

简介: select基本介绍

select基本介绍

1、前言

 

首先说一下观点:人人都应该懂:select、poll、epoll

 

无论是做web开发的,还是做大数据开发,都应该懂这些。

 

这三个知识点,很重要,原因有三条:(1) 面试中经常被问到。尤其是面试高级工程师,架构师的时候。(2) 一切高并发框架的基础技术支撑。Nginx,Redis,kafka等框架都立足于这些基 础技术。(3)select、poll、epoll代表着一种数据结构,代表着一种算法思想,应该好好的去学 习,去体会。其实很多人对数据结构和算法不了解,投入力度不大,觉得工作中用不到, 其实,其实这个观点要纠正一下。

 

2、文件与监控

 

在linux里面,一切都是文件,键盘,显示器等等,一切都是文件。每个文件都有一个id 来标识,被称为文件描述符。每个文件都有可读,可写,异常三大事件

 

类似: 在Java里面,除了基本类型,其他的一切都是对象,每个对象都有一个ID,也就是哈希 值。 

 

那如何监控这些事件呢?很简单,就是把它放在系统内核里面,让系统去监控。

 

可是放进去之后,事件没有发生怎么办?可以一直等着,那就是堵塞;如果不等,马上返 回,那就是非堵塞了。

 

这些文件事件的监控的具体实现就是select函数调用。所以说select函数非常重要啊。

 

3、select介绍

 

select本质就是文件事件的监控机制,是linux最本质,最核心的东西。

整个监控的流程分为几个基本步骤:第一步:确定监控的文件是什么,定义其文件id 第二步:把文件放到一个集合里面,可以放多个文件,也就可以监控多个文件。具体怎么放置,类似于bitmap,一个bit位表示一个文件。第四步:将文件集合,复制三份,并分别监控可读,可写,发生错误三种事件。第五步:将文件集合,放到系统内核,并设置过期时间。到一定时间,如果没有发生事件 就返回。 

 

int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);


下面说一下对函数的解释:


返回结果是int类型maxfd就是要监控的文件的集合 第二个参数readset,是第一个参数的复制,对可读事件的监控 第三个参数writeset,是第一个参数的复制,对可写事件的监控 第四个参数exceptset,是第一个参数的复制,对异常事件的监控 timeout,表示准备监控多久

 

4、select源码例子


//客户端
#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <sys/time.h>
int main() 
{ 
  //第一步:建立客户端socket
    int client_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  //第二步:规划地址,
  struct sockaddr_in address;
    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(9999); 
    int len = sizeof(address); 
  //第三步:建立连接
    int result = connect(client_sockfd, (struct sockaddr *)&address, len); 
    if(result == -1) 
    { 
         perror("oops: client2"); 
         exit(1); 
    } 
    char buffer = '@'; 
    write(client_sockfd, &buffer, 1); 
    read(client_sockfd, &buffer, 1); 
    printf("收到服务器的返回:%c\n", buffer); 
    close(client_sockfd); 
    printf("关闭链接\n");
  return 0; 
}


#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <sys/time.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <stdlib.h>
#define FALSE   0
#define TRUE    1
int main() 
{ 
  //第一步:建立一个socket
    int server_sockfd  = socket(AF_INET, SOCK_STREAM, 0);
  //第二步:建立一个地址,好比是规划局,规划出一块儿地 
  struct sockaddr_in server_address ;
    server_address.sin_family = AF_INET; 
    server_address.sin_addr.s_addr = htonl(INADDR_ANY); 
    server_address.sin_port = htons(9999); 
    int server_len = sizeof(server_address); 
  //第三步:将socket绑定到地址
  bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 
  //第四步:建立一个监听队列,监听队列最多容纳5个链接
  listen(server_sockfd, 5);
  //第五步:建立两个文件集合,将服务器socket加入到集合中,类似于一个哈希表,元素是socket,然后socket带着一个队列,队列里面放在它监听的队列,长度是5
  fd_set originalfds, forkfds; 
  FD_ZERO(&originalfds); 
    FD_SET(server_sockfd, &originalfds);
  //总结一下:上面的五步,其实就是一个哈希结构,
  while(TRUE) 
    {
        forkfds = originalfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量 
        printf("server开启等待模式\n");
        /*无限期阻塞,一直等待事件的发生*/
        int result = select(FD_SETSIZE, &forkfds, (fd_set *)0,(fd_set *)0, (struct timeval *) 0); 
    //FD_SETSIZE:系统默认的最大文件描述符,为什么是所有呢?留作一个疑问题
        if(result < 1) 
        { 
            perror("server is error"); 
            exit(1); 
        } 
        int fd = 0;
        for(fd = 0; fd < FD_SETSIZE; fd++) 
        {
            if(FD_ISSET(fd,&forkfds)) 
            { 
                if(fd == server_sockfd) 
                {//判断是否为服务器套接字,否则表示为客户请求连接。虽然在整个linux系列里面同时存在了n个文件,但是在此进程里面,就两种类型的文件:服务器端的socket,客户端的socket
                    struct sockaddr_in client_address; 
          int client_len = sizeof(client_address); 
                    int client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len); 
                    FD_SET(client_sockfd, &originalfds);
                    printf("添加监控client端的socket fd: %d\n", client_sockfd); 
                } 
                else 
                {
          int readcount; 
                    ioctl(fd, FIONREAD, &readcount);
                    if(readcount == 0) 
                    { //客户数据请求完毕,关闭套接字,从集合中清除相应描述符
                        close(fd); 
                        FD_CLR(fd, &originalfds); 
                        printf("移除监控client端的socket fd: %d\n", fd); 
                    }
                    else 
                    { //处理客户数据请求
            char buffer; 
                        read(fd, &buffer, 1); 
                        printf("处理client端的socket fd: %d\n", fd); 
            printf("收到客户端发送的数据:%c\n", buffer); 
                        write(fd, &buffer, 1); 
                    } 
                } 
            } 
        } 
    } 
    return 0;
}
目录
相关文章
|
8月前
|
SQL 数据库管理
第二章:基础查询与排序---SQL学习笔记
第二章:基础查询与排序---SQL学习笔记
85 0
|
8月前
|
关系型数据库 MySQL 数据库
【MySQL进阶之路 | 基础篇】排序(ORDER BY)与分页(LIMIT)
【MySQL进阶之路 | 基础篇】排序(ORDER BY)与分页(LIMIT)
|
8月前
|
关系型数据库 MySQL
MySQL union和union all的用法详解和区别
MySQL union和union all的用法详解和区别
567 0
|
关系型数据库 MySQL
MySQL深入浅出: order by if()与order by in()之条件排序
MySQL深入浅出: order by if()与order by in()之条件排序
317 0
|
SQL 存储 监控
【MySQL从入门到精通】【高级篇】(二十三)EXPLAIN的概述与table,id字段的剖析
上一篇文章我们介绍了【MySQL从入门到精通】【高级篇】(二十二)慢查询日志分析,SHOW PROFILE查看SQL执行成本,这篇文章我们接着来介绍一下MySQL中一个非常重要的命令 EXPLAIN。当我们定位到查询慢的SQL之后,我们就可以使用EXPLAIN或DESCRIBE工具做针对性的分析查询语句。 DESCRIBE 语句的使用方法与EXPLAIN语句是一样的,并且分析结果也是一样的。
204 0
【MySQL从入门到精通】【高级篇】(二十三)EXPLAIN的概述与table,id字段的剖析
|
SQL Oracle 关系型数据库
SQL的基本介绍
数据库是保存有组织的数据的容器,通常是一个文件或一组文件,可以将其看作电子化的文件柜。用户可以对文件中的数据执行新增、删除、更新、查询等操作。
237 0
|
Scala 开发者
Set 基本介绍和创建 | 学习笔记
快速学习 Set 基本介绍和创建
|
SQL 关系型数据库 MySQL
SQL练习题--5.6和5.7版本的Group by 用法以及中间表使用
M-统计每个老师教授课程的学生总数-if(expr1,expr2)
370 0
|
SQL 关系型数据库 MySQL
软件测试mysql面试题:Union和Union All命令之间有什么区别?
软件测试mysql面试题:Union和Union All命令之间有什么区别?
156 0
|
SQL 关系型数据库 MySQL
软件测试mysql面试题:编写SQL SELECT查询,该查询从Employee_Details表返回名字和姓氏。
软件测试mysql面试题:编写SQL SELECT查询,该查询从Employee_Details表返回名字和姓氏。
121 0

热门文章

最新文章