前言
在这篇文章中,荔枝将会对应用层的最后的内容进行总结,主要是套接字编程和视频流的相关知识,明天就可以步入运输层的学习啦。
目录
一、视频流
HTTP流中,视频仅是储存在HTTP服务器的一个普通文件。在视频传输过程中,流式视频会以帧数进行划块并被用户应用程序周期性根据帧数抓取并在流式视频缓存中解压,当缓存达到上限的时候,就会开始播放,同时流式视频应用程序会对视频后面内容的帧进行缓存。
1.1 DASH
DASH(Dynamic Adaptive Streaming over HTTP),经HTTP的动态适应性流。为了避免客户在接收相同编码的视频时由于带宽的不同而出现错误,我们需要能够根据自身的带宽水平来下载缓存视频文件,这其实就是DASH的应用场景,也就是说,我们可以通过DASH来根据自身带宽的状态来动态请求不同版本的视频流文件。
使用了DASH后,每一个HTTP服务器会有一个告示文件,里面包含着视频文件的所有版本和对应的URL及其比特率。
1.2内容分发网——CDN
CDN(Content Distribute Network)内容分发网,几乎现阶段所有的主要的视频流公司都利用内容分发网来应对数以亿计的视频流需求。CDN管理着不同地理位置的服务器,这些服务器中存储着视频文件的副本,在视频流需求出现的时候,CDN可以为该用户请求定向到一个提供最好用户体验的CDN服务器上。
CDN的分类:
- 专用CDN
- 第三方CDN
CDN操作
为了实现上述的过程,CDN需要在用户发起请求时截获请求并重定向,那么这个过程是如何实现的?
其实大多数的CDN都是基于DNS来实现用户请求的截获和重定向,该过程如下:当用户点击连接访问视频播放页面发起DNS请求的时候,本地DNS服务器会将该请求报文经过DNS层次结构发送到特定前缀识别下的权威DNS服务器,为了将DNS请求移交给特定的权威DNS服务器中(我们假设是targetCDN),该服务器观察到前缀中特定的字段之后不会返回IP地址,而是会返回targetCDN域的一个主机名。此时本地DNS服务器会向targetCDN发送第二次请求获得targetCDN里面内容服务器的IP地址,之后由本地DNS服务器将IP地址转发给客户。这就完成了整个CDN截取和和重定向DNS请求的过程。
集群选择策略
CDN的核心是集群选择策略,及动态地将客户定向到CDN中的某个服务器集群或数据中心地机制。简单点讲:我们在上面介绍了CDN截获和重定向DNS请求,那么重定向到哪里,这就是集群选择。一般最简单的就是选择地理上最为临近的CDN集群,当然了,地理上最为邻近不意味着最近,CDN为了基于当前的流量条件为用户定向到最好的集群服务就需要能够实时测量客户端的网络时延和丢包性能。
二、面向套接字编程
2.1 UDP套接字编程
我们在前面的学习中已经知道套接字socket其实就是应用层于运输层之间的接口,同样也是用户代理和服务器之间的门户。在使用UDP套接字实现两个通信进程的交互时候,需要将目的地址附在该分组上,目的地址主要是由目的主机的IP地址和目的主机的特定套接字端口号组成。当然了,发送方的源地址也会附在分组上,但这一部分的工作主要是由底层的操作系统来实现的。
客户端程序:
from socket import *
serverName = '主机名或者是IP地址'
serverPort = 12000
#创建套接字,参数第一个指向的是地址簇,即底层网络是Ipv4,第二个是套接字类型,这里是UDP套接字
clientSocket = socket(AF_INET,SOCK_DGRAM)
#python内置功能,这里是在客户上用户以'Input lowercase sentence:'提示输入的内容,会被放在message中
message = raw_input('Input lowercase sentence:')
#报文需要通过编码变成字节类型,并用sendto为分组附上目的地址
clientSocket.sendto(message.encode(),(serverName,serverPort))
#一个来自于因特网的分组到达该套接字时,内容保存在modifiedMessage,源地址放在serverAddress
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print(modifiedMessage.decode()) #报文转成字符串后打印出来
clientSocket.close() #关闭套接字
服务器端程序:
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(('',serverPort)) #将端口号与服务器的套接字绑定在一起
print('The server is ready to receive')
while True:
message, clientAddress = serverSocket.recvfrom(2048) #clientAddress保存客户的IP和端口号
#获取报文并将报文转成字符串,同时进行大写处理
modifiedMessage = message.decode().upper()
#将处理后的报文和地址放在一个分组中并交付到客户端,等待下一组UDP报文
serverSocket.sendto(modifiedMessage.encode(),clientAddress)
当我们在进程中生成一个套接字的时候,主句就会给他分配一个端口作为标识符以区分不同的套接字。
2.2 TCP套接字编程
与UDP套接字不同,TCP套接字是一个面向连接的过程。客户在发送数据之前需要跟服务器建立一个TCP连接,即通过TCP的三次握手的过程,该过程发生在运输层中。当客户在生成其套接字的时候会指定服务器的IP地址和欢迎套接字的端口号,同时在三次握手期间服务器会生成一个新的套接字用来与客户端连接一传输数据,这个套接字被称为连接套接字。抽象的看,其实就相当于客户套接字和服务器的连接套接字之间建立起了一座桥梁来传输数据,实现TCP通信。
客户端程序:
from socket import *
serverName = 'servername'
serverPort = 12000
clientSocket = socket(AF_INET,SOCK_STREAM)
#在创建客户的套接字后要创建与服务器端的TCP连接
clientSocket.connect((serverName,serverPort))
sentence= raw_input('Input lowercase sentence:')
#将字符串中的字节放在TCP连接中,这里并未显示指明目的地址
clientSocket.send(sentence.encode())
modifiedSentence = clientSocket.recv(2048)
print('From Server:',modifiedMessage.decode())
clientSocket.close()
服务器端程序:
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_STREAM) #欢迎套接字
#将套接字与服务器的端口号连接在一起
serverSocket.bind(('',serverPort))
#监听客户敲门
serverSocket.listen(1)
print('The server is ready to receive')
while True:
#客户敲门,创建一个新套接字connectionSocket,完成握手
connectionSocket, addr= serverSocket.accept(1)
sentence = connectionSocket.recv(1024).decode()
capitalizedSentence = sentence.upper()
connectionSocket.send(capitalizedSentence.encode())
connectionSocket.close()
TCP与UDP最大不同就是面向连接,我们在创建好TCP连接后,不用再向分组中附上目的地址,而是直接丢在TCP连接里面就可以实现传输。
总结
在这篇文章中,荔枝主要梳理了一些视频流的知识点,了解了视频的分发机制。同时也介绍了面向套接字编程,学习了TCP和UDP套接字编程,为后面的运输层知识打下基础。总之,应用层基础知识就梳理到这里啦,明天终于可以到我心心念念的运输层啦哈哈哈哈。
今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈。
谢谢大家的支持嘻嘻嘻~~~
比心心♥~~~