解密异步IO:使用C++进行高效的网络编程

计算机网络计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。网络编程的目的:传播交流选项,数据交换,通信。

编写项目之前需要遵循的代码层面上的一些规范(例如,运行的步骤,流程等)

c/s架构

c:client客户端s:server服务端计算机或手机下载的各类app本质上就是客户端优势是:下载对应的客户端,可以在客户端体验高度定制服务劣势是:使用必须下载!!b/s架构

b:broswer浏览器s:server服务器通过浏览器来充当各个服务器的客户端,用于想要体验服务又不用下载客户端劣势是:网页的各种功能花里胡哨,观看效果不佳架构发展趋势

1.统一接口原则(可以通过一个接口去往其他程序)2.cs和bs交叉使用,避免各自的劣势二、网络编程基于网络编写代码,程序可以实现远程数据交互。

规定了计算机涉及到远程交互的时候必须要经过相同的流程数据发送出去的时候是从上往下走数据接收回来的时候是从下往上走应用层

主要是程序员自己编写代码的地方,有什么协议和规范取决于程序员自己常见协议有:HTTP,HTTPS等等表示层

会话层

传输层

1、port协议

1.三次握手(建连接)

基于UDP发送的数据没有任何通道也没有任何的限制,缺点是数据容易丢失(可以基于发送过程做一些优化操作)服务端代码importsocketres=socket.socket(type=socket.SOCK_DGRAM)res.bind(('127.0.0.1',8080))msg,address=res.recvfrom(1024)#接收客户端发送过来的消息print('msg>>>%s'%msg.decode('utf8'))#由于UDP没有双向通道所以每次发送消息都会带着它的地址print('address>>>>:',address)res.sendto('服务端'.encode('utf8'),address)客户端代码importsocketc=socket.socket(type=socket.SOCK_DGRAM)server_address=('127.0.0.1',8080)c.sendto('客户端'.encode('utf8'),server_address)msg,address=c.recvfrom(1024)print('msg>>>"%s'%msg.decode('utf8'))print('address>>>>>:',address)

ip协议:规定了任何接入互联网的计算机都必须有IP地址每个ip地址都自带定位IP地址:IPV4:点分十进制最小:0.0.0.0最大:255.255.255.255IPV6:无限大了能够给地球上的每一粒沙子都分一个ip地址IP

IP地址能定位到唯一的一台计算机127.0.0.1本机地址ip地址分类IPV4/IPV6IPV4127.0.0.14个字节组成,0-255总共42亿已用尽IPV6128位8个无符号整数(16进制)例如0000:1111:2222:3456:aaaa:bbbb:cccc:defg公网(互联网)-私网(局域网)ABCD类192.168.xxx.xxxInetAddress类常用方法

