分层模型
每一层利用下层提供的服务为基础来为上层提供服务,最终服务于应用层的应用程序
应用层
网络应用的体系结构
- 客户-服务器模式 (C/S:client/server)
- 服务器先且一直运行,守护在某些端口上
- 可拓展性,可靠性比较差
- 客户数量达到一定阈值后会断崖式下降
- 对等模式 (P2P:Peer To Peer)
- 任意端系统之间可以通信,每个节点即使客户端又是服务器
- 自拓展性
- 迅雷,快播😳
- 混合体 客户-服务器和对等体系结构
跨端系统进程通信
通过套接字(socket)来从网络发送和接受报文,应用程序开发者控制套接字在应用层的一切,对套接字在传输层的控制几乎为0
socket具体看这篇文章: https://segmentfault.com/a/1190000041413541
简单来说tcp socket就是操作系统维护的一个五元组**[源IP, 源端口, 目的IP, 目的端口, 类型(TCP or UDP)],udp socket就是一个三元组[目的IP, 目的端口, 类型(TCP or UDP)]**,返回的socket就是这个数组的下标
跨端系统进程通信需要的信息
- 目标主机地址:IP地址
- 所采用传输层协议:TCP or UDP
- 目标主机中目标进程的标识符:端口号
套接字是应用程序进程和传输层协议直接的接口,使用哪种传输层协议,需要结合应用进程的要求
因特网提供了UDP和TCP两种传输层协议
- TCP:Transmission Control Protocol
- UDP:User Datagram Protocol
TCP安全是靠安全套接字层(secure sockets layer,SSL)来实现的,SSL在应用层实现
HTTP
http 超文本传输协议(hypertext transfer protocol) 使用tcp作为传输层协议,是一个无状态协议(不保存关于客户的任何信息) 默认80端口
http/1.0采用非持续连接,http/1.1默认采用持续连接
非持续,持续连接区别在于一个对象传输结束后,是否立即关闭tcp连接
流水线,一个请求对象还没到达时,再去请求另一个对象
非流水线 一个一个完整请求,请求-到达-请求-到达…………
URL基本格式:
protocol://user:password@www.someSchool.edu/someDept/pic.gif:port 协议 用户名 口令 主机名 路径名 端口
|
HTTP请求报文的通用格式
post方法时才会使用请求实体体
HTTP响应报文的通用格式
cookie
http是无状态的,为了识别用户,实现某写功能,采用了cookie与用户身份联系起来
cookie组件
- 请求和响应报文的cookie首部行
- 用户端系统的cookie文件,由浏览器管理
- web后端数据库
用户第一次访问某个web站点时,web站点会生成一个唯一识别码,并把他当成索引在后端数据库产生一个表项,发送的响应报文会有Set-cookie的首部行
浏览器接受到响应报文后,会在本地cookie文件里添加一行服务器的主机名和cookie值,随后每次对该web的请求都会带有cookie首部行
web缓存器(代理服务器)
可以配置浏览器,使http请求首先经过web缓存器,如果web缓存器存有请求对象的副本,直接返回给浏览器,否则缓存器向原始服务器http请求,把响应对象存储到web缓存器,再通过web缓存器和浏览器原本的tcp连接把对象再返回给浏览器
二八定律,使缓存效果良好
条件get方法
为了避免web缓存器里的副本和原始服务器里的版本不一致,存在一种机制:条件get方法
请求报文使用get方法,包含If-Modified-Since:
首部行,就是一个条件get请求报文
具体过程
web缓存器向原始服务器请求对象,服务器发送的响应报文里下面一句
Last-Modified: Wed, 9 Sep 2023 06:23:45
|
缓存器存储对象时会记下修改时间
一段时间用户再次请求该对象时,缓存器会向服务器发送条件get请求报文
Get /path/xxx.jpg HTTP/1.1 Host: www.grxer.com If-Modified-Since: Wed, 9 Sep 2023 06:23:45
|
如果对象已经修改,在get实体体里返回200ok 实体体里包含该对象,否则返回304 Not Modified,告诉web缓存器可以直接使用该对象
HTTP/1.1 304 Not Modified ......
(empty entity body)
|
E-Mail
三个重要组成部分
- 用户代理 user agent
- 邮件服务器 mail server
- 简单邮件传输协议SMTP simple mail transfer protocol
SMTP(推协议)
使用TCP作为传输层协议 默认25号端口,连续连接
邮件服务器,TCP连接建立后会,进行SMTP握手来确认收发方地址等等,之后发送报文
报文分为首部和主体,由一个空行隔开,报文必须为7位ascii编码
From:hello@xxx.com To:world@xxx.com ....
主体内容
|
对于一些不能用ascii编码表示的数据采用了MIME (Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型
使用MIME需要包含一些首部行
MIME版本
内容类型
Content-Type: [type]/[subtype]; parameter
|
内容传输编码方式
Content-Transfer-Encoding: [mechanism]
|
mechanism的值可以指定为“7bit”,“8bit”,“binary”,“quoted-printable”,“base64”。
邮件访问协议(拉协议)
POP3 Post Office Protocol–Version 3,第三版邮件协议
IMAP Internet Mail Access Protocol ,因特网邮件访问协议
HTTP
DNS
Domain Name System 域名系统,主要功能是将主机名解析为ip地址,通常是由其他应用层协议所使用
DNS运行在UDP之上,默认使用53号端口
DNS采用分布式,分层数据库,大致分为三种类型的服务器
- 根DNS服务器
- 顶级域(Top-Level Domain,TLD)DNS服务器
- 权威DNS服务器
DNS查询有两种方式:递归和迭代。DNS客户端设置使用的DNS服务器一般都是递归服务器,它负责全权处理客户端的DNS查询请求,直到返回最终结果。而DNS服务器之间一般采用迭代查询方式。
递归:DNS服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果DNS服务器本地没有存储查询DNS 信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。
迭代:DNS服务器本地如果没有请求的DNS信息,会向客户机返回能够解析该查询请求的DNS服务器地址,客户机再向这台DNS 服务器提交请求,依次循环直到返回查询的结果
访问 www.ytu.edu.cn
DNS服务器里存储了资源记录(Resource Record,RR),是一个四元组
DNS报文
wireshark一个dns返回报文
P2P
p2p文件分发协议 BitTorrent
视频流和内容分发网
流式视频占据ISP流量的大部分,由于http流存在缺陷,一般采用DASH(Dynmic Adaptive Streaming over Http):经HTTP的动态适应性流来传输
DASH
服务器:
- 将视频文件分割成多个块
- 每个块独立存储,编码于不同码率(8-10种)
- 告示文件(manifest file): 提供不同块不同码率的URL
客户端:
- 先获取告示文件
- 周期性地测量服务器到客户端的带宽
- 查询告示文件,在一个时刻请求一个块,HTTP头部指定字节范围
- 如果带宽足够,选择最大码率的视频块
- 会话中的不同时刻,可以切换请求不同的编码块 (取决于当时的可用带宽)
CDN
Content Distribution Networks 内容分发网络
为了解决高并发流媒体服务,通过cdn全网部署缓存节点,存储服务内容,就近为用户提供服务,提高用户体验
怎么让用户知道部署的cdn服务器在哪?
通过manifest file文件里写入cdn信息
或者cdn利用dns截获和重定向请求(大多数)
利用dns截获和重定向请求
- 用户的本地 DNS 服务器(LDNS)将该 DNS 请求中继到目标服务器的权威 DNS 服务器,目标服务器的权威DNS服务器并不返回一个 IP 地址,而是向 LDNS 返回一个第三方 CDN 的主机名
- LDNS再发送对第三方CDN的主机名的DNS请求,第三方CDN专用DNS基础设施根据它的选择策略将最符合的CDN的IP地址给LDNS
- LDNS将该CDN转发给用户主机
- 主机建立于CDN的TCP连接,发送http get请求,如果使用dash,会先获取告示文件等等
套接字编程
UDP–python
UDPServer
from socket import * serverPort=1234 ''' 创建 Socket 地址簇: AF_INET (IPv4) 类型: SOCK_DGRAM (使用 UDP 传输控制协议) ''' serverSocket=socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(('',serverPort)) while True: message,clientAddr=serverSocket.recvfrom(2048) modifiedMessage=message.decode().upper() serverSocket.sendto(modifiedMessage.encode(),clientAddr)
|
UDPClient
from socket import * serverName="127.0.0.1" serverPort=1234
clientSocket=socket(AF_INET,SOCK_DGRAM) message=input('string-->STRING: ') clientSocket.sendto(message.encode(),(serverName,serverPort)) modifiedMessage,serverAddr=clientSocket.recvfrom(2048) print(modifiedMessage.decode()) clientSocket.close()
|
TCP–python
TCPServer
from socket import * serverPort=1234 ''' 创建 Socket 地址簇address family: AF_INET (IPv4) 类型: SOCK_STREAM (使用 TCP 传输控制协议) ''' serverSocket=socket(AF_INET,SOCK_STREAM) serverSocket.bind(('',serverPort))
serverSocket.listen(1) while True: connectionSocket,addr=serverSocket.accept() print(addr) message=connectionSocket.recv(1024) modifiedMessage=message.decode().upper() connectionSocket.send(modifiedMessage.encode()) connectionSocket.close()
|
TCPClient
from socket import * serverName="127.0.0.1" serverPort=1234 clientSocket=socket(AF_INET,SOCK_STREAM)
clientSocket.connect((serverName,serverPort)) message=input('string-->STRING: ') clientSocket.send(message.encode()) modifiedMessage,serverAddr=clientSocket.recvfrom(2048) print(modifiedMessage.decode()) clientSocket.close()
|
UDP–linux C
UDPServer
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <ctype.h> #define PORT 1234 #define BUFFER_SIZE 2048 #define MAXQUEUE 5 void stringLow2Upper(char *string) { for (int i=0; i<strlen(string); i++) { string[i] =toupper(string[i]); } } int main() { struct sockaddr_in serverSockaddr; serverSockaddr.sin_family = AF_INET; serverSockaddr.sin_addr.s_addr = htonl(INADDR_ANY); serverSockaddr.sin_port = htons(PORT); int serverSocket = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == bind(serverSocket, (struct sockaddr *)&serverSockaddr, sizeof(serverSockaddr))) { perror("bind"); exit(-1); } while (1) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); struct sockaddr_in clientSockaddr; socklen_t length = sizeof(clientSockaddr); recvfrom(serverSocket, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&clientSockaddr, &length); printf("connect%s:%d\n", inet_ntoa(clientSockaddr.sin_addr), ntohs(clientSockaddr.sin_port)); stringLow2Upper(buffer); sendto(serverSocket, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&clientSockaddr, length); } }
|
UDPClient
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #define PORT 1234 #define BUFFER_SIZE 2048 int main() { struct sockaddr_in serverSockaddr = { .sin_addr.s_addr = inet_addr("127.0.0.1"), .sin_port = htons(PORT), .sin_family = AF_INET, }; int clientSocket = socket(AF_INET, SOCK_DGRAM, 0); char sendBuffer[BUFFER_SIZE], recvBuffer[BUFFER_SIZE]; socklen_t length = sizeof(serverSockaddr); memset(sendBuffer, 0, BUFFER_SIZE); memset(recvBuffer, 0, BUFFER_SIZE); fgets(sendBuffer, BUFFER_SIZE, stdin); sendto(clientSocket, sendBuffer, BUFFER_SIZE, 0, (struct sockaddr*)&serverSockaddr, length); recvfrom(clientSocket, recvBuffer, BUFFER_SIZE, 0, (struct sockaddr*)&serverSockaddr, &length); printf("%s",recvBuffer); }
|
TCP–linux C
TCPServer
#include <arpa/inet.h> #include <ctype.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #define PORT 1234 #define BUFFER_SIZE 2048 #define MAXQUEUE 5 void stringLow2Upper(char *string) { for (int i = 0; i < strlen(string); i++) { string[i] = toupper(string[i]); } } int main() {
struct sockaddr_in serverSockaddr; serverSockaddr.sin_family = AF_INET;
serverSockaddr.sin_addr.s_addr = htonl(INADDR_ANY); serverSockaddr.sin_port = htons(PORT); int serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (-1 == bind(serverSocket, (struct sockaddr *)&serverSockaddr, sizeof(serverSockaddr))) { perror("bind"); exit(-1); } if (listen(serverSocket, MAXQUEUE) == -1) { perror("listen"); exit(-1); } while (1) { char buffer[BUFFER_SIZE]; memset(buffer, 0, BUFFER_SIZE); struct sockaddr_in clientSockaddr; socklen_t length = sizeof(clientSockaddr); int connectionSocket = accept(serverSocket, (struct sockaddr *)&clientSockaddr, &length); if (connectionSocket < 0) { perror("connect"); exit(-1); } printf("connect%s:%d\n", inet_ntoa(clientSockaddr.sin_addr), ntohs(clientSockaddr.sin_port)); recv(connectionSocket, buffer, BUFFER_SIZE, 0); puts(buffer); stringLow2Upper(buffer); send(connectionSocket, buffer, BUFFER_SIZE, 0); close(connectionSocket); } }
|
TCPClient
#include <arpa/inet.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #define PORT 1234 #define BUFFER_SIZE 2048 int main() { char sendBuffer[BUFFER_SIZE], recvBuffer[BUFFER_SIZE]; memset(sendBuffer, 0, BUFFER_SIZE); memset(recvBuffer, 0, BUFFER_SIZE); struct sockaddr_in serverSockaddr; serverSockaddr.sin_family = AF_INET; serverSockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); serverSockaddr.sin_port = htons(PORT); int clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (connect(clientSocket, (struct sockaddr *)&serverSockaddr, sizeof(serverSockaddr)) < 0) { perror("connect"); exit(-1); } puts("string-->STRING"); fgets(sendBuffer, BUFFER_SIZE, stdin); send(clientSocket, sendBuffer, BUFFER_SIZE, 0); recv(clientSocket, recvBuffer, BUFFER_SIZE, 0); puts(recvBuffer); close(clientSocket); }
|