【从零开始的嵌入式生活】网络编程5——IO多路复用

简介: 【从零开始的嵌入式生活】网络编程5——IO多路复用

今天继续网络编程,基本的TCP和UDP实现方式我们已经可以实现了,接下来就是学习一些更加底层的原理了,预计这部分是需要七天文章对应一星期的写作。这周刚好完结,希望有人愿意跟我一起学习呀。


🧑🏻作者简介:一个学嵌入式的年轻人

✨联系方式:2201891280(QQ)

📔源码地址:https://gitee.com/xingleigao/study_qianrushi

⏳全文大约阅读时间: 60min


文章目录

IO模型及多路复用

IO模型

阻塞I/O模式

读阻塞

写阻塞

非阻塞模式I/O(不常用)

多路复用IO

1.fd_set()

2.select

写在最后

IO模型及多路复用

IO模型

在UNIX/Linux下主要有四种I/O模型


阻塞I/O:

最常用

非阻塞I/O:

可防止进程阻塞在I/O操作上,需要轮询

I/O多路复用

允许同时对多个I/O进行控制

信号驱动I/O:

一种异步通信模式

阻塞I/O模式

阻塞I/O模式时最普遍使用的I/O模式,大部分程序使用的都是阻塞模式的I/O

缺省情况下,套接字建立后所处于的模式就是阻塞式I/O模式

之前使用的很多函数在调用的时候会发生阻塞


读操作中 read、recv、recvfrom

写操作中的 write、send、sendto默认不阻塞

其他操作:accept、connect

读阻塞

进程调用read函数从套接字上读取数据,当套接字的接收缓冲区还没有数据可读,就会阻塞。

它会一直阻塞,等待套接字的接收缓冲区有数据可读。

经过一段时间后,缓冲区内接收数据,于是内核便去唤醒该进程,然后访问。

如果进程阻塞过程中,对方发生故障,就会永远阻塞下去。

写阻塞

写操作发生阻塞的情况要比读操作少很多,主要时发生在要写入的缓冲区的大小小于要写入的数据量。

这些,写操作不进行任何拷贝工作,讲发生阻塞。

一旦发送缓冲有足够的空间,内核将唤醒进程,将数据从用户缓冲区拷贝到相应的发送数据缓冲区。

UDP不等待确认,没有实际的发送缓冲区,所以UDP不存在发送缓冲区满的情况,在UDP套接字上执行写操作永远不会阻塞。

非阻塞模式I/O(不常用)

当我们将一个套接字设置为非阻塞模式,我们相当于告诉系统内核:“当请求I/O操作不能完成的时候不要休眠我,而是返回一个错误给我”。

当一个应用使用了非阻塞模式的套接字,它需要使用一个循环来不停测试是否一个文件描述符有数据可读(polling)

应用程序不停的polling内核来检查是否I/O操作已经就绪,这是一个极浪费CPU资源的操作。

非阻塞模式的实现


1、fcntl()函数