packagecom.taodou;importjava.net.InetAddress;importjava.net.UnknownHostException;publicclassTestInetAddress{publicstaticvoidmain(String[]args){try{//查询本机地址InetAddressinetAddress1=InetAddress.getByName("127.0.0.1");InetAddressinetAddress2=InetAddress.getByName("LocalHost");InetAddressinetAddress3=InetAddress.getLocalHost();System.out.println(inetAddress1);System.out.println(inetAddress2);System.out.println(inetAddress3);System.out.println("------------------------------------");//查询网址IP地址InetAddressinetAddress4=InetAddress.getByName("www.baidu.com");System.out.println(inetAddress4);System.out.println(inetAddress4.getCanonicalHostName());//规范名System.out.println(inetAddress4.getHostAddress());//IPSystem.out.println(inetAddress4.getHostName());//域名或主机名}catch(UnknownHostExceptione){e.printStackTrace();}}}端口

端口表示计算机上的一个程序的进程

不同的进程有不同的端口号端口号0~65536端口号有两种TCP,UDP65536*2单协议下端口号不能冲突端口分类公有端口(系统程序)0~1023HTTP:80HTTPS:443FTP:21程序注册端口1024~49151Tomcat:8080MySQL:3306动态,私有49152~65536packagecom.taodou;importjava.net.InetSocketAddress;publicclassTestInetSocketAddress{publicstaticvoidmain(String[]args){InetSocketAddressinetSocketAddress1=newInetSocketAddress("127.0.0.1",8080);InetSocketAddressinetSocketAddress2=newInetSocketAddress("LocalHost",8080);System.out.println(inetSocketAddress1);System.out.println(inetSocketAddress2);System.out.println(inetSocketAddress1.getAddress());System.out.println(inetSocketAddress1.getHostName());System.out.println(inetSocketAddress1.getPort());}}数据链路层

1.规定了电信号的分组方式2.规定了每台计算机都必须有一块网卡,网卡上必须有一串记录(电脑的以太网址也称mac地址,身份证号)3.mac地址:由12位16进制数组成前6位:厂商编号后6位:生产流水线号可以根据该地址查找到计算机,基于mac地址实现数据交互物理连接层

保证物理连接介质的条件,传递电信号(主要研究插网线情况)

1.交换机能够让接入交换机的多台计算机实现彼此互联2.以太网通信(mac通信)有了交换机以后,根据电脑的mac地址就可以实现数据交互广播:就是在交换机中发出请求,所有接入交换机的设备都能收到单播:只有被查找设备才会回复相应信息缺陷:1.mac地址通信仅限于局域网2.接入交换机的设备过多的话可能会造成广播风暴3.局域网有某个固定的区域组成的网络4.路由器将多个局域网连接到一起的设备三、socket套接字主要作用于软件层和应用层中间,作为一个接口。如果没有socket,那么我们就需要手动操作各个层之间的代码了!!

1.一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。2.基于文件类型的套接字家族:套接字家族的名字:AF_UNIX3.基于网络类型的套接字家族:套接字家族的名字:AF_INET,所有地址家族中,AF_INET是使用最广泛的一个socket实例

先运行服务端再运行客户端1.服务端importsocket#1.先创建一个socket对象server=socket.socket()#绑定一个固定的地址IP和portserver.bind(('192.168.1.169',8080))#半连接池server.listen(5)#开业等待接口sock,address=server.accept()print(sock,address)#sock是双向通道address是客户端地址65459#数据交互sock.send(b'hello')#朝客户端发送数据res=sock.recv(1024)#接收客户端发送的数据print(res)#断开连接sock.close()#断连接server.close()#关机------------2.客户端importsocket#1.产生一个socket对象client=socket.socket()#2.连接服务端(拼接服务端的ip和port)client.connect(('192.168.1.169',8080))#3.数据交互res=client.recv(1024)#接收服务端发送的数据print(res)client.send(b'hei')#朝服务端发送数据#关闭client.close()#直接把双向通道关闭代码优化

#服务端importsockets=socket.socket()s.bind(('192.168.1.169',8080))s.listen(5)sock,address=s.accept()sock.send(b'hello')sock.send(b'hai')sock.send(b'hahaha')------------#客户端importsocketres=socket.socket()res.connect(('192.168.1.169',8080))c=res.recv(1024)print(c.decode('utf8'))c=res.recv(1024)print(c.decode('utf8'))c=res.recv(1024)print(c.decode('utf8'))打印结果:hellohaihahaha#出现了粘包现象3.4struct模块简介

struct模块无论数据长度是多少都可以帮你打包成固定长度然后基于该固定长度还可以反向解析出真实长度ps:对于数据量特别大的模块,会直接报错粘包问题的解决方案

#服务端1.先将真实数据的长度制作成固定长度42.先发送固定长度的报头3.再发送真实数据#客户端1.先接收固定长度的报头42.再根据报头解压出真实长度3.根据真实长度接收即可代码实操

异步IO相比同步IO不会阻塞当前程序的执行,可以继续向下执行。即当应用程序发起一个IO操作后,调用者不会立刻得到结果,而是在内核完成IO操作后,通过信号或回调来通知调用者。

信号驱动IO是异步IO的一种实现,在异步IO中,当文件描述符上可以执行I/O操作时,进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其他任务直到文件描述符可以执行I/O操作为止,此时内核会发送信号给进程。

使用信号驱动,程序需要按照如下步骤执行:

以上步骤完成后,进程可以去执行其他的任务,当I/O就绪时,内核会向进程发送一个SIGIO信号,当进程接收到信号时,会执行预先注册号的信号处理函数,这样就可以在信号处理函数中进行I/O操作了

使能O_ASYNC

调用open时无法通过指定O_ASYNC标志来使能异步I/O,但是可以通过fcntl()函数添加O_ASYNC标志来使能I/O:

fcntl(fd,F_SETOWN,getpid());//也可以传入其他进程的pid注册SIGIO信号的处理函数

通过signal()或sigaction()函数为SIGIO信号注册一个信号处理函数,当进程接收到内核发送过来的SIGIO信号时,会执行该函数。

代码实例:

#define_GNU_SOURCE//F_SETSIG#include#include#include#include#include#include#include#defineMOUSE"/dev/input/mouse0"staticintfd;staticvoidsigio_handler(intsig){staticintloops=5;charbuf[100]={0};intret;if(SIGIO!=sig){return;}ret=read(fd,buf,sizeof(buf));if(0=loops){close(fd);exit(0);}}intmain(void){intflag;//打开设备,使能非阻塞IOfd=open(MOUSE,O_RDONLY|O_NONBLOCK);if(-1==fd){perror("openmouseerror");exit(-1);}//使能异步IOflag=fcntl(fd,F_GETFL);flag|=O_ASYNC;fcntl(fd,F_SETFL,flag);//设置异步IO的所有者fcntl(fd,F_SETOWN,getpid());//注册信号回调函数signal(SIGIO,sigio_handler);for(;;){sleep(1);}}运行结果:

但是使用默认信号SIGIO会存在一些问题,SIGIO是标准信号,不可靠信号,非实时信号,不支持信号排队机制,不知道文描述符发生了什么事件,未判断文件描述符是否处于可读的就绪态,所以需要进一步优化(实时信号替换)。

1.使用实时信号替换默认信号SIGIO

比如使用SIGRTMIN信号替换SIGIO,比如:

fcntl(fd,F_SETSIG,SIGRTMIN);2.使用sigaction()函数注册信号处理函数

在应用程序中需要为实时信号注册信号处理函数,使用sigaction函数进行注册,sigaction原型:

#includeintsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);使用实例:

#define_GNU_SOURCE//F_SETSIG#include#include#include#include#include#include#include#defineMOUSE"/dev/input/mouse0"staticintfd;staticvoidio_handler(intsig,siginfo_t*info,void*context){staticintloops=5;charbuf[100]={0};intret;if(SIGRTMIN!=sig){return;}//判断鼠标是否可读if(POLL_IN==info->si_code){ret=read(fd,buf,sizeof(buf));if(0=loops){close(fd);exit(0);}}}intmain(void){structsigactionact;intflag;//打开设备,使能非阻塞IOfd=open(MOUSE,O_RDONLY|O_NONBLOCK);if(-1==fd){perror("openmouseerror");exit(-1);}//使能异步IOflag=fcntl(fd,F_GETFL);flag|=O_ASYNC;fcntl(fd,F_SETFL,flag);//设置异步IO的所有者fcntl(fd,F_SETOWN,getpid());//指定实时信号SIGRTMIN作为异步I/O通知信号fcntl(fd,F_SETSIG,SIGRTMIN);//为实时信号SIGRTMIN注册信号处理函数act.sa_sigaction=io_handler;act.sa_flags=SA_SIGINFO;sigemptyset(&act.sa_mask);sigaction(SIGRTMIN,&act,NULL);for(;;){sleep(1);}}运行结果:

LinuxNativeAIO是Linux支持的原生AIO,很多第三方的异步IO库,比如libeio和glibcAIO。很多三方库异步IO库不是真正的异步IO,而是通过多线程来模拟异步IO,比如libeio。

aio_*系列的调用是有glibc提供的,是glibc用线程+阻塞调用来模拟的,性能较差,为了能更多的控制io行为,可以使用更低级的libaio。

Ubuntu安装livaio:

sudoaptinstalllibaio-devLinuxAIO执行流程:

Linux原生AIO处理流程:

从上面流程可以看出,Linux异步IO操作主要由两个步骤组成:

实例代码:

ccaio_demo.c-laio运行结果:

目录下会出现一个aio.txt的文件,内容为1024个@字符

程序说明:

以上示例使用while检测,还可以使用epoll结合eventfd,结合事件驱动的方式来获取异步IO操作的结果。

一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作。

同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果实际的IO读写阻塞请求进程,那么就是同步IO。

阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

所以,IO操作可以分为3类:同步阻塞(即早期的IO操作)、同步非阻塞(NIO)、异步(AIO)。

同步阻塞:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式。

同步非阻塞:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。

异步:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序。

THE END
1.c语言中如何实现网络通信c网络通信//网络通信的地址结构(internet) struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ https://blog.csdn.net/yinbucheng/article/details/51735065
2.C++网络编程初学者的开源项目码云周刊第41期互联网的兴起,让网络程序有了长足的发展,让我们可以通过网络编程在程序中实现计算机的通信。举个例子,当你使用浏览器访问码云时,你的计算机就和码云的某台服务器通过互联网连接起来了,然后,码云的服务器把网页内容作为数据通过互联网传输到你的电脑上。 当然,对于 C++ 网络编程的初学者,小编推荐下面6个还算不错的开https://cloud.tencent.com/developer/article/1078264
3.C++网络编程Socket基础:网络通讯程序入门级教程编程语言:C++ 运行环境:Ubuntu 项目技术:socket 目录 一、基本概念 二、第一个网络通讯程序 2.1 网络通讯的流程示意图 2.2 程序模块 三、运行测试 3.1 准备工作 3.2 编译 3.3 运行 四、程序分析 4.1 客户端程序 https://blog.51cto.com/sarpro/10317992
4.C++Actor库orca简介(四):网络消息通信wlgq2C++ Actor库orca简介(四):网络消息通信 项目地址:https://github.com/wlgq2/orca orca网络通信机制 orca的网络部分基于libuv,并对libuv做了一层C++封装:https://github.com/wlgq2/libu orca的项目中编译了libuv1.22.0的vs2017及gcc5.50版本,如需使用其他版本,则需自己编译相应版本。https://segmentfault.com/a/1190000016976794/
5.C/C++知识点之c语言中如何实现网络通信(流程实例)本文主要向大家介绍了C/C++知识点之c语言中如何实现网络通信(流程实例),通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。 主要函数: --- TCP实现服务器与客户端的通信流程 //服务器端---服务器是一个被动的角色 1.socket//买一个手机 2.bind//SIM卡绑定一个手机号(ip+port) 3.https://www.douban.com/group/topic/130291496/
6.CORBA/TAO使用手记1C/S架构网络通信开发CORBA/TAO使用手记1 -- C/S架构网络通信开发 在C/S结构的C++网络程序中,直接采用Socket API进行开发效率是很低的,所以大家发明了各种各样的网络框架,如Boost.Aiso和ACE,简化了网络通信开发的难度。 但是这种基于数据包收发的模式还是不太方便,于是又出现了RPC、DCOM、CORBA等远程接口调用的标准。客户端只需要像http://www.cppblog.com/cppx/archive/2011/02/16/140184.html
7.c++网络编程(一)TCP/UDPwindows/linux下入门级socket通信客户(remote_addr)); //数据初始化--清零 remote_addr.sin_family=AF_INET; //设置为IP通信 remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址 remote_addr.sin_port=htons(8000); //服务器端口号 /*创建客户端套接字--IPv4协议,面向无连接通信,UDP协议*/ if((client_sockfd=sockethttps://www.imooc.com/article/75561
8.C++,网络库推荐复杂分布式系统、中间件、高性能网络服务器及客户端应用。 实现高并发、低延迟的网络通信。 官网:https://www.dre.vanderbilt.edu/~schmidt/ACE.html Asio 特点: C++模板库,专注于高效、类型安全的异步I/O编程。 支持TCP、UDP、SSL/TLS、串行通信等多种网络协议。 https://www.jianshu.com/p/fc6dc047ff2b
9.VisualC++网络编程案例实战中文pdf版[11MB]电子书下载《Visual C++网络编程案例实战》共15章,分为3篇。第1篇介绍了Visual C++网络开发基础知识,包括Visual C++网络编程概述、Socket套接字编程和多线程与异步套接字编程。第2篇介绍了7大类网络开发典型应用案例的实现,包括FTP客户端实现之一、 FTP客户端实现之二、网页浏览器、网络通信器、邮件接收和发送客户端之一、邮件https://www.jb51.net/books/464022.html
10.965外企内推(统招本科三年以上,不卡年龄,月薪大部分在15~35k左右C++ 【任职要求】 1.精通C++语言,熟悉Linux操作系统及Linux下的C++程序开发 2.熟悉TCP、UDP、HTTP等常用通信协议,熟悉异步网络编程,熟悉多线程编程。 3.思维逻辑严密,代码风格良好,有技术钻研的热情 4.良好的英文读写能力 5.良好的沟通能力和团队协作精神 https://eleduck.com/posts/yGf0pX
11.Go语言木马加密通信分析与检测据网络安全公司 Intezer 报告显示,恶意软件的开发者已经从 C 和 C++ 逐渐转向 Go 语言,自 2017 年以来,基于 Go 语言的恶意软件数量呈现爆发式增长,增幅超过了 2000%。预计Go的使用率在未来几年将持续上升,并与C、C++和Python一起,成为恶意软件编码的首选编程语言之一。 https://gat.zj.gov.cn/art/2021/10/26/art_1229442537_59086782.html
12.CxxConet:基于协程的C++网络开发框架基于协程的C++网络开发框架. 目录 特点 协程模型 通信流程 示例 性能 依赖 Support CxxConet 基于协程的C++网络开发框架. 目录 特点 协程模型 通信流程 示例 性能 依赖 Support 特点 跨平台:同时支持Linux32/64、OSX64两个平台,支持C++11及以上; 易开发:同步的方式编写代码,支持类Mina的filter过滤链、简单而不失强http://git.oschina.net/cxxjava/CxxConet