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

相关文章

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Linux网络配置之网桥和虚拟网络的配置指南

《Linux网络配置之网桥和虚拟网络的配置指南》这篇文章主要为大家详细介绍了Linux中配置网桥和虚拟网络的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、网桥的配置在linux系统中配置一个新的网桥主要涉及以下几个步骤:1.为yum仓库做准备,安装组件epel-re

python如何下载网络文件到本地指定文件夹

《python如何下载网络文件到本地指定文件夹》这篇文章主要为大家详细介绍了python如何实现下载网络文件到本地指定文件夹,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下...  在python中下载文件到本地指定文件夹可以通过以下步骤实现,使用requests库处理HTTP请求,并结合o

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen