SOCKET 网络编程 IO多路复用、同步异步、阻塞非阻塞学习总结

一、IO多路复用概述

1.概念总结

1.1什么是多路复用?

如果使用多进程/多线程模式的话,创建进程和创建线程需要时间开销。在编写服务器客户端程序时,如果服务器性能不行而 客户端太多时这种代价很大。试想如果有一种方法能够同时监听按键设备、串口设备和网络socket的事件(可读、可写、出错),一旦事件发生就通知大家,并告诉是谁的事件、以及究竟什么事件发生了那就好了,这种实现方式就叫做多路复用。

1.2什么是IO

I/O在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

I/O按照设备来分的话,分为两种,其一是网络I/O,也就是通过网络进行数据的拉取和输出。还有一种是磁盘I/O,主要是对磁盘进行读写工作。

例如当我们打开浏览器进入百度页面查询资料,这个动作里就有Input和Output两个操作,首先浏览器需要发送数据到百度服务器告诉它此时你需要浏览百度我想要的html,这是output,而百度接收数据后返回我需要的数据给浏览器时此时变为input。而当我们仅向内存输入数据,便只需input,若仅从磁盘读数据便只需output。

2.同步异步、阻赛非阻塞

在Linux下进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:

2.1·同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

打个比方,朋友让你帮忙送东西,若朋友采用同步请求的机制通知你,那此时的你在收到朋友的消息后并不会立马回复朋友消息,而是选择送到后,再回复朋友。若朋友采用异步请求的机制通知你,那当你收到消息时,你便会回复朋友,“好的!”再去送,等送到后再通过回调等方式通知朋友。

所以,同步和异步最大的区别就是被调用方的执行方式和返回时机。同步指的是被调用方做完事情之后再返回,异步指的是被调用方先返回,然后再做事情,做完之后再想办法通知调用方。

2.2·阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作在没有接收完数据或者没有得到结果之前不会返回,需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

还是前一个例子,若采用阻塞方式,朋友在通知你后会一直等待你,别的什么事也不干。而若采用非阻塞的方式,朋友再告知你后便会去干自己的事,而不用一直等待你的消息。

2.3同步异步与阻塞非阻塞区别

同步异步、阻塞非阻塞的描述对象并不相同,两者互不冲突。

阻塞与非阻塞是形容调用者的,而同步和异步是形容被调用者完成被调用的工作这件事。
同步里面可以有阻塞的情形,也可以有非阻塞的性情。异步也可以有阻塞的情形和非阻塞的情形,两者并不矛盾。

二、多路复用三种模式

IO复用包括 select, poll, epoll 三种模式,那么什么是IO多路复用,这三种模式又有什么区别呢?

1.IO 多路复用

概念:I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。

总结来说,IO多路复用, 是找一个代抢课来帮你蹲守选课系统进行抢课, 这个期间你可以做些其他的事情,例如可以顺便玩玩游戏、看看电视剧等等。

2.三种模式的区别

(以下概念介绍以举例方式说明)

2.1select代选: 每有一个课程容量, select代选都不知道这个是不是你的需要的课程, 她需要一个一个询问, 并且select待选能力还有限, 最多一次帮你监视1024个课程容量;

2.2poll代选: 不限制盯着课程容量的数量, 只要是有容量的课程可选, 都会帮你去问是不是你需要的课程;

2.3epoll代选 :不限制盯着课程的数量, 并且也不需要一个一个去问. 那么如何做呢? epoll待选会为每个进系统里课程贴上一个大字条,上面写上课程的名字, 只要课程空余, epoll代选就知道这个是不是你所需要的课程, 然后代选再通知你;

3.三种模式流程图

3.1select

select()函数允许进程指示内核等待多个事件(文件描述符)中的任何一个发生,并只在有一个或多个事件发生或经历一段指定时 间后才唤醒它,然后接下来判断究竟是哪个文件描述符发生了事件并进行相应的处理。

select的缺点:

1. 单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫 描文件描述符,文件描述符数量越多,性能越差;

2. 内核 / 用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销;

3. select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;

4. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调 用还是会将这些文件描述符通知进程。

使用select()多路复用实现的服务器端示例代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static inline void msleep(unsigned long ms);
static inline void print_usage(char *progname);
int socket_server_init(char *listen_ip, int listen_port);
int main(int argc, char **argv)
{
 int listenfd, connfd;
 int serv_port = 0;
 int daemon_run = 0;
 char *progname = NULL;
 int opt;
 fd_set rdset;
 int rv;
 int i, j;
 int found;
 int maxfd=0;
 char buf[1024];
 int fds_array[1024];
 struct option long_options[] =
 { 
 {"daemon", no_argument, NULL, 'b'},
 {"port", required_argument, NULL, 'p'},
 {"help", no_argument, NULL, 'h'},
 {NULL, 0, NULL, 0}
 }; 
 progname = basename(argv[0]);
 /* Parser the command line parameters */
 while ((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
 { 
 swi
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值