网络异步编程

网络异步编程

一、tcp连接的状态
  1. LISTEN:服务端状态,等待客户端发起连接请求
  2. SYN_SENT:客户端已发送同步连接请求,等待服务端相应
  3. SYN_RECEIVED:服务器收到客户端的SYN请请求,并发送自己的SYN响应,并等待客户端对这个SYN+ACK的确认(等待客户端连接确认);
  4. ESTABLISHED:双方完成三次握手,连接成功,可以进行数据传输;
  5. FIN_WAIT_1:主动关闭连接的一方(通常是客户端)已经发送FIN报文,但是还未收到对方的确认。此时仍可以进行数据接收;
  6. FIN_WAIT_2:主动关闭一方收到了对方的FIN确认,但是没收到对方的FIN,进入半连接状态,仅能接收数据;
  7. CLOSE_WAIT:被动关闭连接的一方已经收到FIN,并发送了确认,但尚关闭连接,等待应用层释放资源;
  8. CLOSING:双方都发送了关闭请求,都在等待对方确认;
  9. LAST_ACK:被动关闭的一方发送了FIN,等待最后的ACK来关闭连接;
  10. TIME_WAIT:主动关闭方发送完FIN,并收到对方的FIN+ACK后进入该状态,等待足够长的时间确保对方能够收到确认后再关闭连接;
  11. 所有连接终止程序完成后,套接字回到CLOSE状态
二、客户端的状态过程:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

三、服务端的状态过程

CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

四、本地网络IP
  • eth0:192.168.0.45 是实际网络设备接口及对应公网或内网IP地址。

  • lo:127.0.0.1 是本地回环接口,仅用于主机内部通信

  • any:0.0.0.0 在服务配置中通常用作通配符,指示服务监听所有网络接口上的流量。

五、文件描述符
  • stdin:0 表示标准写入的文件描述符

  • stdout:1 表示标准输出的文件描述符

  • stderr:2 表示标错误输出的文件描述符

每个线程的前三个文件描述符都是这三个
在这里插入图片描述

  • 客户端操作过程 socket()->bind()->connect()->send()->recv()->close()

  • 服务端操作过程 socket()->bins()->listen()->accept()->recv()->send()->close()

