Oct. 26th, 2018
大家好,我是一个编程小白。虽然编程经验不多,但是个人比较喜欢探索尝试,喜欢做一些小实验来加深对新学东西的理解。最近由于工作原因要学习一些网络编程的知识,通过发送网络请求来使用一个服务器提供的Web服务。虽然在Python的各种神包(比如requests,urllib等等)的帮助下问题很快就解决了,但是自己还是好奇,requests这种包发送出去的网络请求是长什么样的?如何才能看到这些网络请求的信息?了解了它的请求协议之后,我如何自己写代码响应这种请求?于是自己拿Python的socket做了几个小实验。
其实我相信大多数程序员在刚接触网络编程的时候肯定都做过这种简单的实验。但是网上搜索相关资料的时候,还是感觉大神们写文章大都直接给出专业的技术内容,而以探索为主的文章相比比较少。所以这里本小白还是斗胆献丑,给出自己实验的过程。虽然对大多数来说简单地不值一提,但是如果能偶尔对刚入门的小伙伴提供一点帮助的话,我还是很高兴的。
注意:下面的实验用python代码编写。每一段代码都只有短短几行,很容易看懂。代码运行的截图里使用的是Mac的terminal。不过如果使用Windows或者Linux的小伙伴也可以在任何自己喜欢的运行环境里运行这些代码。
复习:分层的互联网协议
大家都知道,所谓互联网协议,或者说TCP/IP协议,并不是一个单一的协议,而是有很多层组合在一起形成的“协议套餐”。我们先来简单复习一下这个协议套餐。
互联网的4个协议层
我们使用的互联网协议大致分为四层。最下面是物理通信层,负责规定如何用电信号或者光信号(光纤)的形式加载和解码信息。往上走,信息的物理表达方式解决了,所谓网络层就解决网络传输和网络地址问题。一个信息,要发给谁,沿着什么途径发送过去,都是网络层规定的东西。我们熟悉的IP地址就是在这一层上工作的。大家可以把这一层协议理解成普通邮递业务中规定收件人发件人地址的协议。大家都认同以某种格式在信封上写明收件人和发件人的地址,邮递员也利用这个普遍认同的格式,去确定这封信到底应该怎么运送。(比如先送去收件人所在的省邮局,再由省邮局根据地市信息分发到下面的邮局,再由下属邮局的邮递员根据街道信息往下分发。这个信息下分投递的方式也就是常常说的“路由”。)
找到了要联系的对象,通信渠道也就畅通了。上面的传输层就规定具体的通信方式。双方在通信过程中是否要一直保持渠道畅通直到一方结束通信?通信前双方要如何进行握手(确认过眼神,你就是收件人?)还是说,要进行一种佛系的通信,不去建立所谓连接,接收方也不去反馈确认信息有没有收到(接收发送,一切随缘?)这有没有握手建立通信渠道,有没有接收确认信息的区别,就是TCP协议和UDP协议的区别。(在普通的邮递过程里,基本上对应平邮和挂号信的区别。)后者可信度较低,收发成本也低。但随着网络传输能力的增强,人们大多数情况下不再需要刻意控制手法成本,于是稳定可信度高的TCP协议就更受人青睐。
最后是应用层。这个最好理解。眼神也确认过了,手也握过了,但是大家具体要干什么(一脸严肃)?这要回到当初是基于什么目的来进行通信的。比如浏览器和网页服务器建立了通信,是为了获取网页的信息(文字、图片、链接等等),而邮件收发工具跟smtp服务器建立了通信,是为了发送电子邮件。请求者用什么格式的文字描述自己的请求内容,服务器又以什么格式进行反馈,这就是应用层的工作了。比如服务器看懂了对方的请求并作出了正确的响应,会发送一个200的代码,表示一切正常。当然我们平时上网时看不到这个代码,因为既然一切正常,浏览器只把得到的信息显示给用户就好了。不过一旦出现了问题,比如服务器找不到请求的资源,大家就会看到这个错误代码了,那就是噩梦般存在的404了。
注意:上面的图并不是出错了没有显示,而是本身的内容就是无法显示哦:)
作为本节结束,我们简单过一遍,当你在浏览器上点击一个链接或者在地址栏输入一个网址的时候会发生什么。首先,在这一瞬间,你其实产生了一个HTTP的请求。一般来说,这会是一个“GET请求”,就是说你想得到某个页面,或者某些网络资源。这个请求里还要指明一些其他内容(后面具体讨论)。请求信息准备好之后,浏览器要选择传输层上那种协议来发这个请求(一般会是TCP协议)。再往下,就是确定寄送方式(网络层),再往下就是决定如何把这些信息加载成电信号或者光信号进行发送了(物理层)。
Requests之类的包,允许用户在应用层上表达自己的请求,而不用管下层发生什么。而我们今天要看的是,这些请求在TCP层上长什么样。为了建立TCP层的通信,我们使用python的TCP工具:socket。
实验1: python socket的TCP通信
socket的使用很简单。两个程序通信,一个要作为主机,开放自己的某个接口允许别的程序连接。这个程序要监听这个接口,直到连接建立;另一个作为从机,要知道主机IP地址以及开放了哪个接口来通信,然后主动连接的这个接口。这样以来,连接就建立了。这个有点类似联机打游戏,要有一方建立游戏,就是开放一个接口等待其他玩家连接;另一方加入别人开的游戏。一旦连接建立起来,双方就可以愉快地玩游戏了。
我们先来看主机那边的代码。
# 主机:开放连接口,等待连接import socket # 引用socket包s_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建主机的连接口。括号里参数指明了连接的类型(基于网络的流连接)s_server.bind(('127.0.0.1', 1212)) # 指定连接的IP地址和端口。print("主机:等待连接...") s_server.listen(1) # 侦听这个端口,等待另一方来连接conn, addr = s_server.accept() # 连接建立,返回连接起来的socket和对方的IP地址。print("主机:接收到来自{}的连接请求。连接已创建!".format(str(addr)))
从机方面代码更简单,创建一个socket,然后去connect主机的socket就可以了。
import socket mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socketmysock.connect(('127.0.0.1', 1212)) # 连接到主机IP地址和端口。 # 这行代码会导致上面的listen函数停止等待并进入下一运行。 print("从机:已连接至主机!")
作者:爱科学的程序员小刘
链接:https://www.jianshu.com/p/b610d48c19ae
共同学习,写下你的评论
评论加载中...
作者其他优质文章