python语法基础-网络编程-TCP协议和UDP协议

###############    网络编程    ##############

"""
网络编程

学习了Python基础之后,包括函数,面向对象等,你就可以开发了,你不会就去找这个模块怎么用,就行了,

现在两个电脑上的程序要通信,才产生了网络,比如qq聊天

###############################
cs架构和bs架构
程序架构有cs架构和bs架构,本质都是两个程序之间的通信,
cs架构是c是客户端,s是服务端,服务端要一直运行,等待服务,客户端是我需要用的时候采取请求服务,
bs结构是b是浏览器,s是服务端,网页都是这种架构,
为什么火了,因为电脑不需要安装这么多的程序了,手机上还是客户端为主,但是未来的趋势还是bs架构,比如微信的小程序和公众号,
bs架构本质还是在统一入口,更方便客户的操作,


####################################
计算机网络

两台电脑通信:
硬件:电脑之间有网线,网线是插到网卡上,网卡提供了这个接口,
网卡上出厂的时候会有全球唯一的mac地址,这个非常长,不容易记忆,所以给mac地址编码,叫做ip地址,
ip地址是4个点分十进制组成的,每一个是0-255,为什么是255?因为是4个8位2进制数组成的,
通过ip地址就能找到mac地址,这是遵循了arp协议,所以本质还是通过mac地址来通信的,

多台电脑通信:
如果是多台电脑通信,需要一个交换机,多个电脑都插到交换机上面去,交换机的出现就是解决了多台电脑通信的问题,
电脑1要找到电脑4,电脑1会通过交换机,交换机会通知所有的电脑,这叫做广播,
只有电脑1会回复,根据arp协议会回复mac地址和ip地址,这叫做单播,

庞大的电脑通信:
由于交换机是广播的,如果有5千万电脑,就爆炸了,
所以通过把一部分电脑连接到交换机,成为一个局域网,这样就会有多个局域网,
局域网之间通过交换机连接路由器然后再连接另一个局域网的交换机来连接通信,这个交换机和路由器连接的接口叫做网关,
如果两个电脑同一个网关就是同一个局域网,局域网要向外访问就要通过网关

ip地址和子网掩码:
ip地址和子网掩码按位与运算,得到的就是网段,同样的网段是前三位是一样的,子网掩码的作用就是为了能得到网段,
所以知道两个电脑的ip和子网掩码,通过与运算,就可以判断两个电脑是否在同一个网络了,

总结:
ip协议的作用有两个:
1,为每一台计算机分配ip地址,
2,另一个是确定哪些地址在同一个子网络,
127.0.0.1,这是本地的地址,访问这个就不需要通过网线了,

端口:
通过ip找到了mac地址,然后找到了电脑,怎么找到程序呢?就要通过端口
在电脑上,每一个需要联网的程序,都需要一个端口,在同一时间,只有有一个程序占用这个端口,
端口的范围:0-65535,一般我们使用8000之后的端口,前面的可能被使用了
这样通过ip+端口,就可以在网络中,找到唯一的一个程序,
端口分为两类,知名端口和动态端口,
知名端口,大家都知道的,小于1024的不能随便用,大于1024的随便用,
端口从0-65535,动态端口就是从1024-65535

"""

###########################################

"""
TCP协议和UDP协议,
上面建立了两个程序的连接,那要如何通信,这就是用到了TCP协议和UDP协议

客户端和服务端之间通信:
单工,百分之百只能忘一个方向,广播,
半双工,可以收,也可以发,但是同一时间只能收或者发,对讲机,
全双工,可以同时收发,比如打电话,这个socket就是全双工的,只要是tcp协议,就一定是全双工的,

"""

################################################

