网络编程
前言
-
我们现在已经学会了写基本形式的python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好。但是如果这两个程序之间想要传递一个数据,你要怎么做呢?
-
网络编程:
-
为什么要学习网络编程?
软件开发的架构
C/S架构
B/S架构
-
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构。
-
Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源)即可。
网络基础
网络通讯的基本要素
网络协议
什么是网络协议?
网络协议就是标准,大家要遵循相同的标准才能正常交流。在计算机网络通讯过程中,发送和接收方约定用一种协议(数据格式)进行通讯,发送数据的一方严格按照一个协议(数据格式)发送数据,接收方也严格按照协议(数据格式)进行解析,这样才能进行一次完整的网络通讯。
在后期web项目中会涉及到微信支付功能,则现在用“微信支付”的功能来举例:我们在做的是“微信扫码支付”,其中微信就规定了一套支付信息提交协议供用户来提交支付账单,拿“生成微信订单”来举例,必须要以xml格式传递以下样式参数:
<xml>
<appid>公众账号ID</appid>
<mch_id>商户号</mch_id>
<detail>商品详情</detail>
<notify_url>通知地址</notify_url>
<out_trade_no>商户订单号</out_trade_no>
<total_fee>下单金额(以分为单位)</total_fee>
<sign>签名</sign>
...............
</xml>
由此,我们必须按照这样的规则要求去传递数据,这样微信才能返回正确的回执。回执如下:
<xml>
<return_code>通讯成功或失败</return_code>
<return_msg>返回信息</return_msg>
<result_code>业务处理结果成功或失败</result_code>
<sign>签名</sign>
<code_url>生成订单的支付二维码</code_url>
</xml>
注意:协议是由发送方和接受方共同制定的,考虑到计算机已经发展了这么多年,所以制定的协议过程很显然我们没有机会参与了,要做的是了解通讯协议中的各种规定即可。
TCP/IP协议簇
osi七层模型
-
osi就是相当于计算机界的通用语言,只要按照OSI规定的标准来通讯,就能够与全世界任何一台所有计算机通讯
-
osi七层模型
-
上图中最右边就是完整的七层模型,是最完整的通讯模型,虽然很详尽,但是整个通讯流程的复杂度较高,后期为了降低学习难度,将其进行了简化,于是有了中间的五层,和左边的四层。
OSI各层工作原理解析
应用层,表示层,会话层都属于应用程序层面所以重点讨论简化后的五层。
-
物理层
-
数据链路层
-
数据链路层的由来:
-
以太网协议:
-
mac地址
-
广播
-
有了mac地址,同一网络内的两台主机就可以通信了.
-
ethernet协议采用最原始的方式,广播的方式进行通信,即计算机通信基本靠吼
-
思考:如果同一网络中有100台电脑,大家都在同一时间都在互相通讯会有什么问题吗?
-
交换机:
-
那么如何使用交换机优化多台机器在同一网络中的相互通讯呢?
-
当pc1想要与pc2通讯前:
-
1.pc1需要知道pc2的MAC地址,所以必须先将这个信息广播给所有的计算机,
-
2.但是这个信息必须先交给交换机,再由交换机广播出去,
-
3.pc2收到消息消息后发现目标MAC是自己,就回复数据给发送方pc1,
-
4.而pc2回复的信息也必须先交给交换机,此时交换机就会记录pc2的MAC地址并将其存到自己的缓存中,
-
5.下一次在要给pc2发数据时从缓存中查找pc2的MAC地址,
-
6.如果找到了就直接单独给pc2发送,不在需要广播,
-
7.如果没有则重复之前的广播过程
-
也就是如果多台机器相互之间都进行过通讯后,则所有机器的mac地址都会存储到交换机的缓存中,则后期通讯就是相当于"点和点"的通讯方式了。这一优化功能称之为自动学习功能。
-
-
第一次链接某计算机时 必须广播获取MAC地址:
-
只要链接过一次 MAC地址就被交换机记录下了下一次就不用广播了:
-
所以:交换机的工作原理类似类似于早期的电话交换机,电话线打到总台,总台问你要找几号?,然后将电话线插到相应的口上。
-
-
-
网络层
-
有了ethernet、mac地址、广播的发送方式,世界上的计算机就可以彼此通信了。
-
ethernet的广播方式通讯方式存在的问题?
-
IP地址
-
子网掩码(了解)
- 单纯的ip地址段只是标识了ip地址的种类,无法辨识一个ip所处的子网 例:192.168.10.1与192.168.10.2并不能确定二者处于同一子网/局域网。
- 子网掩码是一个32位地址,用于说明不同的IP地址是在局域网上,还是在远程网上。
- 知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算,然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。
- 结论:子网掩码就是用来判断两个ip是否属于同一子网/局域网。
-
-
传输层
- 目前我们已知:可以通过物理层来建立计算机之间的链接通道,然后通过数据链路层的MAC地址,可以定位到某个局域网中的某台主机,最后通过网络层的IP地址,子网掩码,可以定位到全球范围某一局域网下的某台主机。
- 那么问题来了:
- 一台计算机上是不可能只能运行一个应用程序的,比如我们可以同时登陆qq和微信,那接收到的数据到底是交给微信还是qq呢?
- 这个时候就需要应用到所谓的"端口号"了。
- 端口号:
- 端口是需要联网的应用程序与网卡关联的编号或者简单理解成应用程序的唯一标识。
- 因此端口号就是需要联网的应用程序与网卡关联的编号。端口范围0-65535,0-1023为系统占用端口。
- 重点:ip地址精确到具体的一台电脑,而端口精确到具体的程序
- 传输层的作用:
- 建立端口到端口的通信。
- 其实我们发现,osi的每一层都会作用自己的一些协议,传输层也是一样的:
- TCP和UDP就是工作在传输层的协议(后面重点讲解)
-
应用层
-
应用层由来:
- 用户使用的都是应用程序(微信,支付宝等),均工作于应用层,互联网是开放的,大家都可以开发自己的应用程序,用什么样的数据格式来传输,就需要由应用程序开发者自己来制定
-
应用层作用:
-
规定应用程序之间交互数据的数据格式,这些数据交互的格式也就是作用在应用层之间的各种协议。
-
这些应用程序的协议就构成了"应用层"
- 常见协议:HTTP,FTP,SMTP等
-
-
-
TCP和UDP协议
什么是TCP协议
-
TCP 是面向有连接且可靠的通信传输协议,面向有连接是指在传送数据之前必须先建立连接,数据传送完成后要释放连接。并且可实现“顺序控制”和“重发控制”的机制。
-
无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议簇中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的且通过四次挥手关闭连接。
- 例如:打电话,需要双方都接通,才能进行对话
- 优劣:效率低,数据传输比较安全
什么是UDP协议
- UDP 是面向报文的通信传输协议,所谓面向报文,指它的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。UDP 是不具有可靠性的数据报协议。在 UDP 的情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此,应用有时会根据自己的需要进行重发处理。
- 例如:发短信,不需要双方建立连接。
- 优劣:效率高,数据传输不安全,容易丢包
三次握手
- TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
- 所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
下面来看看三次握手的流程图:
- 第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
- 第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,且设置ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
- 第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
四次挥手
-
四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
-
中断连接端可以是客户端,也可以是服务器端。
-
第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
-
第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
-
第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
-
第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ack则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL(大概4分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次挥手。
TCP和UDP的应用场景
- TCP应用场景
- 效率要求相对低,但对准确性要求相对高的场景,因为传输中需要对数据进行确认,重发,排序等操作,效率没有udp高
- 文件传输:FTP\HTTP对数据的准确性要求高,速度可以相对慢
- 发送或接收邮件:POP3\IMAP\SMTP对数据准确性要求高,非紧急应用
- 远程登陆:TELNET\SSH对数据准确性有一定要求,有连接概念
- UCP应用场景
- 即时通信:QQ聊天,对数据准确性和丢包要求比较低,但速度必须快
- 在线视频:RTSP速度一定要快,保证视频连续,但是偶尔花了一个图像帧,还是可以接受的
- 网络语音电话:VoIP数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题
Socket概念
- 通俗理解
- 人们经常把socket翻译为套接字,socket其实就是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
- 其实socket就是一个Python中的一个模块。我们通过调用模块中已经实现的方法建立两个进程/应用之间的连接和通信。
TCP协议下的Socket
注意:socket绑定IP和端口时可能出现下面的问题:
解决办法:
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #在bind前加,允许地址重用
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
但是如果你加上了上面的代码之后还是出现这个问题:OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。那么只能换端口了,因为你的电脑不支持端口重用。
尝试不间断的让客户端和服务端进行数据交互
你会发现,第一个连接的客户端可以和服务端收发消息,但是第二个连接的客户端发消息服务端是收不到的.
原因就是:tcp属于长连接,长连接就是一直占用着这个链接,这个连接的端口被占用了,第二个客户端过来连接的时候,他是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,除非一个客户端断开了(优雅的断开可以,如果是强制断开就会报错,因为服务端的程序还在第一个循环里面),然后就可以进行和服务端的通信了。什么是优雅的断开呢?看代码:
UDP协议下的Socket
基于UDP的socket通讯流程:
服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束。
- Socket是一种特殊的I/O,常用的Socket类型有两种:
- 流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
- SOCK_STREAM 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。
- SOCK_DGRAM 是无保障的面向消息的socket , 主要用于在网络上发广播信息。