int fcntl(int fd, int cmd, long arg);
int flag;
flag = fcntl(sockfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(sockdf, F_SETEL, flag);

int b_on = 1;

ioctl(sock_fd, FIONBIO, &b_on);

2、ioctl()函数

 
         


多路复用IO

基本常识:

Linux中每个进程默认情况下,最多可以打开1024个文件,最多有1024哥文件描述符

文件描述符特点:

1. 非负整数

2. 从最小的数字分配

3. 每个进程启动时默认打开0、1、2三个文件描述符

多路复用针对不止套接字,也针对普通文件描述符。


1.fd_set()

void FD_ZERO(df_set *fdset)   //清空集合
void FD_SET(int fd, fd_set *fdset)  //把fd加入集合
void FD_CLR(int fd, fd_set *fdset)  //从集合中删去fd
int FD_ISSET(int fd, set *fdset)  //判断fd是否在set中


2.select

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


一般:填读集合,写集合填NULL,异常集合(带外数据)一般填NULL



struct timeval{
  long tv_sec;  /*seconds*/
  long tv_usec; /*microsecods*/
};


时间单位:1秒(s) = 103毫秒(ms) = 106微秒(us)=109纳秒(ns) = 1012皮秒(ps)

注:select推出后,集合表示有数据的集合,内核对fd_set进行了改变。


if(FD_ISSET(fd, &Rset){
  1. 若是监听套接字有数据,则有新客户端链接,则accept()
  2 .若是建立的连接套接字有数据,则读数据
  }


示例代码:


#include "net.h"
#define QUIT_STR "quit"
int main(void){
        int fd = -1;
        struct sockaddr_in sin;
        /*创建sockt fd*/
        if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){
               perror("socket");
                exit(1);
        }
        /*2.连接服务器*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换
        //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4
        if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){
                perror("inet_pton");
                exit(1);
        }
        if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) <0){
                perror("connect");
                exit(1);
        }
        fd_set rset;
        int maxfd = -1;
        struct timeval tout;
        char buf[BUFSIZ];
        int ret;
        while(1){
                FD_ZERO(&rset);
                FD_SET(0, &rset);
                FD_SET(fd, &rset);
                maxfd = fd;
                tout.tv_sec = 5;
                tout.tv_usec = 0;
                select(maxfd + 1, &rset, NULL, NULL, &tout);
                if(FD_ISSET(0,&rset)){
                        bzero(buf, BUFSIZ);
                        do{
                                ret = read(0, buf, BUFSIZ - 1);
                        }while(ret <0 &&EINTR == errno);
                        if(ret < 0){
                                continue;
                        }
                        if(!ret) break; //服务器关闭
                        if(write(fd, buf, strlen(buf)) < 0){
                                perror("write() to socket");
                                continue;
                        }
                        if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){
                                printf("Client is exiting!\n");
                                break;
                        }
                }
                if(FD_ISSET(fd, &rset)){//服务器给发送过了数据
                        //的读取套接字数据,处理
                        bzero(buf, BUFSIZ);
                        do{
                                ret = read(fd, buf, BUFSIZ - 1);
                        }while(ret <0 &&EINTR == errno);
                        if(ret < 0){
                                perror("read from socket");
                                continue;
                        }
                        if(!ret) break; //服务器关闭
                        printf("serve send mesg:%s\n",buf);
                        if((strlen(buf) > strlen(SERV_RESP_STR)) && !strncasecmp(buf + strlen(SERV_RESP_STR), QUIT_STR, strlen(QUIT_STR))){
                                printf("Client is exiting!\n");
                                break;
                        }
                }
        }
        /*4.关闭服务器*/
        close(fd);
        return 0;
}


写在最后

放假这两天有些懒了,今天我要更完这部分内容,主要是后面的用的也不多,前面的应用较多大家跟我一起改变世界。啊哈哈哈,求求大家给个三连再走吧


相关文章
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
594 0
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
1017 1
Linux C/C++之IO多路复用(aio)
|
监控 应用服务中间件 Linux
掌握并发模型:深度揭露网络IO复用并发模型的原理。
总结,网络 I/O 复用并发模型通过实现非阻塞 I/O、引入 I/O 复用技术如 select、poll 和 epoll,以及采用 Reactor 模式等技巧,为多任务并发提供了有效的解决方案。这样的模型有效提高了系统资源利用率,以及保证了并发任务的高效执行。在现实中,这种模型在许多网络应用程序和分布式系统中都取得了很好的应用成果。
429 35
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
775 23
|
机器学习/深度学习 API Python
Python 高级编程与实战:深入理解网络编程与异步IO
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发和 API 设计。本文将深入探讨 Python 在网络编程和异步IO中的应用,并通过实战项目帮助你掌握这些技术。
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
1050 15
|
消息中间件 NoSQL Java
面试官:谈谈你对IO多路复用的理解?
面试官:谈谈你对IO多路复用的理解?
481 1
面试官:谈谈你对IO多路复用的理解?
|
网络协议 物联网 API
Python网络编程:Twisted框架的异步IO处理与实战
【10月更文挑战第26天】Python 是一门功能强大且易于学习的编程语言,Twisted 框架以其事件驱动和异步IO处理能力,在网络编程领域独树一帜。本文深入探讨 Twisted 的异步IO机制,并通过实战示例展示其强大功能。示例包括创建简单HTTP服务器,展示如何高效处理大量并发连接。
444 1
|
监控 网络协议 Java
IO 多路复用? 什么是 IO 多路复用? 简单示例(日常生活)来解释 IO 多路复用 一看就懂! 大白话,可爱式(傻瓜式)教学! 保你懂!
本文通过日常生活中的简单示例解释了IO多路复用的概念,即一个线程通过监控多个socket来处理多个客户端请求,提高了效率,同时介绍了Linux系统中的select、poll和epoll三种IO多路复用的API。
1143 3
|
Java Linux
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
596 2