网络IO 多路IO复用 之 select

简介: 网络IO 多路IO复用 之 select

网络IO 多路IO复用

select

poll

epoll

 

为什么要用多路io复用呢?

如果不用多路io复用,可以开辟多个线程,或者一个线程循环的去读取每个io的数据,这样的开销是非常的大的,而且会大大的影响系统的性能,因此引入多路io复用

 

select 用于多路io复用,具体是如何实现的?

内部使用fd_set的方式来进行实现,因为做select 只需要标志自己内部的fd 可用还是不可用,则相当于是1个bit位, 0/1的取值。

因此使用fd_set 位图的方式来进行对select的实现

 

select的代码实现流程

1、创建sockfd

socket(AF_INET,SOCK_STREAM,0)

2、初始化addr 属性 struct sockaddr_in (sa_handler ,sa_port,sa_addr.s_addr)

3、bind sockfd

4、listen 设置最大连接数

5、定义select 的集合,fd_set,定义检测集合 rfds,具体使用的集合rsets

6、传入select 参数中,select 的参数,5个,maxfd+1,可读集合,可写集合,异常集合,时间 -- 返回值为 select监听到的连接数 nready

7、判断 sockfd 是否在rset集合里面,如果在,则定义客户端的数据结构, 和定义客户端数据结构的长度,需要传入到accept中

8、开始iaccept,返回clientfd, 把 clientfd 放入 rfds中, 且更新maxfd的大小

9、如果--nready ==0 ,则继续select 阻塞监听,若--nready 大于零, 则是 客户端和服务器进行数据通信

10、数据通信的时候服务器 使用 recv/read 读取数据,放入到buff中,

如果函数返回值是 <0 ,则需要判断,错误号若为 EAGAIN || EWOULDBLOCK -- 被信号打断了,未读取到数据,将当前客户端的fd移除rfds集合,且关闭 当前客户端fd

如果函数返回值是 == 0,说明客户端断开连接了,将当前客户端的fd移除rfds集合,且关闭 当前客户端fd

如果函数返回值是 > 0,说明正常读取到数据,开始对数据进行处理

 

最后 关闭sockfd 套接字 close(sockfd)

 

编码

写一个简单的demo,来感受一下select的使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/poll.h>
#include <unistd.h>
#define BUFFER_LENGTH 1024
int main(int argc,char * argv[])
{
//端口从命令行输入   如 : ./server 8888
  if(argc < 2)
  {
    perror("please input port nunber");
    return -1;
  }
//建立tcp套接字
  int sockfd = socket(AF_INET,SOCK_STREAM,0);
  if(sockfd < 0)
  {
    perror("socket");
    return -1;
  }
//初始化属性
  struct sockaddr_in seraddr;
  memset(&seraddr,0,sizeof(seraddr));
  seraddr.sin_family = AF_INET;
  seraddr.sin_port = htons(atoi(argv[1]));
  seraddr.sin_addr.s_addr = INADDR_ANY;
//bind
  int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr_in));
  if(ret < 0){
    perror("bind");
    return -1;
  }
//设置最大连接数
  ret = listen(sockfd,10);
  if(ret < 0){
    perror("listen");
    return -1;
  }
//select
  fd_set rset,rfds;
  FD_ZERO(&rfds);
  FD_SET(sockfd,&rfds);
  int maxfd = sockfd;
//开始select监听
  while(1){
    rset = rfds;
    int nready = select(maxfd+1,&rset,NULL,NULL,NULL);
    if (nready < 0) {
      printf("select error : %d\n", errno);
      continue;
    }
//开始accept
    if(FD_ISSET(sockfd,&rset)){
      struct sockaddr_in client_addr;
      memset(&client_addr, 0, sizeof(struct sockaddr_in));
      socklen_t client_len = sizeof(client_addr);
      int clientfd = accept(sockfd,(struct sockaddr*)&client_addr,&client_len);
      if(clientfd <= 0){
        printf("accept error\n");
        continue;
      }
//打印消息
      char str[BUFFER_LENGTH] = {0};
      printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
        ntohs(client_addr.sin_port), sockfd, clientfd);
      if (maxfd == FD_SETSIZE) {
        printf("clientfd --- out range\n");
        break;
      }
      FD_SET(clientfd,&rfds);
      if (clientfd > maxfd) maxfd = clientfd;
      printf("\nsockfd:%d, maxfd:%d, clientfd:%d\n\n", sockfd, maxfd, clientfd);
      printf("nready == %d\n",nready);
      if (--nready == 0) continue; //说明 当前请求的都是连接,并没有发数据
    }
//客户端开始和服务器进行数据的传输
    for(int i = sockfd+1;i<=maxfd;i++){
      if(FD_ISSET(i,&rset)){
        char buffer[BUFFER_LENGTH] = {0};
        int ret = recv(i, buffer, BUFFER_LENGTH, 0);
        if (ret < 0) {
          if (errno == EAGAIN || errno == EWOULDBLOCK) {
            printf("read all data");
          }
          FD_CLR(i, &rfds);
          close(i);
        } else if (ret == 0) {
          printf(" disconnect %d\n", i);
          FD_CLR(i, &rfds);
          close(i);
          break;
        } else {
          printf("Recv: %s, %d Bytes\n", buffer, ret);
          //将客户端发过的数据,发回去
          send(i, buffer, BUFFER_LENGTH, 0);
        }
        //减去一个客户端的fd
        if (--nready == 0) break;
      }
    }
  }
  close(sockfd);
  return 0;
}

验证和调试

 

 

相关文章
|
2月前
|
存储 网络协议 算法
从HPACK到多路复用,揭秘HTTP/2如何终结网络拥堵
HTTP/2通过HPACK压缩头部冗余信息,提升传输效率;并利用多路复用技术,在单个TCP连接上并行处理多个请求,避免队头阻塞,显著提升性能。同时支持服务器推送和流优先级设置,优化资源加载体验。
202 7
|
6月前
|
监控 应用服务中间件 Linux
掌握并发模型:深度揭露网络IO复用并发模型的原理。
总结,网络 I/O 复用并发模型通过实现非阻塞 I/O、引入 I/O 复用技术如 select、poll 和 epoll,以及采用 Reactor 模式等技巧,为多任务并发提供了有效的解决方案。这样的模型有效提高了系统资源利用率,以及保证了并发任务的高效执行。在现实中,这种模型在许多网络应用程序和分布式系统中都取得了很好的应用成果。
213 35
|
Ubuntu 网络协议 Unix
02理解网络IO:实现服务与客户端通信
网络IO指客户端与服务端通过网络进行数据收发的过程,常见于微信、QQ等应用。本文详解如何用C语言实现一个支持多客户端连接的TCP服务端,涉及socket编程、线程处理及通信流程,并分析“一消息一线程”模式的优缺点。
286 0
|
8月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
351 23
|
8月前
|
机器学习/深度学习 API Python
Python 高级编程与实战:深入理解网络编程与异步IO
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发和 API 设计。本文将深入探讨 Python 在网络编程和异步IO中的应用,并通过实战项目帮助你掌握这些技术。
|
9月前
|
缓存 网络协议 Java
JAVA网络IO之NIO/BIO
本文介绍了Java网络编程的基础与历史演进,重点阐述了IO和Socket的概念。Java的IO分为设备和接口两部分,通过流、字节、字符等方式实现与外部的交互。
296 0
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
264 17
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
223 10
|
11月前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
11月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
234 10