一、在TCP协议中我们如何解决粘包的问题
(1)TCP协议为啥会产生粘包问题:(流式协议,补充:数据的可靠性--->必须得到回应才会在内存删除)
1.接收:我根本不知道接收的总长度。————》落包的现象
2.发送方:会把数据量小的并且时间间隔较短的数据一次性打包发送。---》粘包
(2)UDP协议:基于数据报协议
没有双向通道
1.UDP不存在粘包问题
2、客户端可以发空
3.UDP可以实现并发的效果(qq程序)
4.即使服务端不存在,也不影响客户端向服务端发数据
TCP------>打电话
UDP----->发短信
1.通过struck模块
具体看实例:
import struck import json 第一步:建立字典 my_dic = {"file_name": "澳门最大赌场线上赌场开业了!", "file_size": 435413543654364364546546544445} 第二:我们进行对字典的序列化 data_json = json.dumps(my_dic) 得到字符串,我们要对他进行z转换转成计算机能识别的语言:二进制类型 data_bytes = data_json.encode("utf-8") print(data_bytes) # 第三:开始对我们得到的信息打包-->打包成“i” 模式下的字节 data = struck.pick("i",len( data_bytes)) print(data) #4 第四解包: res =struck.unpick("i", data)[0] prit(res)
字符串:{"file_name": "u6fb3u95e8u6700u5927u8d4cu573au7ebfu4e0au8d4cu573au5f00u4e1au4e86uff01", "file_size": 435413543654364364546546544445} 二进制:b'{"file_name": "\u6fb3\u95e8\u6700\u5927\u8d4c\u573a\u7ebf\u4e0a\u8d4c\u573a\u5f00\u4e1a\u4e86\uff01", "file_size": 435413543654364364546546544445}' “i”格式的4个字节数:b'x92x00x00x00' 4 146
2.subprocess模块的运用:作用--->可以解决客户端与服务端循环交流
服务端:
import socket # 套接字模块 import subprocess # 三流 import json # 序列化 import struct # 解决粘包问题 """ 服务端必备三要素: 1.要有固定的ip和port 2.要能24小时进行服务 3.能承受高并发 并发:看上去像同时进行 """
# 第一步: server = socket.socket() server.bind(('127.0.0.1', 8080)) # 绑定通信地址 server.listen(5) # 半链接池 # 第二步 开始通信循环 while True: conn, addr = server.accept() # d等待服务 while True: try: cmd = conn.recv(1024).decode('utf-8') # 接收到的是二进制要进行解码 if len(cmd) == 0: break # 若接收到的信息为0时 直接退出 obj = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() # 1.制作一个固定长度的报头 header = struct.pack('i', len(stdout + stderr)) # 2.发送固定长度的报头 conn.send(header) # 3.发送真实数据的长度 # conn.send(stdout ) # conn.send(stderr ) # 由于tcp协议的特点上面和下面完全是等价的 conn.send(stderr + stdout) except ConnectionResetError: # 捕获异常 break # 直接退出 conn.close() # 关闭连接
客户端: import socket import struct # 第一步 client = socket.socket() client.connect(('127.0.0.1', 8080)) # 第二步 while True: cmd = input('>>>:').encode('utf-8') if len(cmd) == 0: continue client.send(cmd) # 解析报头获取的真实数据的长度 header = client.recv(4) total_size = struct.unpack('i', header)[0] # 循环接收信息 recv_size = 0 data = b'' while recv_size < total_size : res = client.recv(1024) data += res recv_size +=len(res) print(data.decode('gbk'))
3.tcp大文件上传示范列:
客户端: import socket import struck import json 第一步建立连接: socket.socket() client.connect(('127.0.0.1', 8080)) 第二步: 文件大小:file_size=os.path.getsize(r'文件路径=path=os.path.jion()') 设置文件名;file_name="性感美女在线发牌.mp4' 第三:设置一个字典 my_dic={"fiel_name":file_name,"file_size':file_size} #序列化字典--》转成二进制模式 data_bytes=json.dumps(my_dic).encode("utf-8) #制作报头: header= struck.pack('i', len(data_bytes)) #发送报头 client.sent(header) #发送字典编码后的二进 制3字符串 client.send(header_bytes) b'{"file_name": "\u6027\u611f\u7f8e\u5973\u5728\u7ebf\u53d1\u724c.mp4",
"file_size": 254031692, "msg": "\u6ce8\u610f\u8eab\u4f53\u54e6"}' #开始发送真实信息 with open("文件路径“,“rb”)as f: for line in : client.sent(line)
服务端: import json import os import socket import struck 第一步:建立连接 socket.socket() server.bind(("127.0.0.1", 8080)) server.lisent(5) #半链接池 第二步:开始接收信息 while True: conn, addr = srever.accept() #等待接收信息 while True: try: # 接收报头 header = server.recv(4) #接收到报头4个字节 #j解析报头,获取报头长度 henader_len = struck.unpack("i'', header)[0] #获取字典 header_bytes = conn.recv(header_len) #heaher_dic = json. loads(header_bytes.decode(utf-8)) #开始循环接收文件传到到本地 #file_name = header_dic.get("file_name') #file-size = header_dic.get(file_name') recv_size =0 with open(file_name,"wd")as f: while recv_size < file_size: data = conn.recv(1024) f.write(data) recv_size = recv_size+len(data) print(recv_size) #第一次接收的值 break print(header_dic.get("msg")) # 字典点.get是他的内置方法 except ConnectionResetError: break conn.close()
4.qq简易版:
QQ版udp:客户端 import socket 第一步:建立连接 server = socket.socket(stype = socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: add, addr=server.recvfrom(1024) print(msg,decode('utf-8')) data = inpout(">>>:").encode('utf_8') server.sendto(data.addr)
qq版udp服务端: import socket client = socket.socket(type= socket.SOCK_DGRAM) server_addr = ('127.0.0.1',8080) while True: msg = input(">>>:") msg = '客户端1的消息%s'% msg client.sendto(msg.encode('utf-8'),server_addr) data,addr= client.recvfrom(1024) print(data.decode('utf-8')) ------------------------------------------ import socket client = socket,socket(type=socket.SOCK_DGMAM) server_addr ('127.0.0.1'),8080) while True: msg= input(">>>:”) msg = "客户端2的消息%s''%msg client.sendto(msg.encode('utf_8'), server_addr) data ,addr = client.recvfrom(1024) print(data.decode("utf-8')) -------------------------------------------- import socket client = socket.socket(type= socket.SOCK_DGRAM) server_addr = ('127.0.0.1',8080) while True: msg = input(">>>:") msg = '客户端3的消息%s'% msg client.sendto(msg.encode('utf-8'),server_addr) data,addr= client.recvfrom(1024) print(data.decode('utf-8')) -----------------------------------------------------
5.socktedsrever模块
TCP:通过socketserver进行完成TCP
TCP:服务端:(实现并发) import socketserver class Mybaby(socketserver.BaseRequestHandler): def handle(self): # 循环同心 while True: # data = self.request.recv(1024) # 收信息 print(data) self.request.send(data.upper()) if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.01', 8080), Mybaby) server.serve_forever()
TCP:客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024) print(data)
2.UDP:通过soc
import socketserver import import socket.socket() calss Myud(socketserver.BaseRequestHandler): def handler(self): #j建立通信循环 while True: data,sock=self.request #受消息 self.request相当于你的conn通信对象 print(data) sock.sendto(data.upper(),self.client_addr) #self.client_addr相当于客户地址 if __name__ = "__name__" : server = socketserver.ThreadingUDPSserver(('127.0.0.1'),8080) server.server_forever()
客户端: import socket socket.socket(socket.SOCK_DGRAM) server_addr =('127.0.0.1',8080) while True: client.sendto(b'hello',server_addr) data ,addr = client.recvfrom(1024) print(data, addr) time.sleep(1)
socketserver实现并发
1.能够实现并发效果
并发:看起来像同时运行就能称为并发
2.UDP在使用的时后,多个客户端要有一些IO操作不然容易卡死
6.并发编程
操作系统的发展史:
1.多道技术:
空间上的复用(多个程序共用一套硬件设备,他是多道技术实现时间上的复用的基础 )
时间上的复用(单个CPU的电脑上,起了多个应用程序。cpu快速切换,给人感觉像是同时运行)
cpu哪两种情况下才会切换:(西庵堡村当前运行的状态)
1.一个任务占用cpu时间过长或被操作系统强行夺走cpu的权限(比串行效率反而降低)
2.一个任务遇到IO操作时,也会被系统强行夺走CPU的执行权限(比起串行效率更高)
补充:
并发 :看上去像同时进行的
并行:同时运行
单核计算永远不可能实现并行。