上一篇文章当中,学记大体阐述了套接字与网络编程之间的关系,套接字是 TCP/IP 协议的延伸。提供了各种编程语言网络编程的接口。
在上一篇文章的结尾,学记简单地搭建了一个本地客户端,那个代码不是多么复杂,是一个客户端样板,在实际应用当中,需要添加更多的组件。
因此,此篇文章学记将接续上一篇的内容,继续阐述网络编程方面的知识。
当然,想要开始一些新的程序开发,前期的知识基础是必不可少的。
在开始本文所说的程序开发之前,来先学习一下 IPV4 、IPV6 、 HTTP 等相关的知识。
IPV4
IPV4 又称 互联网通信协议第四版(Internet Protocol version 4)是网际协议开发过程中的第四个修订版本,也是此协议第一个被广泛部署和使用的版本。其后继版本即为IPv6,也称为互联网通信协议第六版。
IPv4是一种通信协议,操作在使用分组交换的链路层(如以太网)上。此协议会尽量交付数据包,也就是说它不保证任何数据包均能送达目的地,也不保证所有数据包均按照能正确的顺序无重复地到达。
这个过程就像是送快递一样,每个快递包裹(数据包),快递小哥都是尽量派送,不过他送快递的时候不一定是按照顺序来送,而是离哪个客户近就先送谁。而快递在运输的途中难免会发生一些意外,导致包裹遗失(数据包丢失)。IPV4在这个过程中就扮演了快递小哥的角色。
IPv4使用32位地址,因此地址空间中只有4,294,967,296(即2的32次方)个地址。但在2011年2月3日,在最后5个地址块被分配给5个区域互联网注册管理机构之后,IPV4的主要地址池已经用尽。
所有的IPV4地址分别如下:
私有IP地址段:
A类:10.0.0.0到10.255.255.255 1658万个
B类:172.16.0.0到172.31.255.255 104万个
C类:192.168.0.0到192.168.255.255 6.5万个
公网IP地址段:
A类:1.0.0.0----9.255.255.255 1.5亿个
11.0.0.0-----126.255.255.255 19.23亿个
B类:
128.0.0.0-----172.15.255.255 7.3亿个
172.32.0.0-----191.255.255.255 3.3亿个
C类:
192.0.0.0-------192.167.255.255 0.11亿个
192.169.0.0-----223.255.255.255 5.03亿个
合计约36.47亿个可用公网IP地址、理论上是255*255*255*255约42.28亿个,除去私有网段、网络ID、广播ID、保留网段、本地环回127.0.0.0网段、组播224.0.0.0网段、实际可用就是36.47亿个。
看起来IPV4的地址很多,但其实IPV4的地址空间已经枯竭。从上世纪八十年代开始,人们就开始发现,IPV4 的消耗速度已经超出当初设计的预期。
为了应对地址快速消耗的负面影响,出现了一些新的技术,比如说国内一般家庭网络连接的就是内网,即采用了 网络地址转换(NAT)技术。抵消了一部分负面影响。
但是,这些技术只能在维持IPV4的可用性,对下一代的网络环境的帮助并不大。为此,便催生出了 IPV6 ,即上文提到的互联网通信协议第六版。
IPV6
为了应对IPV4地址消耗过快的问题,从1990年开始,互联网工程工作小组就开始规划IPv4的下一代协议,除要解决即将遇到的IP地址短缺问题外,还要发展更多的扩展。
1994年,正式提出IPv6发展计划,该提议直到同年的11月17日才被认可,并于1996年8月10日提出了草案标准,最终IPv6在1998年12月由互联网工程工作小组以互联网标准规范的方式正式公布。
IPv6与IPv4相比,IPv6能够降低复杂性和成本,然而当前却只有制造商较能够感受到这个优势,用户和运营商无法直接感受到,导致产业链缺乏推动IPv6的动力。因此,现在大多数网络用户还是在使用IPV4作为自己的网络通信协议。
IPv6具有比IPv4大得多的编码地址空间。这是因为IPv6采用128位的地址,而IPv4使用的是32位。因此新增的地址空间支持2的128次方(约3.4×10的38次方)个地址,具体数量为340,282,366,920,938,463,463,374,607,431,768,211,456 个。
目前,在我国,从2017年11月26日开始,中共中央办公厅、国务院办公厅印发《推进互联网协议第六版(IPv6)规模部署行动计划》,要求各地各部门贯彻落实。其中主要目标包括:到2018年末,IPv6活跃用户数达2亿,互联网用户中占比不低于20%;到2020年末,IPv6活跃用户数超过5亿,互联网用户中占比超过50%,新增网络地址不再使用私有IPv4地址;到2025年末,中国IPv6网络规模、用户规模、流量规模位居世界第一,网络、应用、终端全面支持IPv6。
IPV4与IPV6,供给了互联网中各个用户进行通信的所有地址,有了IP地址,就像知道了对方的门牌号一样。不过有了门牌号以后该怎么去拜访也是一个问题。是敲门还是钻窗户?
这就需要HTTP 登场了。
HTTP
HTTP即超文本传输协议(HyperText Transfer Protocol)是一种应用层协议。HTTP是万维网的数据通信的基础。
设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。
HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。HTTP的标准制定由万维网协会和互联网工程任务组(IETF)进行协调,最终定义了HTTP协议中现今广泛使用的一个版本——HTTP 1.1。
HTTP是一个客户端和服务端之间请求和应答的标准,通常使用TCP协议。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。
HTTP/1.1协议中共定义了八种方法来以不同方式操作指定的资源:
GET
向指定的资源发出“显示”请求。使用GET方法一般用来读取数据。
HEAD
与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”。
POST
向指定资源提交数据,请求服务器进行处理(例如上传文件)。
PUT
向指定资源位置上传其最新内容。
DELETE
请求服务器删除Request-URI所标识的资源。
TRACE
回显服务器收到的请求,主要用于测试或诊断。
OPTIONS
这个方法可使服务器传回该资源所支持的所有HTTP请求方法。
CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器就会Return状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,就会Return状态码501(Not Implemented)。
了解了这些基础知识以后我们就可以进入实践阶段了。下面是一个服务器,它实现了访问日期时间,代码如下:
from socket import socket, SOCK_STREAM, AF_INET
from datetime import datetime
# 1.创建套接字对象并指定使用哪种传输服务
# family=AF_INET - IPv4地址
# family=AF_INET6 - IPv6地址
# type=SOCK_STREAM - TCP套接字
# type=SOCK_DGRAM - UDP套接字
# type=SOCK_RAW - 原始套接字
server = socket(family=AF_INET, type=SOCK_STREAM)
# 2.绑定IP地址和端口(端口用于区分不同的服务)
# 同一时间在同一个端口上只能绑定一个服务否则报错
server.bind(('192.168.1.2', 1080))
# 3.开启监听 - 监听客户端连接到服务器
# 参数512可以理解为连接队列的大小
server.listen(512)
print('服务器启动开始监听...')
while True:
# 4.通过循环接收客户端的连接并作出相应的处理(提供服务)
# accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行
# accept方法返回一个元组其中的第一个元素是客户端对象
# 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成)
client, addr = server.accept()
print(str(addr) + '连接到了服务器.')
# 5.发送数据
client.send(str(datetime.now()).encode('utf-8'))
# 6.断开连接
client.close()
if __name__ == '__main__':
main()
下面是一个客户端的实现代码:
from socket import socket
def main():
# 1.创建套接字对象默认使用IPv4和TCP协议
client = socket()
# 2.连接到服务器(需要指定IP地址和端口)
client.connect(('192.168.1.2', 1080))
# 3.从服务器接收数据
print(client.recv(1024).decode('utf-8'))
client.close()
if __name__ == '__main__':
main()
这便是这篇文章的全部内容了
如果对文章中的内容有什么困惑的地方,可以在评论区提出自己的问题,学记同大家一起交流,解决各种问题,一起进步。
青年学记 陪伴着各位青年
作者:青年学记 一名不断进步的程序猿
一起学习 一起进步
走向自立