网络编程(学习)2024.8.30

2024-09-05 00:20
文章标签 学习 编程 网络 30 2024.8

本文主要是介绍网络编程(学习)2024.8.30,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

IO多路复用  select、poll、epoll

IO多路复用机制 

一.select

1.函数

2.流程

3.案例使用select创建全双工客户端

4.并发服务器

5.案例使用select创建全双工服务端

二.poll

1.函数

2.流程

3.案例使用poll创建全双工客户端

4.案例使用poll创建全双工服务端

三、epoll

1.流程

2.案例使用epoll创建全双工服务端

select,poll和epoll的特点:

1.select特点

2.poll特点

3.epoll特点

IO多路复用  select、poll、epoll

案例分析:键盘鼠标事件

同时对键盘和鼠标进行监听,当敲击键盘按下回车,就打印键盘输入的东西,动鼠标就要打印鼠标写入的内容。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>#define N 64
int main(int argc, char const *argv[])
{int mouse = open("/dev/input/mouse0", O_RDONLY);if (mouse < 0){perror("open失败");return -1;}char buf[N];while (1){memset(buf, 0, N);gets(buf);printf("buf:%s\n", buf);int ret = read(mouse, buf, N);if (ret < 0){perror("read失败");return -1;}else{printf("mouse:%s\n", buf);}}
}

IO多路复用机制 

使用I/O多路复用技术。其基本思想是:

1.先构造一张有关描述符的表,然后调用一个函数。
2.当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
3.函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

一.select

1.函数

#include <sys/select.h>
#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(超时时长));

一般写:select(int nfds,fd_set *readfds,NULL,NULL,NULL);

void FD_CLR(int fd, fd_set *set);        //将某一文件描述符在表里去除
int  FD_ISSET(int fd, fd_set *set);        //判断某一文件描述符是否在表里
void FD_SET(int fd, fd_set *set);        //将某一文件描述符放入表中
void FD_ZERO(fd_set *set);        //将表置零

2.流程

第一步:建表初始化
第二步:填表
第三步:监听表
第四步:判断,操作

e26c46e3e64c4269ba46d4f5e45ce5d6.png

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>#define N 64
int main(int argc, char const *argv[])
{int mouse = open("/dev/input/mouse0", O_RDONLY);if (mouse < 0){perror("open失败");return -1;}char buf[N];// 第一步:建表初始化fd_set readfds, tempfds;FD_ZERO(&readfds);// 第二步:填表FD_SET(0, &readfds);FD_SET(mouse, &readfds);// 第三步:监听表while (1){memset(buf, 0, N);temphfds = readfds;int ret = select(4, &tempfds, NULL, NULL, NULL);// 第四步:判断,操作if (ret == -1){perror("select失败");return -1;}if (FD_ISSET(0, &tempfds)){gets(buf);printf("buf:%s\n", buf);}if (FD_ISSET(mouse, &tempfds)){int n = read(mouse, buf, N);if (n < 0){perror("read失败");return -1;}else{printf("mouse:%s\n", buf);}}}return 0;
}

3.案例使用select创建全双工客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>int main(int argc, char const *argv[])
{// 1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sorkfd:%d\n", sockfd);// 2.连接unsigned short post = 0;char ip[15];printf("请输入ip地址");scanf("%s", ip);getchar();printf("请输入端口号");scanf("%hd", &post);getchar();struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(post);saddr.sin_addr.s_addr = inet_addr(ip);socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect失败\n");return -1;}// 3.接收
#define N 64char buf[N];while (1){// 第一步:建表初始化fd_set readfds, tempfds;FD_ZERO(&readfds);// 第二步:填表FD_SET(0, &readfds);FD_SET(sockfd, &readfds);// 第三步:监听表while (1){memset(buf, 0, N);tempfds = readfds;int ret = select(4, &tempfds, NULL, NULL, NULL);// 第四步:判断,操作if (ret == -1){perror("select失败");return -1;}if (FD_ISSET(0, &tempfds)){scanf("%s", buf);send(sockfd, buf, N, 0);}if (FD_ISSET(sockfd, &tempfds)){int ret = recv(sockfd, buf, N, 0);printf("服务端:%s\n", buf);}}}close(sockfd);return 0;
}

4.并发服务器

可以同时接收多个客户端的连接

5.案例使用select创建全双工服务端

sockfd只要创建并监听listen,就可以接收连接请求,即只要sockfd的读缓冲区可读,就可以创建连接。
客户端的连接请求会发给sockfd,如果可以建立连接的话,会在sockfd的缓冲区内保存
accept其实就是去sockfd的缓冲区里取连接

select创建并发服务器过程