"""
TCP协议的三次握手,四次挥手

3次握手:
这个通信的过程有一个3次握手的过程,
1次握手:客户端往服务端发信息,问我能发信息吗?同时携带一个标志,seq=x
2次握手:服务端往客户端发信息,说能,然后询问我能往客户端发信息吗?这是两个合成一个了,同时会发送两个标志,seq=y,ack=x+1
3次握手:客户端往服务端发信息,说能,同时会发送两个标志,ack=y+1

建立通信:
这样就建立了通信了,建立了连接就不会断了,就可以进行来回通信了,
后面就可以传输数据了,客户端传输数据,然后服务端告诉客户端返回数据,这就是tcp的应答机制了,

4次挥手:
结束的时候,可以是服务端发起,也可以是客户端发起,
假设是客户端发起挥手,
1次挥手:客户端往服务端发信息,说我不想发送信息了
2次挥手:服务端往客户端发信息,说好的
3次挥手:服务端往客户端发信息,说我也不想发送信息了
4次挥手:客户端往服务端发信息,说好的
这样四次挥手就结束了通信了,

三次挥手和四次挥手之间就确保了数据可以稳定传输,

为什么会有四次挥手?
在建立连接的时候,Server把响应客户端的请求和请求客户端的确认放在一起发送给客户端了,即第二次握手时有SYN+ACK
而断开连接的时候,一个方向的断开,只是说明该方向数据已传输完毕,而另一个方向或许还有数据,
所以得等到另一个方向数据也全部传输完成后,才能执行第三次挥手
因为socket是全双工的,有收发两个通道,所以这两个都要关闭才可以,所以会有四次来回,


长连接和短连接:
http/1.0是短连接,http/1.1就是长连接,
什么是长连接和短连接?
如果为了获取一个数据每次都要进行握手和挥手,就是短连接,
在握手之后进行多次数据传输,不断开,传输之后再断开,这就是长连接,
http协议是浏览器和服务器之间进行通讯的一种规定,

"""

##############################################

"""
tcp协议和udp协议比较:
1,udp 直接发送信息,不需要建立连接,tcp需要建立连接
2,所以UDP快,但是不安全,tcptcp比较安全,有反馈,这是牺牲效率完成的
3,而且udp发送之后有没有收到,是没有返回信息的,所以不知道有没有收到,
tcp比较安全,更加稳定,tcp也可能丢包,但是会重发,比如打电话,打电话的时候会嘟嘟嘟的响,这是在确定通道,通了之后这个通道会占用,别人就不能打了,
所以tcp比较复杂, 但是比较安全,稳定,


udp就像是发短信,
tcp就像是打电话,
udp想象成写信,每一封信都要写收件人的地址,这种不太安全,这封信可能丢,

tcp采用了应答机制,
比如udp,
a给b发了一个信息,b有没有收到,这是不会给a回复的,
但是tcp,
a发送了一个消息给b,b会告诉a我收到了,这是有回复的,a发送了之后,会开始计时,如果没有收到,会超时重传,
所以tcp的机制,就比较安全,会确保数据已经传递过去了,

udp发送,客户端发送了之后,服务器是没有反馈数据的,也就是你有没有真的发过去,是不知道的,
tcp,一大特点就是操作系统的会互相打招呼,是需要返回数据的,告诉你是不是你发的东西我真的收到了,
-----------------------------------
使用迅雷下载的时候,会有下载也会有上传,这种上传就是一种应答,告诉对方我接收到了,
此外,tcp还有错误校验,流量控制,阻塞管理,这些机制,这些不用记,总之这些机制,保证了tcp比较安全稳定,
现在udp基本不用,基本都是用tcp,因为udp不稳定,不安全,
现在访问百度,使用微信,都是tcp协议,
比如买票,如果udp就完了,扣了钱,但是对方没收到,就完了,

"""

##############################################

"""
互联网协议
a) 四层协议:应用层、传输层、网络层、网络接口层 b) 五层协议: 应用层:用户使用的应用程序都归属于应用层,作用为规定应用程序的数据格式。 传输层:网络层帮我们找到主机,但是区分应用层的应用就是靠端口,所以传输层就是建立端口到端口的通信。(端口范围0-65535,0-1023为系统占用端口) 网络层:区分不同的广播域或者子网(否则发送一条数据全世界都会受到,是灾难)。 数据链路层:定义电信号的分组方式。 物理层:基于电器特性发送高低点电压(电信号),高电压对应数字1,低电压对应数字0。 c)七层协议:(应用层、表示层、会话层)、传输层、网络层、(数据链路层、物理层)
"""

###############    基于tcp的socket代码    ##############

 服务端:

"""
socket,
socket位于应用层和传输层之间,也就是说我们不直接和tcp协议和udp协议打交道,这些握手挥手都是socket承包了
socket是一组接口,这是一种门面模式,把复杂的tcp/ip协议隐藏到socket接口后面,
我们使用socket,socket就是一个模块而已,
socket就是套接字,

"""
# 基于tcp的socket代码
# 服务端
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)

客户端:

# client端
import socket
sk = socket.socket()           # 创建客户套接字
sk.connect(('127.0.0.1',8898))    # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024)         # 对话(发送/接收)
print(ret)
sk.close()            # 关闭客户套接字

 什么是socket?简述基于tcp协议的套接字通信流程。

"""

什么是socket?简述基于tcp协议的套接字通信流程。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

服务端:创建socket对象,绑定ip端口bind(),  设置最大链接数listen(),accept()与客户端的connect()创建双向管道, send(), recv(),close()
客户端:创建socket对象,connect()与服务端accept()创建双向管道 ,  send(), recv(),close()

"""

  

###############   基于udp的socket代码    ##############

 服务端:

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字

客户端:

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

###############    黏包    ##############

""""
服务端给多个客户端发布指令,客户端接收,
然后客户端把结果反馈回来,服务端接收,

黏包:
1,什么是黏包?
2,什么时候会出现黏包?
3,为什么会出现黏包?
4,什么时候需要处理黏包?
5,怎么处理黏包?

################################################
什么是黏包?
使用tcp会有黏包现象:
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,就是多次传输的数据,客户端接收的时候接收多了,或者接收少了,
同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。
注意:只有TCP有粘包现象,UDP永远不会粘包

#################################################
什么场景会出现黏包:
1,连续send两个小的数据包
2,两个recv,第一个recv特别小的,可能一次性发了一个大的包,但是你一次接收不完就会出问题,你下次再接收就会和下一次发送的内容黏包在一起,

###############################################
为什么出现粘包现象:
(1)发送方原因
tcp协议的拆包机制,当发送端发送的数据比较大,tcp会将这次发送的数据拆成几个数据包发送出去。

TCP发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),
将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。
因为每次发送之后都需要客户端确认,合并之后发送可以提高效率
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

(2)接收方原因
TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不一定会立即处理;
实际上,TCP将收到的分组保存至接收缓存里,然后应用程序主动从缓存里读收到的分组。
这样一来,如果TCP接收分组的速度大于应用程序读分组的速度,多个包就会被存至缓存,应用程序读时,就会读到多个首尾相接粘到一起的包。
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

所以本质是拆包和封包,以及应用层接收和读取是不同步的,是有一个缓存的,而且你不知道你要接收多大的问题,
主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
如果你发送的和接收的都是一样大小的,就可以解决这个问题了,


#############################################################
什么时候需要处理粘包现象
(1)如果发送方发送的多个分组本来就是同一个数据的不同部分,比如一个很大的文件被分成多个分组发送,这时,当然不需要处理粘包的现象;
(2)但如果多个分组本毫不相干,甚至是并列的关系,我们就一定要处理粘包问题了。比如,我当时要接收的每个分组都是一个有固定格式的商品信息,
如果不处理粘包问题,每个读进来的分组我只会处理最前边的那个商品,后边的就会被丢弃。这显然不是我要的结果。

################################################################
黏包的解决方案:
1,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
存在的问题:
程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

应用层处理
应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题。
解决方法就是循环处理:应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理;但是如何判断每条数据的长度呢?
两种途径:
1)格式化数据:每条数据有固定的格式(开始符、结束符),这种方法简单易行,
但选择开始符和结束符的时候一定要注意每条数据的内部一定不能出现开始符或结束符;
2)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,
应用层处理时可以根据长度来判断每条数据的开始和结束。

当时在做购物车的时候,我最开始的做法是设置开始符(0x7e)和结束符(0xe7),但在测试大量数据的时候,发现了数据异常。
正如我所猜测,在调试过程中发现某些数据内部包含了它们。因为要处理的数据是量非常庞大,为做到万无一失,最后我采用了发送长度的方式。
再也没有因为粘包而出过问题。

"""

  

###############    网络编程    ##############

 

原文地址:https://www.cnblogs.com/andy0816/p/12236567.html