python学习Day39

Day 39

今日内容概要

  • OSI七层协议2(传输层、应用层)
    • 传输层中的TCP与UDP协议
  • socket套接字模块编程
    • 客户端与服务端交互的基础代码
    • 客户端与服务端交互的优化完善版代码
  • 半连接池
  • TCP黏包问题及解决思路

今日内容详细

1.OSI七层协议2(传输层、应用层)
4.传输层:
  1)RORT端口协议
      端口层面
  2)TCP协议与UDP协议
      规定了数据传输层面所遵循的规则
    #数据传输可以遵循的协议有很多TCP和UDP只是较为常见的两个
    
    1.TCP协议:
    	规定了两个特征:
			1)三次握手
            	建立双向通道
               #当服务端同一时间收到了无数个请求会崩掉,这就是洪水攻击也是黑客常用手段
			2)四次挥手
            	断开双向通道
               #和三次握手步骤类似,区别在于中间两步不能合并,这里有一个检查时间。
     """
     提问:基于TCP传输数据非常安全是因为有双向通道
       答:不对, 基于TCP传输数据时数据不容易丢失。不容易丢失的原因是因为有二次确认机制(每次发送数据都需要返回确认消息,否则在一定时间内会反复发送,过了该时间就不发了)
     """
    2.UDP协议:
    	基于UDP协议发送数据,没有任何通道等限制
         #UDP发送数据没有TCP安全(因为没有二次确认机制)

    
   #TCP协议与UDP协议的区别:
	TCP类似于打电话:你一句我一句
	UDP类似于漂流瓶:只要发送了,不管有没有看到也不管有没有回复

image

5.应用层
	主要取决于程序员自己采用什么策略和协议
  #常见协议有:HTTP、HTTPS、FTP..
2.socket套接字模块编程

image

#没有socket就相当于电脑没有操作系统
1.基于文件类型的套接字家族(基于本地计算机)
	套接字家族的名字:AF_UNIX
2.基于网络类型的套接字家族(基于网络传输)
	套接字家族的名字:AF_INET
#注意:
1.运行程序时要'先确保服务端在运行',然后才是客户端
2.客户端与服务端必须一个接收(recv)数据 一个send(发送)数据
socket实现本地客户端与服务端交互的基础代码
服务端:
import socket

#1.创建一个socket对象
server=socket.socket()#括号内什么也不用谢 默认就是基于网络的TCP套接字
#2.绑定一个固定的地址(ip和端口号)
server.bind(('127.0.0.1',8080))#127.0.0.1是本地回环地址(只允许自己机器访问)
#3.半连接池(主要用作缓冲,类似饭店外等待的座位)
server.listen(5)
#4.等待接受服务
sock,addr=server.accept()
print(sock,addr)#sock是双向通道 addr是客户端ip地址和端口号
#5.数据交互
sock.send(b'hello')#朝客户端发送数据
data=sock.recv(1024)#接收客户端发送的1024bytes
print(data)
#6.断开连接
sock.close()#断开连接
server.close()#关机
——————————————————————————————————————————————
客户端:
import socket

#1.创建一个socket对象
client=socket.socket()
#2.连接服务端(用服务端的ip和端口号)
client.connect(('127.0.0.1',8080))
#3.数据交互
data=client.recv(1024)#接收服务端发送的1024bytes
print(data)
client.send(b'hi')#朝服务端发送数据
#6.断开连接
client.close()#断开连接
socket实现本地客户端与服务端交互的优化完善版代码
1.send(发送)与recv(接收):
	客户端与服务器必须一个接收数据一个发送数据
2.消息自定义:
	input获取要发送的数据,然后要编码和解码
	encode('utf8')编码    decode('utf8')解码
3.循环通信:
	数据交互的地方添加while循环即可
4.服务端能持续提供服务:
	客户断开连接不报错(异常捕获,一但客户端断开连接,服务端结束通信循环并调到连接处等待下一个客户)
5.消息不能为空:
	需在'客户端'发送消息处加判断是否为空,如果是则重新输入。因为服务端不可能是程序员在和客户一句一句交互所以不用在服务端处加
6.服务端频繁重启可能会报错端口被占用(mac最为常见):
 #解决方式:
	from socket import SOL_SOCKET,SO_REUSEADDR#把这条加在最顶头
	server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#把这条代码在bind上一行加上即可
