当前位置: 首页 > news >正文

《LINUX系统编程》笔记p14

高级IO(Advanced IO)

标准IO是创建文件描述符并操作文件描述符进行读写的操作。

高级IO是IO中的一直用特殊现象,用于更方便的操作IO。

高级IO 包括

  • 非阻塞 IO
  • IO多路转接
  • 存储器映射
  • 文件锁等技术

非阻塞IO

非阻塞IO是让IO 变为非阻塞的方式进行读写操作。适用于多个IO 需要同时读取数据的场合(通常使用轮询)。

两种方法:

  1. open(2) 函数打开时指定打开方式标志位为 O_NONBLOCK。
  2. 使用 fcntl(2)的F*SETFL重设为 * O_NONBLOCK标志位。

使用非阻塞IO 轮询的方式实现UDP 客户端

// UDP 客户端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>// #define SERVER_IP "192.144.206.112"
#define SERVER_IP "127.0.0.1"
// #define SERVER_IP "10.11.9.208"
#define SERVER_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区char buf_write[BUF_SIZE];  // 写数据缓冲区// 创建套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");}sin.sin_family = AF_INET;sin.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &sin.sin_addr);// 将 sock_fd 设置成非阻塞IOint flag = fcntl(sock_fd, F_GETFL);flag |= O_NONBLOCK;ret = fcntl(sock_fd, F_SETFL, flag);if (-1 == ret) {perror("fcntl");return 3;}flag = fcntl(0, F_GETFL);flag |= O_NONBLOCK;ret = fcntl(0, F_SETFL, flag);if (-1 == ret) {perror("fcntl: 0");return 3;}// 与服务器端通信while(1) {ret = read(0, buf_write, BUF_SIZE);if (ret > 0) {buf_write[ret-1] = 0;// 将buf_write的内容发送给,服务器端.ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {perror("sendto");break;}if(0 == strcmp("exit", buf_write))break;}ret = recv(sock_fd, buf_read, BUF_SIZE-1, 0);if (ret > 0) {buf_read[ret] = 0;printf("服务器回复:%s\n", buf_read);}    }// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

假错:

IO读取时由于中断信号到来,导致阻塞IO立即返回,错误号 errno 等于 EINTR (error interrupt)。

IO 多路转接(也叫IO多路复用)

用一个函数来阻塞等待多个IO,当其中一个IO 有信号(有数据需要解除阻塞时),则此函数解除阻塞进行处理。

IO多路复用的三种方式:

  • select
  • poll
  • epoll(仅Linux支持)

select IO 多路转接

/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
// 参数
//     nfds 最大的 fd的值。
//     readfds 读集合,不用时可以置空NULL.
//     writefds 写集合,不用时可以置空NULL.
//     exceptfds 异常集合,不用时可以置空NULL.
//     timeout 超时时间,不用时可以置空NULL,表示不超时。
// 返回值:
//    大于0,是有信号的文件描述符的数量。
//    等于0, 超时。
//    小于0, 出错,同时设置错误号。struct timeval {long    tv_sec;         /* seconds */long    tv_usec;        /* microseconds */
};void FD_CLR(int fd, fd_set *set);  // 从 FD 集合 set中 删除fd 
int  FD_ISSET(int fd, fd_set *set); // 判断 fd 是否在 FD 集合set 中
void FD_SET(int fd, fd_set *set);  // 将 fd 加入 FD 集合 set
void FD_ZERO(fd_set *set); // 清空所有 FD 集合 set

使用 select 实现 udp客户端的代码

实现丢包是逻辑正常