创建并发服务器
创建连接      accept  --> sockfd
发送消息         gets    --> 0
接收          recv    --> 所有已连接的acceptfd

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>#define N 64
char buf[N];
#define ERR_MSG(msg)                           \do                                         \{                                          \fprintf(stderr, "line:%d ", __LINE__); \perror(msg);                           \} while (0)int main(int argc, char const *argv[])
{if (argc != 2){printf("用法:<port>\n");return -1;}// 1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sorkfd:%d\n", sockfd);// 2.bind绑定IP和Port端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));// saddr.sin_addr.s_addr = inet_addr("192.168.50.213");socklen_t addrlen = sizeof(saddr);
#if 0saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
#elsesaddr.sin_addr.s_addr = INADDR_ANY;
#endifif (bind(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){ERR_MSG("bind失败");return -1;}printf("bind成功\n");// 3.监听listen将主动套接字变为被动套接字if (listen(sockfd, 7) < 0){ERR_MSG("lisren失败");return -1;}printf("listen成功\n");// 第一步:建表初始化fd_set readfds, tempfds;FD_ZERO(&readfds);FD_SET(sockfd, &readfds);FD_SET(0, &readfds);int max = sockfd;while (1){memset(buf, 0, N);tempfds = readfds;int ret = select(max + 1, &tempfds, NULL, NULL, NULL);if (ret == -1){perror("select失败");return -1;}if (FD_ISSET(sockfd, &tempfds)){// 4.accept阻塞等待链接int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &addrlen);if (acceptfd < 0){ERR_MSG("accept失败\n");return -1;}printf("acceptfd:%d\n", acceptfd);printf("客户端ip:%s\t 端口号:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));FD_SET(acceptfd, &readfds);if (max < acceptfd){max = acceptfd;}}// 5.发送else if (FD_ISSET(0, &tempfds)){scanf("%s", buf);for (int i = 4; i <= max; i++){if (FD_ISSET(i, &readfds)){send(i, buf, N, 0);}}}// 6.接收for (int n = 4; n <= max; n++){if (FD_ISSET(n, &tempfds)){int ret = recv(n, buf, N, 0);if (ret < 0){perror("recv失败");return -1;}else if (ret > 0){printf("客户端:%s\n", buf);}else{printf("客户端acceptfd:%d退出\n", n);FD_CLR(n, &readfds);close(n);while (!FD_ISSET(max, &readfds)){max--;}}}}}close(sockfd);return 0;
}

二.poll

1.函数

#include <poll.h>

int poll(suct trpollfd *fds, nfds_t nfds, int timeout);

参数:

        fds:创建的pollfd结构体类型的数组

        nfds:数组的大小

        timeout:超时检测的时间,一般不用的话设置为-1

返回值:

        成功:0

        失败:-1

struct pollfd {
        int   fd;        //第一个成员变量 fd是向poll说明要监听哪个文件描述符
        short events;        //第二个成员变量 events 是向poll说明要对这个文件描述符的哪种事件进行监听,一般设置为POLLIN
        short revents;         //第三个成员变量 revents 是poll函数自动生成,当fd发生了events事件时,poll函数会将events(POLLIN)写入revents
};

2.流程

3.案例使用poll创建全双工客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>int main(int argc, char const *argv[])
{// 1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sorkfd:%d\n", sockfd);// 2.连接unsigned short post = 0;char ip[15];printf("请输入ip地址");scanf("%s", ip);getchar();printf("请输入端口号");scanf("%hd", &post);getchar();struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(post);saddr.sin_addr.s_addr = inet_addr(ip);socklen_t addrlen = sizeof(saddr);if (connect(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){perror("connect失败\n");return -1;}// 3.接收
#define N 64char buf[N];while (1){// 第一步:建表初始化struct pollfd fds[2];// 第二步:填表fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfd;fds[1].events = POLLIN;// 第三步:监听表while (1){memset(buf, 0, N);poll(fds, 2, -1);// 第四步:判断,操作// if (ret == -1)// {//     perror("select失败");//     return -1;// }for (int i = 0; i <= 1; i++){if (fds[i].revents == POLLIN){if (i == 0){scanf("%s", buf);send(sockfd, buf, N, 0);}if (i == 1){int ret = recv(sockfd, buf, N, 0);if (ret < 0){perror("recv失败");return -1;}else{printf("服务端:%s\n", buf);}}}}}}close(sockfd);return 0;
}

