网络编程(学习)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

相关文章

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

shell脚本自动删除30天以前的文件(最新推荐)

《shell脚本自动删除30天以前的文件(最新推荐)》该文章介绍了如何使用Shell脚本自动删除指定目录下30天以前的文件,并通过crontab设置定时任务,此外,还提供了如何使用Shell脚本删除E... 目录shell脚本自动删除30天以前的文件linux按照日期定时删除elasticsearch索引s

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同