7.客户端异常退出会给服务端发送一条空消息(mac linux最常见):
	需在'服务端'接收消息处加判断是否为空
服务端:
import socket
from socket import SOL_SOCKET,SO_REUSEADDR

#1.创建一个socket对象
server=socket.socket()#括号内什么也不用谢 默认就是基于网络的TCP套接字
#解决苹果电脑服务端重启异常
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#2.绑定一个固定的地址(ip和端口号)
server.bind(('127.0.0.1',8080))#127.0.0.1是本地回环地址(只允许自己机器访问)
#3.半连接池(主要用作缓冲,类似饭店外等待的座位)
server.listen(2)
while True:
    # 4.等待接受服务
    sock,addr=server.accept()
    print(sock,addr)#sock是双向通道 addr是客户端ip地址和端口号
    while True:
        try:#添加异常捕获
            #5.数据交互
            data=sock.recv(1024)#接收客户端发送的1024bytes消息
            print(data.decode('utf8'))#把接收到的消息解码并打印
            if len(data)==0:break
            msg=input('请输入发送给客户端的消息:').strip()#获取要发送给客户端的消息
            sock.send(msg.encode('utf8'))#把要发送的消息编码并发送
        except ConnectionResetError:#当遇到该报错原因时执行以下代码
            sock.close()#关闭双向通道
            break#结束当前循环退到外层循环处

_______________________________________________
客户端:
import socket

#1.创建一个socket对象
client=socket.socket()
#2.连接服务端(用服务端的ip和端口号)
client.connect(('127.0.0.1',8080))
while True:
    #3.数据交互
    msg=input('请输入发送给服务端的消息:').strip()#获取要发送给客户端的消息
    if len(msg)==0:continue
    client.send(msg.encode('utf8'))#把要发送的消息编码并发送
    data=client.recv(1024)#接收服务端发送的1024bytes消息
    print(data.decode('utf8'))#把接收到的消息解码并打印


3.半连接池
server.listen(5)
	主要是为了做缓冲 避免太多无效等待
    6个(接待1个 等待5个)客户端可以同时发送消息,但是只有1对1断掉后服务端才能收到客户端2的消息
    #类似于饭店外放的等待座位,允许等待5桌,多余的报错不允许访问。
4.TCP黏包问题及解决思路

image

struct模块举例:
import struct

info='张三说吃吃吃吃吃吃'
print(len(info))#9 数据原本的长度
res=struct.pack('i',len(info))#把数据原本长度打包
print(len(res))#4 打包后的长度

ret=struct.unpack('i',res)#把打包好的数据长度拆包
print(res[0])#9(因为结果式元组,所以要索引0)
黏包问题主要是怎么产生的?
	1.TCP特性原因
    	流式协议:所有的数据类似于水流连接在一起
                 当数据量很小且间隔时间很断就会自动连接一起
    2.recv原因
    	不知道即将要接收的数据量多大,如果知道的话就不会产生黏包
解决黏包问题方法:
	用struct模块不论数据长度是多少都可以打包成固定长度。且还可以基于该长度反向解析出真实长度
    
解决思路:
	服务端:
		1.先将真实数据长度制作成固定长度  4
		2.先发固定长度的报头
		3.再发真实数据
	客户端:
		1.先接收固定长度的报头  4
		2.再根据报头解压出真实长度
		3.根据真实长度接收即可

    #struct模块针对数据量很大的数据没办法打包!!!!
    #但是!我们可以使用字典的方式!

解决黏包问题的终极方法:
	服务端:
		1.先构造一个数据的详细字典(包括该数据的真实长度)
		2.对字典数据进行打包处理,得到一个固定长度的数据 (4)
		3.将上述打包之后的数据发送给客户端
		4.将字典数据发送给客户端(字典中存储了该数据真实长度)
		5.将真实数据发送给客户端
        
	客户端:
		1.先接收固定长度的数据(4)
		2.根据固定长度解析出即将要接收的字典真实长度
		3.接收字典数据
		4.根据字典数据获取真实数据的长度
		5.接收真实数据长度

作业

1.编写一个cs架构的软件
	就两个功能
  	一个视频下载:从服务端下载视频
    一个视频上传:从客户端上传视频
 	"""
 	只要实现就可以 无需整合(如果能做到更好)
 	"""
2.整理今日内容及博客
3.周末练习选课系统
4.预习并发编程
posted @ 2022-08-06 00:16  逢诱  阅读(37)  评论(0)    收藏  举报