// UDP 客户端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>// #define SERVER_IP "192.144.206.112"
#define SERVER_IP "127.0.0.1"
// #define SERVER_IP "10.11.9.208"
#define SERVER_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区char buf_write[BUF_SIZE];  // 写数据缓冲区// 创建套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");}sin.sin_family = AF_INET;sin.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &sin.sin_addr);// 使用 IO 多路复用对阻塞的IO进行统一管理int max_fd = sock_fd;  // 统一管理文件描述的最大值fd_set read_fds; // 创建一个读文件描述符集合fd_set rfds; // 创建一个读文件描述符,准备保存临时值struct timeval tv; // 用于设置超时时间FD_ZERO(&read_fds); // 清空集合FD_SET(0, &read_fds);FD_SET(sock_fd, &read_fds);// 与服务器端通信while(1) {tv.tv_sec = 10; // 设置超时时间为 10s.tv.tv_usec = 0;rfds = read_fds; // 复制 read_fds;printf("select ...\n");ret = select(max_fd+1, &rfds, NULL, NULL, &tv);if (-1 == ret) {perror("select");break;} else if (0 == ret) {printf("超时...\n");} else {if (FD_ISSET(0, &rfds)) { // 需要读取键盘ret = read(0, buf_write, BUF_SIZE);buf_write[ret-1] = 0;// 将buf_write的内容发送给,服务器端.ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {perror("sendto");break;}if(0 == strcmp("exit", buf_write))break;}if (FD_ISSET(sock_fd, &rfds)) { // 需要读取sock_fd;ret = recv(sock_fd, buf_read, BUF_SIZE-1, 0);buf_read[ret] = 0;printf("服务器回复:%s\n", buf_read);}}}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

poll IO 多路转接(IO多路复用)

作用:使用poll 函数同时等待多个IO(文件描述符)的读写信号。当某个IO 有读写需求是,则此函数解除阻塞并处理对应 IO 的读写请求。

poll 函数

#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// 参数
//     fds 用于存储多个 pollfd 的结构体数组的起始地址。
//     nfds 数据中有效数据的个数。
//     timeout 超时时间(毫秒)
// 
struct pollfd {int   fd;         /* file descriptor */short events;     /* requested events */short revents;    /* returned events */
};
// 参数:
//   fd 文件描述符
//   events 请求事件
//      POLLIN 输入事件
//      POLLOUT 输出事件
//      POLLHUP  检测需要挂起的数据流事件。
//   revents 返回的事件结果。
// 返回值:
//    大于0,是有信号的文件描述符的数量。
//    等于0, 超时。
//    小于0, 出错,同时设置错误号。

poll 实现 bot 的客户端

// UDP 客户端实现
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>   // man 7 ip
#include <netinet/ip.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <poll.h>// #define SERVER_IP "192.144.206.112"
#define SERVER_IP "127.0.0.1"
// #define SERVER_IP "10.11.9.208"
#define SERVER_PORT 6666#define BUF_SIZE (1024)int main(int argc, char * argv[]) {int sock_fd;int ret;struct sockaddr_in sin;  // 服务器端IPv4地址族结构struct sockaddr_in cin;  // 客户端IPv4地址族结构char buf_read[BUF_SIZE];  // 读数据缓冲区char buf_write[BUF_SIZE];  // 写数据缓冲区struct pollfd fds[1024];  // 用于方文件描述符和等待事件int fds_count = 0; // 用于记录fds数组中的数据个数.// 创建套接字sock_fd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sock_fd) {perror("socket");}sin.sin_family = AF_INET;sin.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &sin.sin_addr);// struct pollfd fds[1024];  // 用于方文件描述符和等待事件// int fds_count = 0; // 用于记录fds数组中的数据个数.// 将0 和 sock_fd 加入到 fds数组,并将 fds_count置为2fds[0].fd = 0;fds[0].events = POLLIN | POLLHUP; // 等待输入事件fds[1].fd = sock_fd;fds[1].events = POLLIN | POLLHUP; fds_count = 2;// 与服务器端通信while(1) {printf("poll...\n");ret = poll(fds, fds_count, 10000);if (-1 == ret) {perror("poll");continue; // 进行下一次等待} else if (0 == ret) {printf("timeout ...\n");continue;}if (fds[0].revents) { // 有键盘输入信号ret = read(0, buf_write, BUF_SIZE);buf_write[ret-1] = 0;// 将buf_write的内容发送给,服务器端.ret = sendto(sock_fd, buf_write, strlen(buf_write), 0, (struct sockaddr*)&sin, sizeof(sin));if (-1 == ret) {perror("sendto");break;}if(0 == strcmp("exit", buf_write))break;}if (fds[1].revents) { // 有UDP输入信号ret = recv(sock_fd, buf_read, BUF_SIZE-1, 0);buf_read[ret] = 0;printf("服务器回复:%s\n", buf_read);}}// 断开连接 close(sock_fd);printf("主进程结束\n");return 0;
}