4.案例使用poll创建全双工服务端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <poll.h>#define N 64
char buf[N];
#define ERR_MSG(msg)                           \do                                         \{                                          \fprintf(stderr, "line:%d ", __LINE__); \perror(msg);                           \} while (0)int main(int argc, char const *argv[])
{if (argc != 2){printf("用法:<port>\n");return -1;}// 1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sorkfd:%d\n", sockfd);// 2.bind绑定IP和Port端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("192.168.50.213");socklen_t addrlen = sizeof(saddr);// #if 0//     saddr.sin_addr.s_addr = inet_addr("0.0.0.0");// #else//     saddr.sin_addr.s_addr = INADDR_ANY;// #endifif (bind(sockfd, (struct sockaddr *)&saddr, addrlen) < 0){ERR_MSG("bind失败");close(sockfd);return -1;}printf("bind成功\n");// 3.监听listen将主动套接字变为被动套接字if (listen(sockfd, 7) < 0){ERR_MSG("lisren失败");close(sockfd);return -1;}printf("listen成功\n");// 第一步:建表初始化struct pollfd fds[100];int last = -1;// 第二步:填表fds[++last].fd = 0;fds[last].events = POLLIN;fds[last].revents = 0;fds[++last].fd = sockfd;fds[last].events = POLLIN;fds[last].revents = 0;while (1){memset(buf, 0, N);int po = poll(fds, last + 1, -1);if (po == -1){perror("select失败");close(sockfd);return -1;}for (int i = 0; i <= last; i++){if (fds[i].revents == POLLIN){if (fds[i].fd == sockfd){// 4.accept阻塞等待链接int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &addrlen);if (acceptfd < 0){ERR_MSG("accept失败\n");return -1;}printf("acceptfd:%d\n", acceptfd);printf("客户端ip:%s\t 端口号:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));fds[++last].fd = acceptfd;fds[last].events = POLLIN;fds[last].revents = 0;}else if (fds[i].fd == 0){scanf("%s", buf);for (int j = 2; j <= last; j++){send(fds[j].fd, buf, N, 0);}}else{int ret = recv(fds[i].fd, buf, N, 0);if (ret < 0){perror("recv失败");close(sockfd);return -1;}else if (ret > 0){printf("客户端%s:%s\n", inet_ntoa(caddr.sin_addr), buf);}else{printf("客户端acceptfd:%d退出\n", fds[i].fd);close(fds[i].fd);fds[i] = fds[last];last--;}}}}}close(sockfd);return 0;
}

三、epoll

1.流程

2.案例使用epoll创建全双工服务端

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
int main(int argc, char const *argv[])
{if (argc != 2){printf("usage: <port>\n");return -1;}// 1.创建套接字-->tcp  流式套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);printf("sockfd:%d\n", sockfd);// 2.绑定IP和端口号// 填充通信结构体struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;            // 选定ipv4协议族saddr.sin_port = htons(atoi(argv[1])); // 绑定端口// saddr.sin_addr.s_addr = inet_addr(argv[1]); // 绑定ip地址
#if 1saddr.sin_addr.s_addr = INADDR_ANY; // 绑定ip地址
#elsesaddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 绑定ip地址
#endifsocklen_t len = sizeof(saddr);// 绑定if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0){perror("bind err");return -1;}printf("bind ok\n");// 3.启动监听 将主动套接字变成被动套接字if (listen(sockfd, 8) < 0){perror("listen err");return -1;}printf("listen ok\n");// 1.创建红黑树,拿到根节点---》建表int epfd = epoll_create(99);// 2.将关心的文件描述符挂载到树上---》填表struct epoll_event event;struct epoll_event events[10];event.data.fd = sockfd;event.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);// 3.去链表中拿事件char buf[128];while (1){int ret = epoll_wait(epfd, events, 10, -1);if (ret < 0){perror("errr");return -1;}else if (ret == 0){printf("nothing \n");}else{for (int i = 0; i < ret; i++){if (events[i].data.fd == sockfd){int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err");return -1;}printf("%d,login\n", acceptfd);event.data.fd = acceptfd;event.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfd, &event);}else{int ret = recv(events[i].data.fd, buf, sizeof(buf), 0);if (ret < 0){perror("recv err");return -1;}else if (ret > 0){printf("%d:%s\n", events[i].data.fd, buf);}else{printf("%d exit\n", events[i].data.fd);close(events[i].data.fd);event.data.fd = events[i].data.fd;event.events = EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &event);}}}}}return 0;
}

select,poll和epoll的特点:

1.select特点

优点:可跨平台,Linux、macos、windows都可用
      可以监听多个文件描述符
      轻量级

缺点:最大监听1024个文件描述符,最大监听1020个客户端连接
      每次都要进行轮询,消耗CPU
      每次都要拷贝一遍表

2.poll特点

优点:
1、优化了文件描述符的数量,监听的文件描述符数取决于数组的大小,数组大小受内存容量的限制。
2、不需要每次都拷贝一遍表
缺点:
1、需要轮询
2、只能用在UNIX原生系统下,不支持跨平台

3.epoll特点

优点:
1、超高并发,百万级并发
2、不需要轮询,因为有异步通知机制
3、不需要拷贝表
缺点:
    只能跑在Linux

这篇关于网络编程(学习)2024.8.30的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1137452

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依