FlayerVEO 学无止境 - ········C&C++ http://www.flayerveo.com/index.php/category/c C++-网络编程-5.socket阻塞与非阻塞 http://www.flayerveo.com/index.php/archives/335/ 2020-02-12T08:14:00+00:00 **阻塞(blocking) 、非阻塞 (non-blocking):**Windows套接字在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。 ##阻塞模式: **阻塞模式的套接字上,调用任何一个Windows Socket API 都会耗费等待时间:** 当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好那么系统就处于等待状态。当数据准备好后,将数据从系统内核缓冲区复制到用户控件,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。 ![](http://www.flayerveo.com/usr/uploads/2020/02/2257592501.png) ####四种阻塞API: ![](http://www.flayerveo.com/usr/uploads/2020/02/3499074338.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/942829760.png) **注意: **不是所有Windows Sockets API以阻塞套接字为参数调用都会发生阻塞。例如,以阻塞模式的套接字为参数调用bind()、listen()函数时,函数会立即返回。 ##非阻塞模式: ![](http://www.flayerveo.com/usr/uploads/2020/02/2591045364.png) 非阻塞模式需要一直不停的调用recv()函数知道有数据过来。 **把套接字设置为非阻塞模式,即通知系统内核: **在调用Sockets API时,应让函数立即返回,并返回一个错误代码。 一个非阻塞模式套接字多次调用recv()函数时,内核数据还没有准备好。因此,该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时,数据已经准备好,被复制到应用程序的缓冲区中,recv()函数返回成功指示,应用程序开始处理数据。 ![](http://www.flayerveo.com/usr/uploads/2020/02/3522763754.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/3674783033.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/1626178759.png) C++-网络编程-4.socket编程之TCP通讯 http://www.flayerveo.com/index.php/archives/327/ 2020-02-12T08:11:00+00:00 ![](http://www.flayerveo.com/usr/uploads/2020/02/1540225395.png) ##服务端 Server: ```cpp 初始化winsock 1.创建TCP套接字 2.绑定socket到一个IP和端口上 3.监听套接字 4.接受客户端请求 5.发送、接收消息 6.关闭套接字 销毁winsock ``` ##客户端 Client: ```cpp 初始化winsock 1.创建TCP套接字 2.连接服务端 3.接收、发送消息 4.关闭套接字 销毁winsock ``` C++网络编程-3.WSA系列API函数 http://www.flayerveo.com/index.php/archives/325/ 2020-02-12T08:09:29+00:00 **什么是WSA (Windows Sockets Asynchronous)? ** 不带WSA前缀标准的socket函数,如何socket(), send(), recv()这些不带WSA前缀的函数,这些函数是全平台通用的;而加上了WSA前缀的是微软的扩展函数,一般是Winsock2定义的2.0版的函数,只能在Windows平台用。 ##WSAStartup() 函数: 用于初始化进程调用的Winsock相关的dll。 格式: ```cpp int WSAStartup( IN WORD wVersionRequested, OUT LPWSADATA lpWSAData) ``` 参数一 (wVersionRequested):Windows Sockets DLL规定调用者可以使用的Windows Sockets规范的版本,为WORD类型。高位字节中存储副版本号,低位字节中存储高版本号。可以使用MAKEWORD宏返回该值,例如MAKEWORD(2, 2)。 参数二 (lpWSAData):指向WSADATA结构体的指针,用于接收WindowsSockets执行的数据。 ##WSACleanup() 函数: 释放对Winsock链接库的调用,在多线程环境下,将终止所有 格式: ```cpp int WSACleanup(void); ``` 返回值:0表示正常退出,SOCKET_ERROR表示异常。 ##WSASocket() 函数: 创建一个与指定传送服务提供者捆绑的套接字,可选地创建和/或加入一个套接字组。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/1203427664.png) 第四个和第五个参数可以设为none,空。 返回值:成功返回套接字的描述符,失败返回INVALID_SOCKET。 ##socket() 和 WSASocket() 的区别: 1. socket()函数创建一个通讯店并返回一个套接字。如果是阻塞套接字时相应API会阻塞。 2. WSASocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据; 发送函数也可以被多次调用,组成一个发送缓冲区队列。可是socket()却只能发过之后等待回消息才可以做下一步操作。 ##WSAConnect() 函数: 在Winsock2版本中的连接函数,作用是创建一个与远端的连接,交换连接数据,并根据所提供的流描述确定所需的服务质量。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/3042438043.png) 平时只需要设置前三个参数即可。 返回值:成功返回0,失败返回SOCKET_ERROR,通过WSAGetLastError()获取。 **例:** ```cpp WSAConnect(sock,(sockaddr *)&addr,sizeof(addr),NULL,NULL,NULL,NULL) ``` ##WSAAccept() 函数: 有条件的接受一个连接基于状态函数的返回值,选择创建或加入一个套接字组,提供QOS flowspecs,运行连接数据的转移。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/1466649325.png) 平时可以只设置前三个参数,将后面的参数设为none。 返回值:成功返回接受套接字的描述字,失败返回INVALID_SOCKET。 ##WSARecv() 函数: 在recv的基础上增加了一些新的特性。 比如说重叠I/O和部分数据报通知。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/296614210.png) 返回值:成功返回0,完成例程将在调用线程处于alertable状态时被调用;失败返回SOCKET_ERROR的值,并通过调用WSAGetLastError错误代码。 ##WSARecvEx() 函数: 与WSARecv关系不大,与recv()函数作用相同,本质调用recv()从TCP连接的另一端接收数据。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/1113947377.png) 返回值:成功时返回接收到的字节数,错误返回SOCKET_ERROR,连接关闭返回0。如果接受到部分消息,则在flags参数中设置MSG_PARTIAL;如果接收到完整消息,则不设置。 ##WSASend() 函数: 增强标准的send()函数,可用进行重叠的发送。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/3668243333.png) 返回值:成功返回0,完成例程将在调用线程处于alertable状态时被调用;失败返回SOCKET_ERROR的值,并通过调用WSAGetLastError错误代码。 ##WSARecvFrom() 函数: 在recvfrom的基础上增加了一些新特性。比如说重叠I/O和部分数据报通知。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/2829391399.png) 返回值:成功返回0,完成例程将在调用线程处于alertable状态时被调用;失败返回SOCKET_ERROR的值,并通过调用WSAGetLastError错误代码。 ##WSASendTo() 函数:增强标准的sendto函数,可用进行重叠的发送。 格式: ![](http://www.flayerveo.com/usr/uploads/2020/02/1612842552.png) 返回值:成功返回0,完成例程将在调用线程处于alertable状态时被调用;失败返回SOCKET_ERROR的值,并通过调用WSAGetLastError错误代码。 ##WSAGetLastError() 函数: 返回上次发生的网络错误的错误代码。 格式: ```cpp int WSAGetLastError(void); ``` 返回值:指出了该线程进行的上一次Windows Sockets API函数调用时的错误代码。 常用的错误代码: ![](http://www.flayerveo.com/usr/uploads/2020/02/24266528.png) C++-网络编程-2.socket常用API函数 http://www.flayerveo.com/index.php/archives/314/ 2020-02-12T08:03:00+00:00 ##socket() 函数: 创建一个套接字 格式: ```cpp SOCKET 套接字描述字 = socket(int af, int type, int protocol) ``` 参数一 (af): 指定应用程序使用的协议族,对于TCP/IP协议族,该参数设置为AF_INET; 参数二 (type):指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套 接字类型为SOCK_STREAM;原始套接字SOCK_RAW; 参数三 (protocol):指定套接字使用的协议,IPPROTO_TCP,IPPROTO_DUP, IPPROTO_RAW,IPPROTO_IP,也可以设为0即不指定具体协议。 返回值: 如果调用成功则返回新创建的套接字描述符(一个大于0的整数),失败返回INVALID_SOCKET。 **使用示例:** ```cpp SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET){ return 0; } ``` ##bind() 函数: 服务端对socket进行地址和端口的绑定。 格式: ```cpp int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); ``` 参数一 (sockfd): 即socket描述字,是socket()函数创建的唯一标识。 参数二 (*addr): 一个指针,指向要绑给sockfd的协议地址。设置时要用(SOCKADDR *) 强转,值可以设为INADDR_ANY表示"0.0.0.0"即不确定地址即任意地址。 参数三 (addrlen): 对应的结构体的长度。 返回值: 成功返回0,错误返回SOCKET_ERROR。 **使用示例: ** ```cpp struct sockaddr_in addr; //初始化结构体, 建议用sockaddr_in addr.sin_family = AF_INET; //设置协议族 addr.sin_addr.S_addr = inet_addr("127.0.0.1"); //设置地址(网络字节序存储) addr.sin_port = htons(8000); //设置端口(网络字节序存储) if (bind(s, (SOCKADDR*)&addr, sizeof(SOCKADDR)) == SOCKET_ERROR){ return 0; } ``` ![](http://www.flayerveo.com/usr/uploads/2020/02/3009618213.png) 结构体sockaddr (给操作系统用的): ![](http://www.flayerveo.com/usr/uploads/2020/02/1688513033.png) 结构体sockaddr_in (给开发者用的): ![](http://www.flayerveo.com/usr/uploads/2020/02/1254170471.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/2303126716.png) **网络字节序的定义:**当一个字节被当做高位,就要求发送端发送的第一个字节应当是高位。 (大端:高位字节放在内存的低地址端,低位字节放在内存的高位端) ##listen() 函数: 服务端对socket进行地址和端口的绑定。 格式: ```cpp int bind(int sockfd, int backlog); ``` 参数一 (sockfd):为要监听的socket描述字 参数二 (backlog):未完成队列的大小(正在等待完成相应的TCP三次握手过程的队列。这些套接口处于SYN_RCVD) 返回值:如果调用成功返回0,否则返回SOCKET_ERROR。可以调用WSAGetLastError()函数获取错误代码。 **使用实例:** ```cpp if (listen(s, 30) == SOCKET_ERROR){ return 0; } ``` ##accpet() 函数: 服务端接受客户端连接请求 格式: ```cpp int accpet(int sockfd, strut sockaddr *addr, socklen_t *addrlen); ``` 参数一 (sockfd): 要监听的socket描述字。 参数二 (*addr): 为指向struct sockaddr *的指针,用于返回客户端的协议地址。 参数三 (*addrlen): 协议地址的长度。 返回值:成功返回由内核自动生成一个全新的socket,代表与返回客户端的TCP连接。失败则返回INVALID_SOCKET。 **使用示例:** ```cpp sockaddr_in addrClient; //创建与客户端连接的套接字 memset(&addrClient, 0, sizeof(SOCKADDR)); //清空即初始化新建的套接字 int len = sizeof(SOCKADDR); SOCKET c = accept(s, (SOCKADDR*)&addrClient, &len); ``` ##connect() 函数: 客户端连接服务端 格式: ```cpp int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); ``` 参数一 (sockfd): 为客户端的socket描述字。 参数二 (*servaddr): 为服务器的socket地址。 参数三 (addrlen): 为socket地址的长度。 返回值: 成功返回0,错误返回SOCKET_ERROR。 **使用示例:** ```cpp struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.S_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(8000); if (connect(s, (SOCKADDR*)&addr, sizeof(SOCKADDR)) == SOCKET_ERROR){ return 0; } ``` ##send() 函数: 用send函数从TCP连接的另一端发送数据 格式: ```cpp int send(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags) ``` 参数一 (s): 为客户端(不完全指客户端,而是非监听的套接字)的socket描述字 参数二 (*buf):要发送的数据缓存 参数三 (len): 实际要发送的数据长度 参数四 (flags): 一般设为0 返回值: 成功返回发送数据缓冲区的字节数(可能小于len),错误返回SOCKET_ERROR。 **使用示例:** ```cpp int ret = send(s, "hello", strlen("hello"), 0); ``` ##recv() 函数: 用recv函数从TCP连接的另一端接收数据 格式: ```cpp int recv(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags) ``` 参数一 (s): 为客户端(不完全指客户端,而是非监听的套接字)的socket描述字 参数二 (*buf): 指明一个缓冲区用来存放接收到的数据 参数三 (len): 缓冲区的长度 参数四 (flags): 一般设为0 返回值: 成功返回接收到的字节数,错误返回SOCKET_ERROR,连接关闭返回0。 **使用示例:** ```cpp char buf[100]; //创建接收数据缓冲区 memset(buf, 0, 100); //初始化缓冲区 可以用char buf[100]={'\0'}代替memset()。 int ret = recv(s, buf, 100, 0); //接收数据 printf(buf); //打印接收到的数据 ``` ##sendto() 函数: 用sendto函数从UDP连接的另一端发送数据 格式: ```cpp int sendto(int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen); ``` 参数一 (s): 为客户端(不完全指客户端,而是非监听的套接字)的socket描述字 参数二 (*buf):要发送的数据缓存 参数三 (len): 实际要发送的数据长度 参数四 (flags): 一般设为0 参数五 (*to): 要发向的地址 参数六 (tolen): 数据发的长度 返回值: 成功返回发送数据缓冲区的字节数(可能小于len),错误返回SOCKET_ERROR。 **使用示例: (不需要使用connect函数,sendto与connect合二为一)** ![](http://www.flayerveo.com/usr/uploads/2020/02/721052222.png) ##recvfrom() 函数: 用recvfrom函数从UDP连接的另一端接收数据 格式: ```cpp int recvfrom(SOCKET s, char FAR* buf, int len, int flags, struct socketaddr FAR* from , int FAR* fromlen); ``` 参数一 (s): 为客户端(不完全指客户端,而是非监听的套接字)的socket描述字 参数二 (*buf): 指明一个缓冲区用来存放接收到的数据 参数三 (len): 缓冲区的长度 参数四 (flags): 一般设为0 参数五(from): 接收方的地址 参数六(fromlen):数据的长度 返回值: 成功返回接收到的字节数,错误返回SOCKET_ERROR,连接关闭返回0。 **使用示例:** ![](http://www.flayerveo.com/usr/uploads/2020/02/1549242758.png) ##shutdown() 函数: 禁止在指定套接字上发送和接收套接字数据。并不会关闭套接字,在调用closesocket()函数前,所有与该套接字相关的资源都不会被释放。 格式: ```cpp int shutdown(SOCKET s, int how); ``` 参数一 (s): 为客户端的socket描述字 参数二 (how): how的方式有三种: > SD_RECEIVE:表明关闭接收通道,在socket上不能再接收数据,如果当前接收 缓存中仍有未取出数据或将来有数据要到达,则TCP会向发送端发送RST包, 将连接重置。 SD_SEND:表明关闭发送通道,TCP会将发送缓存中的数据都发送完毕在收到所 有数据的ACK后向对端发送FIN包,表明本端没有更多数据发送。 SD_BOTH:表明同时关闭接收通道和发送通道。 返回值: 成功返回0,错误返回SOCKET_ERROR。 ##closesocket() 函数: 关闭一个套接字。 格式: ```cpp int closesocket(SOCKET s); ``` 参数一 (s): 要关闭的socket描述字 返回值:成功时返回0,错误返回SOCKET_ERROR ##setsockopt() 函数: 设置套接口的选项。 格式: ```cpp int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen) ``` 参数一 (s): 要设置的socket描述字 参数二 (level): 定义层次,目前仅支持SOL_SOCKET和IPPROTO_TCP层次 参数三 (optname):需要设置的选项 参数四 (*optval):存放选项值的缓冲区 参数五 (optlen):缓冲区长度 返回值:成功时返回0,错误返回SOCKET_ERROR **使用示例:** 方法一: 设置send()和recv()时的发送和接收超时: ![](http://www.flayerveo.com/usr/uploads/2020/02/3699611612.png) 方法二: 设置调用closesocket后套接字不会立刻关闭,而会经历TIME_WAIT的过程: ![](http://www.flayerveo.com/usr/uploads/2020/02/3440656857.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/1660059738.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/726813506.png) C++网络编程-1.socket简介与初始化 http://www.flayerveo.com/index.php/archives/303/ 2020-02-12T07:50:04+00:00 **Socket:**将TCP/IP集成到Unix中,出现了许多TCP/IP应用程序接口, 这些接口称为Socket接口。如今Socket接口已成为TCP/IP网络最通用、成熟的API,被称为"套接字"。 **WinSock:**微软制定的一套Windows下的网络编程接口,即WinSock规范。在实际应用中Windows Sockets规范主要有1.1和2.2版。两者的最重要区别是1.1版只支持TCP/IP协议,而2.2版可以支持多协议并且有良好的向后兼容性。 **Socket在TCP/IP协议模型中的位置:** 介于应用层与传输层之间,作为应用程序的接口进行TCP或UDP的网络编程。 (见下图) ![](http://www.flayerveo.com/usr/uploads/2020/02/2465894894.png) ####套接字分为以下三种类型: **流式套接字(SOCK_STREAM):** TCP协议使用该接口。 面向连接、无差错、无记录边界、有顺序和非重复的信包传输。 **数据报套接字(SOCK_DGRAM):**UDP协议使用该接口。 无连接的服务,以独立的信包进行网络传输,不保证顺序性、可靠性和无重复性。用于单报文传输或可靠性不重要的场合。数据报套接字的特点是保留了记录边界。 **原始套接字(SOCK_RAM):**提供对网络下层通讯协议(如IP协议)的直接访问,一般不是提供给普通用户的,主要用于开发新的协议或用于提取协议教隐蔽的功能。 **WinSock包含两个主要当的版本: ** (即WinSock1和WinSock2) **在使用WinSock1.1时,需要引用头文件库文件代码如下:** ```cpp #include #pragma comment(lib, "wsock32.lib") ``` **在使用WinSock2.2时,需要引用头文件库文件代码如下:** ```cpp #include #pragma comment(lib, "ws2_32.lib") ``` **使用WSAStartup函数初始化WinSock相关的动态库:** ```cpp WSADATA wsaData; //"wsaData"可以取任意名字 if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0 ){ printf("WSAStartup 无法初始化!"); return 0; } ``` **使用WSACleanup函数销毁Winsock:** ```cpp if( WSACleanup() == SOCKET_ERROR ){ printf( "WSACleanup 出错!" ); return 0; } ``` C++-网络编程-域名和网络结构体hostent http://www.flayerveo.com/index.php/archives/301/ 2020-02-12T07:46:54+00:00 #域名和网络地址结构体---struct hostent **该结构体定义如下:** ```cpp struct hostent { char *h_name; //主机名,即官方域名 char **h_aliases; //主机所有别名构成的字符串数组,同一IP可绑定多个域名 int h_addrtype; //主机IP地址的类型,例如IPV4(AF_INET)还是IPV6 int h_length; //主机IP地址长度,IPV4地址为4,IPV6地址则为16 char **h_addr_list; /* 主机的ip地址,以网络字节序存储。若要打印出这个IP,需要调用inet_ntoa()。*/ }; ``` **gethostbyname():** 使用gethostbyname函数可以利用字符串格式的域名获得IP地址,并且将地址信息装入 hostent 域名结构体。该函数定义如下: ![](http://www.flayerveo.com/usr/uploads/2020/02/2022399891.png) 由于hostent结构体成员h_addr_list指向字符串指针数组,也就是说数组里元素 char *, 但是 inet_ntoa() 需要的是 struct in_addr*(存放IPV4地址信息),所以要使用struct in_addr*进行强制类型转换。 **为什么hostent结构体的h_addr_list里的元素是 char \*, 而不直接是 struct in_addr*? ** ![](http://www.flayerveo.com/usr/uploads/2020/02/2443249200.png) **gethostbyaddr():** 反之,如果要利用IP地址获得域名,可以使用函数**gethostbyaddr**,定义如下: ![](http://www.flayerveo.com/usr/uploads/2020/02/2673097271.png) C++-算法库 algorithm http://www.flayerveo.com/index.php/archives/297/ 2020-02-12T07:43:01+00:00 ##导入库: ```cpp #include ``` ##非修改性序列操作 循环 对序列中的每个元素执行某操作 for_each() 查找 在序列中找出某个值的第一次出现的位置 find() 计数 在序列中统计某个值出现的次数 count() 两个序列中的对应元素都相同时为真 equal() 搜索 在序列中找出一子序列的第一次出现的位置 search() ##修改性序列操作 复制 从序列的第一个元素起进行复制 copy() 从序列的最后一个元素起进行复制 copy_backward() 交换 交换两个元素 swap() 交换指定范围的元素 swap_ranges() 交换由迭代器所指的两个元素 iter_swap() 替换 用一个给定值替换一些值 replace() 复制序列时用一给定值替换元素 replace_copy() 填充 用一给定值取代所有元素 fill() 用一给定值取代前n个元素 fill_n() 生成 用一操作的结果取代所有元素 generate() 用一操作的结果取代前n个元素 generate_n() 删除 删除具有给定值的元素 remove() 复制序列时删除具有给定值的元素 remove_copy() 唯一 删除相邻的重复元素 unique() 复制序列时删除相邻的重复元素 unique_copy() 反转 反转元素的次序 reverse() 复制序列时反转元素的次序 reverse_copy() 随机 采用均匀分布来随机移动元素 random_shuffle() ##序列排序及相关操作 排序 以很好的平均效率排序 sort() 排序,并维持相同元素的原有顺序 stable_sort() 将序列的前一部分排好序 partial_sort() 复制的同时将序列的前一部分排好序 partial_sort_copy() 第n个元素 将第n各元素放到它的正确位置 nth_element() 二分检索 找到大于等于某值的第一次出现 lower_bound() 找到大于某值的第一次出现 upper_bound() 找到(在不破坏顺序的前提下)可插入给定值的最大范围 equal_range() 在有序序列中确定给定元素是否存在 binary_search() 归并 归并两个有序序列 merge() 归并两个接续的有序序列 inplace_merge() 有序结构上的集合操作 一序列为另一序列的子序列时为真 includes() 构造两个集合的有序并集 set_union() 构造两个集合的有序交集 set_intersection() 构造两个集合的有序差集 set_difference() 构造两个集合的有序对称差集(并-交) set_symmetric_difference() 堆操作 向堆中加入元素 push_heap() 从堆中弹出元素 pop_heap() 从序列构造堆 make_heap() 给堆排序 sort_heap() 最大和最小 两个值中较小的 min() 两个值中较大的 max() 序列中的最小元素 min_element() 序列中的最大元素 max_element() 排列生成器 按字典序的下一个排列 next_permutation() 按字典序的前一个排列 prev_permutation( C++-数学库 math http://www.flayerveo.com/index.php/archives/296/ 2020-02-12T07:41:35+00:00 ##导入方式: ```cpp #include ``` ##功能: ```cpp double sqrt(double x); //开平方sqrt是square root缩写 double pow(double x,double y); //求乘方x^y,pow是power的缩写 ``` C++-库-动态链接库的编写 http://www.flayerveo.com/index.php/archives/295/ 2020-02-12T07:35:32+00:00 ##动态链接库简介: 动态链接库(Dynamic-LinkLibrary)通常包含一堆程序员自定义的变量与函数,可以在运行时动态链接到可执行文件。 ##静态库的扩展名: .dll (Win) .so(Linux) ##静态库的优缺点: 优点: ![](http://www.flayerveo.com/usr/uploads/2020/02/1611926148.png) 缺点: ![](http://www.flayerveo.com/usr/uploads/2020/02/2740564615.png) ##Windows操作系统的动态链接库: Windows操作系统核心有三个动态链接库(Kernel32.dll,User32.dll,Gdi32.dll),这些动态链接库构成了Win32 API函数。 ![](http://www.flayerveo.com/usr/uploads/2020/02/111609185.png) ##创建静态库的步骤: >(1) 创建动态库(DLL)项目 (2) 添加 .h头文件和 .cpp文件 (3) 使用extern "C" 与 _declspec(dllexport) (4) 生成 .lib 与 .dll文件 **(1) 创建动态库(DLL)项目** ![](http://www.flayerveo.com/usr/uploads/2020/02/2348995760.png) **(2) 添加 .h头文件和 .cpp文件(默认有,不用加)** ![](http://www.flayerveo.com/usr/uploads/2020/02/1717886751.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/3902678820.png) **(3) 使用extern "C" 与 _declspec(dllexport)** ![](http://www.flayerveo.com/usr/uploads/2020/02/3659368890.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/89140497.png) **(4) 生成 .lib 与 .dll文件** ![](http://www.flayerveo.com/usr/uploads/2020/02/1398097542.png) 观察Debug目录可以发现成功生成了.dll和.lib文件 ![](http://www.flayerveo.com/usr/uploads/2020/02/3548747597.png) 使用Dependency Walker工具查看该Dll1.dll文件,发现sum函数确实在Dll1.dll中: ![](http://www.flayerveo.com/usr/uploads/2020/02/3587599212.png) ##使用动态链接库: >(1) 包含库的 .h头文件 (2) #pragma comment(lib, "XXX.lib") (3) 运行时需要 .dll文件 ![](http://www.flayerveo.com/usr/uploads/2020/02/1787901471.png) C++-库-静态链接库的编写 http://www.flayerveo.com/index.php/archives/282/ 2020-02-12T07:30:17+00:00 ##静态库简介: 静态库(staic library)通常包含一堆程序员自定义的变量与函数,在编译期间由编译器与链接器将它集成到可执行文件中。 ##静态库的扩展名: .lib (Win) .a(Linux) ##静态库的优缺点: **优点:**发布时只需发布exe,因为库已被集成到可执行文件中,运行时对这个库不再依赖。 **缺点:**静态库集成到可执行文件中导致exe文件较大,后续想升级库必须重新编译 ##创建静态库的步骤: >(1) 创建静态库项目 (2) 添加 .h头文件和 .cpp文件 (3) 生成 .lib文件 **(1) 创建静态库项目:** ![](http://www.flayerveo.com/usr/uploads/2020/02/2176593518.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/2493900416.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/3692912832.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/197002588.png) **(2) 在头文件 添加 .h头文件和 .cpp文件** ![](http://www.flayerveo.com/usr/uploads/2020/02/274587304.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/2078473985.png) ![](http://www.flayerveo.com/usr/uploads/2020/02/2707968177.png) 在.h头文件中写函数的声明: ![](http://www.flayerveo.com/usr/uploads/2020/02/2561746179.png) 在.cpp源文件中写函数的实现: ![](http://www.flayerveo.com/usr/uploads/2020/02/2356725108.png) **(3) 生成 .lib文件** ![](http://www.flayerveo.com/usr/uploads/2020/02/3911259588.png) 可以发现在Debug目录下生成了.lib静态库文件: ![](http://www.flayerveo.com/usr/uploads/2020/02/4113877804.png) ##>>>使用静态库