文章转载自:

http://3kWG7tQi.dpLmq.cn
http://RkAmEWtf.dpLmq.cn
http://lvgBTXHM.dpLmq.cn
http://2q9JN6Gg.dpLmq.cn
http://jWE2IgE3.dpLmq.cn
http://zGtEL8sp.dpLmq.cn
http://UcjSeSwV.dpLmq.cn
http://9HHl5tVk.dpLmq.cn
http://U0m5Bish.dpLmq.cn
http://05d5D1nk.dpLmq.cn
http://QWErhNOa.dpLmq.cn
http://RrRaH4Pc.dpLmq.cn
http://Xby5Oa7n.dpLmq.cn
http://qTyYWk3V.dpLmq.cn
http://IlK0bXG1.dpLmq.cn
http://r7WjyDUx.dpLmq.cn
http://fvU95dZT.dpLmq.cn
http://uYzcz77F.dpLmq.cn
http://aOjlb2LO.dpLmq.cn
http://ed1hlvph.dpLmq.cn
http://BTQmPW31.dpLmq.cn
http://ajeFnYsp.dpLmq.cn
http://Sp0FVTdH.dpLmq.cn
http://jCIl6pDm.dpLmq.cn
http://hwf5OkTG.dpLmq.cn
http://EEnIecKn.dpLmq.cn
http://TucEXBe8.dpLmq.cn
http://xXJeDiFF.dpLmq.cn
http://xmUTPtTC.dpLmq.cn
http://ok62GEzv.dpLmq.cn
http://www.dtcms.com/a/387046.html

相关文章:

  • 可直接落地的pytest+request+allure接口自动化框架
  • 【精品资料鉴赏】267页政务大数据资源平台建设方案
  • 面试前端遇到的问题
  • 【深度学习计算机视觉】05:多尺度目标检测——从理论到YOLOv5实践
  • STM32 通过USB的Mass Storage Class读写挂载的SD卡出现卡死问题
  • 【Nginx开荒攻略】Nginx基本服务配置:从启动到运维的完整指南
  • 《漫威争锋》公布开发者愿景视频:介绍1.5版本的内容
  • Isight许可管理与其他软件集成的方法
  • 论文提纲:学术写作的“蓝图”,如何用AI工具沁言学术高效构建?
  • 快速解决云服务器的数据库PhpMyAdmin登录问题
  • 知识更新缺乏责任人会带来哪些风险
  • 容器化部署番外篇之Nexus3搭建私有仓库09
  • 计算机视觉(opencv)实战二十四——扫描答题卡打分
  • 居住证申请:线上照片回执办理!
  • Roo Code 的差异_快速编辑功能
  • 【深度学习】基于深度学习算法的图像版权保护数字水印技术
  • mcp初探
  • 深入C++对象生命周期:从构造到析构的奥秘
  • 视频上传以及在线播放
  • Powershell and Python are very similar
  • 鸿蒙Next离线Web组件实战:轻松实现离线加载与缓存优化
  • deepseek原理
  • 力扣复盘 之“移动零”
  • 任务管理系统常用平台整理:适合多项目团队
  • docker安装华为openGauss数据库
  • AI的设计图,神经网络架构
  • abaqus仿真完后如何把受力曲线显示出来
  • 核心硬件面试题目详解和回答策略之1
  • [MySQL]Order By:排序的艺术
  • Android创建新的自定义系统分区实现OTA内容修改