数据在网络中传输时按照的规则就是协议(protocol)
协议(protocol)规定了数据在网络中传输的顺序,格式,以及携带哪些内容
应用层是运行一些应用程序(qq,微信等)
如下图访问百度网站用到了HTTPS协议
注意:www.baidu.com叫做域名(domain name)
1.数据加解密:发送之前加密,接收到之后解密
2.数据解压缩:将一个数据在发送前进行压缩,其总的数据量变小,传输的就快,占用网络通道时间就更短(网络通道是大家共用的),接收到之后解压缩。
3.图片/视频编解码:在发送之前将图片和视频进行编码,在接收到之后解码
1.session会话管理:登录一个网站后,例如爱奇艺,当登陆完账户之后,再在爱奇艺中打开其他页面就不需要再次重新登录了,因为是同一个账户(也叫做同一个会话),保证用户信息不会过期不需要登录
2.服务器验证用户登录:当访问某一个网站/服务器,要做用户登录,输入用户名和密码,验证密码是否正确,用户是否存在
3.断点续传:在下载一个应用的时候,断网了,恢复网络的时候,还是从没下的部分进行下载,下过的部分进不需要再下载了
TCP是传输控制协议,提供一种面向连接的、可靠的、基于字节流的传输层通信协议,有流量控制和差错控制,使用TCP协议的应用有邮件的接收和发送、文件传输、远程登录等。
需要数据稳定和完整性比较高的场景多使用TCP协议
UDP是用户数据报协议,提供一种无连接的、高效率的、低可靠性的数据传输服务,使用UDP协议的应用比如音视频聊天、在线游戏王者荣耀、工业物联网数据传输等。
需要数据时效性比较高的场景多使用UDP协议
端口决定了在数据到达设备后,操作系统要把这个数据分配到具体哪一个应用程序(应用程序会绑定端口号)
注意:应用程序和端口号之间的关系是一对多的关系,一个应用程序可以绑定多个端口号,一个端口只能被一个应用程序所绑定(一旦端口被绑定了,那其他应用程序就不能绑定该端口)
socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口
保护电脑安全,有两种方式
1.黑名单:通过拉黑IP地址,所有端口都不会接受拉黑IP地址发出的数据,以保护电脑安全
2.白名单:设备/某些应用程序只允许某些端口被外部访问,以保护电脑安全
IP是每一个接入网络设备都要具有的
查看IP的步骤
1.windows键+R键,打开一个窗口,如下
2.输入cmd,确定,就会出现命令输入窗口
3.在命令输入窗口中输入ipconfig(ip的相关配置)
4.就可以看到当前当前电脑ip地址的相关配置了
IP地址分为两类,一种是IPv4地址,另一种是IPv6地址
当我们使用网卡的时候就会给网卡分配一个IP地址(手动分配或者是自动获取的)
网卡除了IP地址还有MAC地址,MAC地址出厂的时候是全球唯一的(生产网卡的厂商会给它分配全球唯一的MAC地址)
交换机与路由器的区别:
1.交换机只能通过单纯的物理连接
2.交换机的数据不能跨交换机,路由器可以跨路由器
将设备中传输的数字信号转换成电信号或者是光信号,通过光缆或者是电缆传输到对端去
发射端应用层->发射端表示层->发射端会话层->发射端传输层->发射端网络层->发射端数据链路层->发射端物理层,然后转换成电信号或者是光信号通过电缆或者光缆传输到对端设备,再从对端设备的物理层->对端设备的数据链路层->对端设备的网络层->对端设备的传输层->对端设备的会话层->对端设备的表示层->对端设备的应用层
(1)应用层
(2)传输层
(3)网络层
(4)物理层
1.访问应用程序的客户端固定(每一个应用程序都有自己固定的客户端,只能由自己固定的客户端访问)
2.客户端和服务端的协议可以是任意协议(常用的是TCP和UDP协议)
1.访问网站的浏览器不固定(一个网站可以由多个浏览器访问)
2.必须使用HTTP或者是HTTPS协议
1.加载库(库为ws2_32.lib)
2.创建套接字(所用到的函数为socket())
3.绑定IP地址和端口号
4.接消息(所用到的函数为recvfrom()),回消息(所用到的函数为sendto())
5.关闭套接字(所用到的函数为closesocket()),卸载库(所用到的函数为WSACleanup())
#include
#include
//这里说一下包含头文件时的一个知识点
//<>就是在操作系统指定的地方开始找要包含的头文件
//“”就是从当前文件夹开始找要包含的头文件
using namespace std;
//导入依赖库(使用WSAStartup函数和WSACleanup函数时需要此依赖库)
#pragma comment(lib,"Ws2_32.lib")//这里只需要输入名字就行,编译器知道它的具体路径在哪
int main() {
//1.加载库
//创建一个WORD类型的变量
WORD version = MAKEWORD(2,2);//MAKEWORD这个宏是创键一个WORD类型的变量(这里创建的是2.2版本的WORD类型的变量,然后用version接一下这个变量的值)
//定义一个结构体变量
WSADATA data;
//加载库的函数
int err = WSAStartup(version, &data);/*此函数的头文件为winsock2.h,此函数第一个参数是一个输入参数,该输入参数一个是版本号(WORD类型的)
第二个参数是输出参数,该输出参数是一个结构体指针
接一下返回值,并根据返回值判断是否成功*/
//判断是否成功,打印日志
if (err != 0) {
cout << "WSAStartup error" << endl;
return 1;
}
//判断加载的版本号是否正确
if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2) {
cout << "WSAStartup Version error" << endl;
//卸载库(程序退出之前需要将加载的库卸载)
WSACleanup();//此函数没有参数
//程序退出
return 1;
}
else {
cout << "WSAStartup suuccess" << endl;
}
//2.创建套接字
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);/*此函数的第一个参数是使用的地址族格式(我们这里填的是AF_INET(这是个宏定义),表示用的使用IP地址的格式是IPv4类型的),
第二个参数是表示创建一个什么类型的套接字,这里填的是SOCK_DGRAM(这个是宏定义)表示创建的是使用UDP的套接字
第三个参数表示用哪个协议,这里填的是IPPROTO_UDP(这个是宏定义)表示使用的是UDP的协议
如果此函数成功了,那么此函数返回的是新创建出来的套接字
如果失败了,返回的是INVALID_SOCKET*/
//判断套接字是否创建成功
if (sock == INVALID_SOCKET) {
cout << "socket error" << endl;
//获取失败的错误码并进行打印输出
cout << WSAGetLastError() << endl;
//卸载库(程序退出之前需要将加载的库卸载)
WSACleanup();//此函数没有参数
//程序退出
return 1;
}
else {
cout << "socket success" << endl;
}
//3.绑定ip地址和端口号(在操作系统那注册一下当前进程对应的ip和端口)
//定义一个sockaddr_in类型的结构体
sockaddr_in addServer;
//进行结构体的初始化
addServer.sin_family = AF_INET;//初始化地址族格式,和上面套接字的地址族格式一样,表示用的使用IP地址的格式是IPv4类型的
addServer.sin_port = htons(666666);//绑定端口号(端口号可以随便写,但是为了避免和已有的应用程序绑定的端口号重复,端口号最好写5位或6位的长度的),htons函数是将端口号的存储方式转换成网络字节序(大端存储)
addServer.sin_addr.S_un.S_addr = INADDR_ANY;//绑定所有网卡(因为大部分设备不只有一个网卡(也就不只有一个IP地址))
//绑定IP地址和端口号的函数
err = bind(sock, (sockaddr*)&addServer,sizeof(addServer));/*此函数的第一个参数是套接字
第二个参数是sockaddr类型的结构体指针
第三个参数是第二个参数中结构体所占的空间的大小
如果此函数成功了,
如果失败了,*/
if (err== SOCKET_ERROR) {
cout << "bind error" << endl;
//获取失败的错误码并进行打印输出
cout << WSAGetLastError() << endl;
//关闭套接字,卸载库
closesocket(sock);//在将加载的库卸载之前关闭套接字
WSACleanup(); //程序退出之前需要将加载的库卸载
//程序退出
return 1;
}
else {
cout << "bind success" << endl;
}
int recv = 0;//定义一个变量用来接recvfrom函数的返回值
int send = 0;//定义一个变量用来接sendto函数的返回值
char recvData[1024] = ""; //定义一个空间用来接要来的数据
char sendData[1024] = "";//定义一个空间用来接要来的数据
sockaddr_in recvaddr;//定义一个sockaddr_in的结构体,这里要存的是数据是从哪个端口号和ip来的(这里注意要强转成sockaddr*)
int recvaddrlength = sizeof(recvaddr);//这个变量存的是sockaddr_in结构体的空间大小
while (true) {
//4.接受数据
recv=recvfrom(sock, recvData,sizeof(recvData),0, (sockaddr*)&recvaddr, &recvaddrlength);
/*
第一个参数是用哪个套接字接收数据(输入参数)
第二个参数是用来接收数据的空间(输出参数)
第三个参数是用来接收数据的空间的长度(输入参数)
第四个参数是标志位,如果有特殊套接字的话要设置标志位,这里无特殊的,不需要使用标志位,填0(输入参数)
第五个参数是一个sockaddr的结构体,这里存的是端口号和ip,表示数据是从哪个ip地址和端口号来的(输出参数)
第六个参数是第五个参数结构体的大小(输入,输出参数)
如果成功了,返回的是接收到的数据的字节数
如果失败了返回SOCKET_ERROR
*/
if (recv > 0) {
//打印ip地址和接收的数据
cout << "IP:" << inet_ntoa(recvaddr.sin_addr)/*这里因为recvaddr.sin_addr这个变量村的ip地址是u_long类型的,看不懂,所以我们要转成字符串类型*/ << " " << "SAY: " << recvData << endl;
//我们打印出来能看懂的ip地址,"192.168.3.123"这个是十进制四等分ip字符串类型的地址
//我们打印出来看不懂的ip地址,u_long类型的ip地址
//这里介绍两个函数
//inet_addr(),这个函数是将字符串类型转换成u_long类型
//inet_ntoa(),这个函数是直接把sin_addr的结构体转换成字符串类型
}
//5.发送数据
cin >> sendData;//把要输出的数据存到用来发送数据的空间
send = sendto(sock, sendData, sizeof(sendData), 0, (sockaddr*)&recvaddr, recvaddrlength);
/*第一个参数是用哪个套接字发送数据(输入参数)
第二个参数是用来发送数据的空间(输出参数)
第三个参数是用来发送数据的空间的长度(输入参数)
第四个参数是标志位,如果有特殊套接字的话要设置标志位,这里无特殊的,不需要使用标志位,填0(输入参数)
第五个参数是一个sockaddr的结构体,这里存的是端口号和ip,表示数据是发到哪个ip地址和端口号(输出参数)
第六个参数是第五个参数结构体的大小(输入,输出参数)
如果成功了,返回的是发送的数据的字节数
如果失败了返回SOCKET_ERROR
*/
if (send == SOCKET_ERROR) {//如果失败了
//输出错误日志,获取失败的错误码并进行打印输出
cout << "send failed " << WSAGetLastError() << endl;
break;
}
//6.关闭套接字
closesocket(sock);
//7.卸载库
WSACleanup();//此函数没有参数
return 0;
}