6.21 静态库与动态库
库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态、动态是指链接。静态库在链接期把整个库文件都拷贝到可执行文件中,而动态库在链接期只是把索引文件拷贝到可执行文件中,在运行时才调用动态库。(通过索引文件找到动态库文件)
静态库与动态库优缺点
静态库:
优点:由于静态库是将整个库文件都拷贝到可执行文件中,所以调用速度相对快。
缺点:1.浪费内存空间(多个应用程序依赖同一个静态库时,多个程序运行时,会在内存中拷贝多份静态库)2.对程序的更新、部署、发布比较麻烦,静态库修改后,需要重新编译整个可执行文件,重新安装。
动态库:
优点:1.节省内存空间(多个应用程序依赖同一个动态库时,多个程序运行时,只会在内存中拷贝一份动态库)2.对程序的更新、部署、发布简单,动态库修改后,只需要重新编译动态库即可,不需要重新编译整个可执行文件。
缺点:调用速度比静态库慢,移植的时候需要同时移植可执行文件和动态库。
6.22 网络基本模型
七层网络模型
应用层
用来提供应用程序之间的信息通信的(例:淘宝、qq、微信等)
涉及到的协议(protocol):
HTTP
超文本传输协议,TCP端口 80
HTTPS
超文本传输安全协议,TCP端口 443,相比HTTP多了SSL加密方式,更安全
FTP
File Transfer Protocol,文件传输协议,TCP端口 23
DNS
Domain Name System,域名系统,UDP端口 53,将域名解析成IP地址
DHCP
Dynamic Host Configuration Protocol,动态主机配置协议,UDP端口 68,动态获取网络配置
Telnet
远程终端协议,TCP端口 23,远程控制Web服务器
SMTP
简单邮件传输协议,TCP端口 25,提供可靠且有效的电子邮件传输的协议
SSH
Secure Shell,安全外壳,是一种网络安全协议,通过加密和认证机制实现安全的访问和文件传输等业务
表示层
提供数据格式转换的,主要用来数据加解密、数据解压缩。图片/视频编解码。
几个注意点:
数据在发送之前要用一定的加密方法进行加密
想要使数据发送的快,就要对数据进行压缩,这样也会使网络更通畅
实时传输(直播)一般都需要视频编解码,不然延迟会高
会话层
session会话管理、服务器验证用户登录、断点续传。
session会话管理 例:如果我们在登录一个网站之后,浏览页面过程中要跳转网页,那么不需要重复登录,靠的就是session会话管理,它保证了用户在同一个网站中不管多少次的页面跳转,都不需要重新登录
断点续传 例:在下载文件中途有网络波动,在一段时间内网络恢复了,那么会接着下载
传输层
(操作系统级别的)
TCP
TCP,传输控制协议,提供一种面向连接的、可靠的、基于字节流的传输层通信协议,有流量控制和差错控制,使用TCP协议的应用比如邮件的接收和发送、文件传输、远端登录。需要数据稳定和完整性比较高的场景多使用TCP协议。
UDP
UDP,用户数据报协议,提供一种无连接的、高效率、低可靠性的数据传输服务,使用UDP协议的应用比如音视频聊天、在线游戏王者荣耀等、工业物联网数据传输等。需要数据时效性比较高的场景多使用UDP协议。
线程
端口
注:一个应用程序可以使用多个端口,但是一个端口只能被一个应用程序使用
socket(套接字)
是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
其中TCP/IP协议不是TCP和IP协议,而是代表一个协议族
数据传输一定需要的两个信息:
IP地址和端口号,IP地址能够保证数据到达目标设备,端口号保证数据能从设备到应用程序
网络层
网络层不止有协议,还有实际的设备,比如路由器
协议有IP、ARP、RARP、防火墙
网络层主要做的是寻址和路由选择
可以通过ipconfig查看自己的ip地址
数据链路层
交换机、网卡(MAC地址)
设备有几个网卡,就有几个ip地址
交换机只能进行单纯的转发,大家要连到同一台交换机上,而路由器除了支持数据转发外还支持路由寻址
物理层
光纤、网线
把数字信号转换为电信号或光信号进行比特流传输
TCP/IP事实标准网络模型
包含应用层、传输层、网络层、物理层
C/S架构与B/S架构对比
C/S(Client/Server)
客户端和服务端可以使用任意协议(常用TCP、UDP)连接
需要指定客户端,每个应用程序都有指定的客户端
B/S(Browser/Server)
客户端不受限制
浏览器和服务端必须使用 http 或 https 协议
实战训练:C/S架构
基于UDP
服务端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
using namespace std;
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
int main()
{
//加载库
WORD wVersion = MAKEWORD(2, 2);/*创建版本号 2.2 版本*/
WSAData data;
int err = WSAStartup(wVersion,&data);
if (0 != err)
{
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
if (2 != LOBYTE(data.wVersion) || 2 != HIBYTE(data.wVersion))/*判断加载的版本号是否正确*/
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//创建套接字
SOCKET sock = socket(AF_INET/*IPV4版本*/, SOCK_DGRAM/*UDP报文段*/, IPPROTO_UDP/*协议类型为UDP*/);
if (INVALID_SOCKET == sock)
{
printf("sock failed with error: %d\n",WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("sock success\n");
//绑定ip和端口号
/*这里相当于初始化结构体*/
struct sockaddr_in addrServer;
addrServer.sin_family = AF_INET;/*IPV4类型*/
addrServer.sin_addr.S_un.S_addr = INADDR_ANY;/*任意网卡*/
addrServer.sin_port = htons(12345);/*htons 转换成网络字节序*/
/*这里是绑定*/
err = bind(sock,(sockaddr*)&addrServer,sizeof(addrServer));
if (SOCKET_ERROR == err)
{
printf("bind failed with error: %d\n", WSAGetLastError());
//关闭套接字
closesocket(sock);
//卸载库
WSACleanup();
return 1;
}
else
printf("bind success\n");
//收发数据
int recvNum = 0;
char recvBuf[1024] = "";
int sendNum = 0;
char sendBuf[1024] = "";
struct sockaddr_in addrClient;
int addrClientSize = sizeof(addrClient);
while (true)
{
//接收消息
recvNum = recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrClient, &addrClientSize);
if (recvNum > 0)
{
//打印收到的数据
char ip[100] = "";
cout << "ip:" << inet_ntoa(addrClient.sin_addr) << ":" << recvBuf << endl;
/*inet_addr 将点分十进制ip地址转换为ulong类型,inet_ntoa 将ulong类型转换为点分十进制*/
}
else if (recvNum == 0)
{
cout << "connect closed,please try again..." << endl;
}
else
break;
//发送消息
gets_s(sendBuf);//读取数据
/*sendto 给谁发信息就绑定谁的地址信息*/
sendNum = sendto(sock,sendBuf, sizeof(recvBuf), 0, (sockaddr*)&addrClient, addrClientSize);
if (SOCKET_ERROR == sendNum)
{
cout << "send error" << endl;
break;
}
}
//关闭套接字
closesocket(sock);
//卸载库
WSACleanup();
return 0;
}
客户端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
using namespace std;
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
int main()
{
//加载库
WORD wVersion = MAKEWORD(2, 2);/*创建版本号 2.2 版本*/
WSAData data;
int err = WSAStartup(wVersion, &data);
if (0 != err)
{
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
if (2 != LOBYTE(data.wVersion) || 2 != HIBYTE(data.wVersion))/*判断加载的版本号是否正确*/
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//创建套接字
SOCKET sock = socket(AF_INET/*IPV4版本*/, SOCK_DGRAM/*UDP报文段*/, IPPROTO_UDP/*协议类型为UDP*/);
if (INVALID_SOCKET == sock)
{
printf("sock failed with error: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("sock success\n");
//接收端的地址信息
struct sockaddr_in addrServer;
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(12345); //htons转换成网络字节序
addrServer.sin_addr.S_un.S_addr = inet_addr("192.168.246.1");
//收发数据
int recvNum = 0;
char recvBuf[1024] = "";
int sendNum = 0;
char sendBuf[1024] = "";
while (true)
{
//发送消息
gets_s(sendBuf);//读取数据
sendNum = sendto(sock, sendBuf, sizeof(recvBuf), 0, (sockaddr*)&addrServer, sizeof(addrServer));
if (SOCKET_ERROR == sendNum)
{
cout << "send error" << endl;
break;
}
//接收消息
recvNum = recvfrom(sock, recvBuf, sizeof(recvBuf), 0, NULL/*传出参数,后续用不上addrServer了,所以可以传NULL,后面的NULL同理*/, NULL);
if (recvNum > 0)
{
//打印收到的数据
cout << "server say:" << recvBuf << endl;
}
else if (recvNum == 0)
{
cout << "connect closed,please try again..." << endl;
}
else
break;
}
//关闭套接字
closesocket(sock);
//卸载库
WSACleanup();
return 0;
}
运行结果
可见,服务端和客户端可以实现一来一回收发数据。
常见报错原因
无法解析的外部符号:没有添加依赖库
未定义标识符:没有头文件