六、多线程网络I/O
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <pthread.h>void *client_thread(void *arg)
{int client_socket = *(int *)arg;while (true){char buffer[1024];memset(buffer, 0, sizeof(buffer));int count = recv(client_socket, buffer, sizeof(buffer), 0); // 读取客户端发送的数据if (count == 0)break;printf("receive data: %s\n", buffer);send(client_socket, buffer, strlen(buffer), 0); // 发送数据给客户端}close(client_socket); // 关闭连接
}int main()
{int socketfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个TCP socketstruct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 设置服务器的IP地址//servaddr.sin_addr.s_addr = ntohl(INADDR_ANY); servaddr.sin_port = htons(1234); // 设置服务器的端口号if (-1 == bind(socketfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) // 将socket绑定到指定的地址和端口{perror("bind");return -1;}listen(socketfd, 10); // 开始监听,允许最多10个连接请求在等待队列中排队while (true){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(socketfd, (struct sockaddr *)&clientaddr, &clientaddrlen); // 接受一个连接请求,printf("accept a new connection\n");pthread_t tid;pthread_create(&tid, NULL, client_thread, (void *)&connfd); // 创建一个线程来处理连接}  close(socketfd); // 关闭socketreturn 0;
}
  • TIME_WAIT状态是为了确保TCP状态的可靠终止,防止旧数据包在网络中滞留

  • 服务频繁的创建和关闭会导致出现大量的TIME_WAIT状态的进程;一般是服务器主动去断开连接

  • 代码中出现大量的TIME_WAIT,可以使用netstat -anop 查看网络连接状态

select、pool、epoll是Linux中用于实现I/O多路复用的三种机制。主要是在单个线程中同时监控多个文件描述符的状态变化,包括可读、可写等从而实现提高服务并发处理的能力;

七、SELECT
  • 通过一个集合监控文件描述符列表,默认FD_SETSIZE大小位1024
  • 每次调用时都需要将整个文件描述符集合从用户空间拷贝到内核空间,每次需要遍历每个文件描述符的状态,返回就绪集合,效率回随着文件描述符的增加而降低
    //select//select(maxfd, &rset, &wset, &error, timeout);fd_set rfds, rset;FD_ZERO(&rfds);FD_SET(socketfd, &rfds);struct timeval timeout;// timeout.tv_sec = 5;// timeout.tv_usec = 0;int maxfd = socketfd;while (true){rset = rfds;int nready = select(maxfd + 1, &rfds, NULL, NULL, NULL);if (nready > 0){if (FD_ISSET(socketfd, &rfds)){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(socketfd, (struct sockaddr *)&clientaddr, &clientaddrlen);printf("accept a new connection\n");FD_SET(connfd, &rfds);maxfd = connfd;}for (int i = socketfd + 1; i <= maxfd; i++){if (FD_ISSET(i, &rfds)){char buf[1024];memset(buf, 0, sizeof(buf));int count = recv(i, buf, sizeof(buf), 0);if (count == 0){close(i);FD_CLR(i, &rfds);break;}//else{send(i, buf, count, 0);printf("send %d bytes data to client, send data:%s\n", count, buf);}}}}}
八、POLL
  • 与select一样每次需要将监控的分拣描述符拷贝到内核,轮询所有文件描述符
  • poll将文件描述符和事件类型以及检测事件的文件描述符封装到一个结构体里面
struct pollfd fds[1024];int maxfd = socketfd;fds[socketfd].fd = socketfd;fds[socketfd].events = POLLIN;while (true){int nready = poll(fds, maxfd + 1, -1);if (nready == 0)continue;if (fds[socketfd].revents & POLLIN){struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(socketfd, (struct sockaddr*)&client_addr, &client_addr_len);printf("accept a new client, client ip:%s, client port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));fds[client_fd].fd = client_fd;fds[client_fd].events = POLLIN;maxfd = (client_fd > maxfd) ? client_fd : maxfd;}for (int i = socketfd + 1; i <= maxfd; i++){if (fds[i].revents & POLLIN){char buf[1024] = {0};int ret = recv(fds[i].fd, buf, sizeof(buf), 0);if (ret <= 0){close(fds[i].fd);fds[i].fd = -1;fds[i].events = 0;continue;}else{printf("recv buf: %s\n", buf);send(fds[i].fd, buf, strlen(buf), 0);}}}}   
九、EPOLL
  • epool采用事件通知的方式,避免轮询,提高io的性能
  • 使用红黑树结构维护文件描述符,并引入水平触发(LT)和边缘触发(ET)两种模式
  • 不需要多次拷贝文件描述符集合
  • 水平触发一搬在传输小文件中使用,边缘触发一般在传输发文件中使用
  • 主要使用三个函数
    • epoll_create()
    • epoll_ctl()
    • epoll_wait()
    int epfd = epoll_create(1);struct epoll_event event, events[1024];event.events = EPOLLIN;event.data.fd = socketfd;epoll_ctl(epfd, EPOLL_CTL_ADD, socketfd, &event);while (1){int number = epoll_wait(epfd, events, 1024, -1);for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (sockfd == socketfd) // socketfd RPOLLIN事件{struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(socketfd, (struct sockaddr*)&client_addr, &client_addr_len);event.events = EPOLLIN; //水平触发//event.events = EPOLLIN | EPOLLET; //边缘触发event.data.fd = client_fd;epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event);printf("new client: %d\n", client_fd);}else if (events[i].events & EPOLLIN) // client_fd RPOLLIN事件   {char buf[1024] = {0};int len = read(sockfd, buf, sizeof(buf));if (len == 0){perror("read error");close(sockfd);epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);continue;}printf("client00000 say: %s\n", buf);send(sockfd, buf, strlen(buf), 0);}}}
reactor模式
  • listenfd触发EPOLLIN事件,执行accept_cb
  • clientfd触发EPOLLIN事件,执行recv_cb
  • clientfd触发EPOLLOUT事件,执行send_cb

#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/poll.h>
#include <pthread.h>
#include <sys/epoll.h>typedef int (*RCALLBACK)(int fd);int accept_cb(int fd);
int recv_cb(int fd);
int send_cb(int fd);struct connet_item
{int fd;char rbuffer[1024];char wbuffer[1024];int rlen;int wlen;union{RCALLBACK recv_callback;RCALLBACK accept_callback;} recv_t;RCALLBACK sned_callback;;
};int epfd = 0;
connet_item conncet_list[1024] = {0};void set_event(int fd, int events, int flag)
{struct epoll_event event;event.events = events;event.data.fd = fd;if (flag)epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);elseepoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);
}int accept_cb(int fd)
{struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(fd, (struct sockaddr*)&client_addr, &client_addr_len);if (client_fd < 0){perror("accept error:--");return -1;}//event.events = EPOLLIN; //水平触发//event.events = EPOLLIN | EPOLLET; //边缘触发set_event(client_fd, EPOLLIN, 1);conncet_list[client_fd].fd = client_fd;memset(conncet_list[client_fd].rbuffer, 0, 1024);conncet_list[client_fd].rlen = 0;memset(conncet_list[client_fd].wbuffer, 0, 1024);conncet_list[client_fd].wlen = 0;conncet_list[client_fd].recv_t.recv_callback = recv_cb;conncet_list[client_fd].sned_callback = send_cb;printf("new client: %d\n", client_fd);return client_fd;
}
int recv_cb(int fd)
{char *buf = conncet_list[fd].rbuffer;int idx = conncet_list[fd].rlen;int len = recv(fd, buf + idx, 1024 - idx, 0);if (len == 0){perror("read error");close(fd);epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);return -1;}conncet_list[fd].rlen += len;printf("client00000 say: %s\n", buf);memcpy(conncet_list[fd].wbuffer, buf, conncet_list[fd].rlen);conncet_list[fd].wlen = conncet_list[fd].rlen;conncet_list[fd].rlen = 0;set_event(fd, EPOLLOUT, 0);return len;
}
int send_cb(int fd)
{char *buf = conncet_list[fd].wbuffer;int idx = conncet_list[fd].wlen;int len = send(fd, buf, idx, 0);if (len == 0){perror("write error");close(fd);epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);return -1;}   printf("send data:%s\n", buf);set_event(fd, EPOLLIN, 0);conncet_list[fd].wlen += len;return len;
}int main()
{int socketfd = socket(AF_INET, SOCK_STREAM, 0); // 创建一个TCP socketstruct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 设置服务器的IP地址//servaddr.sin_addr.s_addr = ntohl(INADDR_ANY); servaddr.sin_port = htons(1234); // 设置服务器的端口号if (-1 == bind(socketfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) // 将socket绑定到指定的地址和端口{perror("bind");return -1;}conncet_list[socketfd].fd = socketfd;;conncet_list[socketfd].recv_t.accept_callback = accept_cb;listen(socketfd, 10); // 开始监听,允许最多10个连接请求在等待队列中排队epfd = epoll_create(1);set_event(socketfd, EPOLLIN, 1); // 设置socketfd的EPOLLIN事件epoll_event events[1024];while (1){int number = epoll_wait(epfd, events, 1024, -1);for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (events[i].events & EPOLLIN) // client_fd RPOLLIN事件   {conncet_list[sockfd].recv_t.accept_callback(sockfd); }else if (events[i].events & EPOLLOUT){// 客户端发送数据conncet_list[sockfd].sned_callback(sockfd);}}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/666926.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java仓库进销存商品库存管理系统springboot+vue

库存管理信息系统研究的内容涉及库存管理的全过程&#xff0c;包括入库、出库、退 货、订货、库存统计查询等等。 根据上述工作流程&#xff0c;库存管理系统将包含以下内容 1&#xff09;登录信息的输入&#xff0c;密码的修改。 2&#xff09;基本信息的输入&#xff0c;包括…

部门管理的主要内容:从目标设定到团队发展的全面指南

部门管理是指对一个组织机构中的各个部门进行协调、规划和管理的过程。部门管理的主要内容包括以下几个方面&#xff1a;部门目标和职责的规划、部门人员的管理、部门绩效的评估和监控、部门之间的协调和沟通、部门文化和价值观的塑造。下面为大家具体介绍这些内容 1、部门目标…

自学网安-IIS服务器

部署环境&#xff1a;win2003 配置环境&#xff1a;winxp ip&#xff1a;10.1.1.2 win2003 ip&#xff1a;10.1.1.1 开始安装 双击“应用程序服务器” 双击“Internet 信息服务&#xff08;IIS&#xff09;” 勾选万维网服务&#xff0c;确定然后下一步 查看端口号;netstat …

ConcurrentHashMap的使用以及源码分析

一、ConcurrentHashMap&#xff1f; 1.1 存储结构 ConcurrentHashMap是线程安全的HashMap ConcurrentHashMap在JDK1.8中是以CASsynchronized实现的线程安全 CAS&#xff1a;在没有hash冲突时&#xff08;Node要放在数组上时&#xff09; synchronized&#xff1a;在出现ha…

Java 日期时间相互转换 格式化输出

Java 日期时间相互转换 格式化输出 package com.zhong.time;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;public class SimpleDateFormatTest {public static void main(String[] args) throws ParseException {Date date n…

impala与kudu进行集成

文章目录 概要Kudu与Impala整合配置Impala内部表Impala外部表Impala sql操作kuduImpala jdbc操作表如果使用了Hadoop 使用了Kerberos认证&#xff0c;可使用如下方式进行连接。 概要 Impala是一个开源的高效率的SQL查询引擎&#xff0c;用于查询存储在Hadoop分布式文件系统&am…

设计模式1-访问者模式

访问者模式是一种行为设计模式&#xff0c;它允许你定义在对象结构中的元素上进行操作的新操作&#xff0c;而无需修改这些元素的类。这种模式的主要思想是将算法与元素的结构分离开&#xff0c;使得可以在不修改元素结构的情况下定义新的操作。 所谓算法与元素结构分离&#x…

极速上手:使用Jmeter轻松实现N种参数化

参数化的方式&#xff1a; 一、使用用户自定义变量 一种方式&#xff1a;直接在测试计划中添加用户自定义变量 另外一种方式&#xff1a;配置元件——用户自定义变量 示例&#xff1a;用户自定义变量&#xff0c;登录手机号码 在接口请求的时候&#xff0c;进行引用 请求之后&…

32ADC模数转换器&AD单通道&多通道

目录 一.简介 二.逐次逼近法​编辑 三.结构框图 四.小tips (1)转换模式 &#xff08;2&#xff09;触发控制 &#xff08;3&#xff09;数据对齐 &#xff08;4&#xff09;转换时间 &#xff08;5&#xff09;校准 &#xff08;6&#xff09;硬件电路 五.相关函数 …

vue使用es的reduce方法编译报错Error: Can‘t resolve ‘core-js/modules/es.array.reduce.js‘

哈喽 大家好啊 最近在vue使用es的reduce方法编译报错Error: Cant resolve core-js/modules/es.array.reduce.js 报错如图所示&#xff1a; 解决方案&#xff1a; npm install --save core-js 然后重新编译下将正常了 参考原文: 使用import异步加载语法报错_module not foun…

Android Split APK介绍

文章目录 Split APKSplit APK 详细介绍概念Android App Bundle&#xff08;AAB&#xff09;Split APK 的优势动态分发减小安装包大小模块化和渠道分发 Split APK 的类型基于屏幕密度### 基于 CPU 架构基于语言 实现 Split APK Split APK Split APK 是 Android 中一种应用程序安…

SpringBoot实战第二天

今日战报 继续完善用户相关接口开发&#xff1a; 1.完成获取用户信息功能 2.完成更新用户信息功能 3.完成更新用户头像功能 4.完成更新用户密码功能 获取用户信息 接口文档 如接口文档所示&#xff0c;我们需要做的就是从header中的Authorization中读取token&#xff0c;解码…

问题:测风站应设置在平直的巷道中,其前后()范围内不得有障碍物和拐弯等局部阻力。 #微信#媒体

问题&#xff1a;测风站应设置在平直的巷道中&#xff0c;其前后&#xff08;&#xff09;范围内不得有障碍物和拐弯等局部阻力。 参考答案如图所示

stable-diffusion | v1-5-pruned.ckpt和v1-5-pruned-emaonly.ckpt的区别

https://github.com/runwayml/stable-diffusion?tabreadme-ov-file#reference-sampling-script 对于 1.5 模型&#xff0c;其中可能包括四部分&#xff1a;标准模型、文本编码器、VAE模型、EMA模型。 标准模型&#xff1a;生成图片的核心模块&#xff0c;潜空间中的前向扩散和…

【lesson32】MySQL用户管理

文章目录 用户管理介绍用户用户信息创建用户 删除用户修改用户密码数据库的权限给用户授权回收权限 用户管理介绍 用户 用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中 //操作语法 mysql> use mysql; Database changed mysql> select host,use…

VR全景技术可以应用在哪些行业,VR全景技术有哪些优势

引言&#xff1a; VR全景技术&#xff08;Virtual Reality Panorama Technology&#xff09;是一种以虚拟现实技术为基础&#xff0c;通过360度全景影像、立体声音、交互元素等手段&#xff0c;创造出沉浸式的虚拟现实环境。该技术不仅在娱乐领域有着广泛应用&#xff0c;还可…

Git使用命令大全

命令大全参考阮一峰的博客&#xff0c;根据自己的使用习惯作了调整。 Git常用命令 其他常用的命令 配置Git # 显示当前的Git配置 $ git config --list# 编辑Git配置文件 $ git config -e [--global]# 设置提交代码时的用户信息 $ git config [--global] user.name "[nam…

第14章_视图

第14章_视图 1.常见的数据库对象 对象描述表(TABLE)表是存储数据的逻辑单元&#xff0c;以行和列的形式存在&#xff0c;列就是字段&#xff0c;行就是记录数据字典就是系统表&#xff0c;存放数据库相关信息的表。系统表的数据通常由数据库系统维护&#xff0c; 程序员通常不…

MDK Keil uVision5 cannot read project file 解决办法

MDK Keil uVision5 cannot read project file 解决办法 问题描述 我的系统重装过后是英文版的Windows 10&#xff0c;在打开别人/以前中文系统环境下保存的Keil Project文件&#xff08;uvprojx文件&#xff09;会报错&#xff0c;内容大致是 Cannot read project file D:\xx…

十、VTK创建圆锥体vtkConeSource 带颜色

为圆锥体的每一面,添加一种颜色: 上述效果的代码: #include <vtkSmartPointer.h> #include <vtkPoints.h> #include <vtkLine.h> #include <vtkPolyData.h> #include <vtkPolyDataWriter.h> #include <vtkPolyDataMapper.h> #incl…