TCP小结

socket套接字
TCP
1.最简易的版本的客户端与服务端之间通信
2.通信循环
recv() 阻塞
3.连接循环
accept() 阻塞
4.TCP粘包问题
5.struct模块 对数据进行打包处理 固定长度
pack
unpack
解决粘包问题的流程

服务端
1.生成一个字典
2.制作该字典的报头
json序列化
编码 统计长度
3.发送字典的报头
4.发送字典
5.最后发真实数据

客户端
1.先接受固定长度的4个字节字典报头
2.解析获取字典数据的长度
unpack(...)[0]
3.接受字典数据
解码 反序列化
4.接受真实数据
为什么要多加一个字典
1.打包的数据大小有限 2.可以携带更多的信息
上传电影练习

import socket
import json
import struct
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr = server.accept()
while True:
try:
header_len = conn.recv(4)
# 解析字典报头
header_len = struct.unpack('i',header_len)[0]
# 再接收字典数据
header_dic = conn.recv(header_len)
# 反序列化 得到真实数据
real_dic = json.loads(header_dic.decode('utf-8'))
# 获取数据长度
total_size = real_dic.get('file_size')
# 循环接收并写入文件
recv_size = 0
with open(real_dic.get('file_name'),'wb') as f:
while recv_size < total_size:
data = conn.recv(1024)
f.write(data)
recv_size += len(data)
print('上传成功')
except ConnectionResetError as e:
print(e)
break
conn.close()

import socket
import json
import os
import struct
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
# 获取电影列表 循环展示
MOVIE_DIR = r'D:\python脱产10期视频\day25\视频'
movie_list = os.listdir(MOVIE_DIR)
# print(movie_list)
for i,movie in enumerate(movie_list,1):
print(i,movie)
# 用户选择
choice = input('please choice movie to upload>>>:')
# 判断是否是数字
if choice.isdigit():
# 将字符串数字转为int
choice = int(choice) - 1
# 判断用户选择在不在列表范围内
if choice in range(0,len(movie_list)):
# 获取到用户想上传的文件路径
path = movie_list[choice]
# 拼接文件的绝对路径
file_path = os.path.join(MOVIE_DIR,path)
# 获取文件大小
file_size = os.path.getsize(file_path)
# 定义一个字典
res_d = {
'file_name':'性感荷官在线发牌.mp4',
'file_size':file_size,
'msg':'注意身体,多喝营养快线'
}
# 序列化字典
json_d = json.dumps(res_d)
json_bytes = json_d.encode('utf-8')
# 1.先制作字典格式的报头
header = struct.pack('i',len(json_bytes))
# 2.发送字典的报头
client.send(header)
# 3.再发字典
client.send(json_bytes)
# 4.再发文件数据(打开文件循环发送)
with open(file_path,'rb') as f:
for line in f:
client.send(line)
else:
print('not in range')
else:
print('must be a number')
异常
程序在运行过程中出现了不可预知的错误 并且该错误没有对应的处理机制,那么就会以异常的形式表现出来 造成的影响就是整个程序无法再正常运
异常的结构
1.异常的类型:NAMEERROR
2.异常的信息:name 'fdsdfsdf' is not defined
3.异常的位置:Traceback (most recent call last):
File "D:/python/day29/01 异常处理.py", line 1, in <module>
异常的种类
分为两大类
1.语法错误
是你程序立刻就能解决的,这种错误是不能被容忍的
语法上的错误 发现之后应该立刻解决
2.逻辑错误
这种错是可以被容忍的 因为一眼看不出来
针对逻辑上的错误 可以采用异常处理机制进行捕获
常见的错误类型
NAMERROR 名字错误 SyntaxError 语法错误 KeyError 键不存在 ValueError 值错误 IndexError 索引错误 TypeError 类型错误
如何避免
异常处理 在你认为可能会出现bug的代码块上方try一下:注意try内部的代码块越少越好
try: 可能出错的代码except 出错的类型 as e: # 将报错信息赋值给变量e 出错之后的处理机制
UDP
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考模型中一种无连接的传输层协议,提供简单不可靠信息传送服务
是一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成。且不对传送数据包进行可靠性保证,适合于一次传输少量数据

UDP使用

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
# 不需要建立双向连接 直接收发数据
server_addres = ('127.0.0.1',8080)
# 发送数据需要对方的大致
while True:
client.sendto(b'strawberry',server_addres)
data , addr = client.recvfrom(1024)
print('服务端发的数据',data)
print('服务端发的地址',addr)

import socket
# UDP中要指定type=socket.SOCK_DGRAM,默认代表的是TCP
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
# UDP不需要设置半连接池,它也没有半连接池的概念
# 因为没有双向通道, 不需要accept来建立双向连接,也就不需要循环连接,直接通信循环
while True:
data, addr = server.recvfrom(1024)
print('数据', data)
# 客户端的数据
print('地址',addr)
#客户端的地址
server.sendto(data.upper(),addr)
#发送数据需要客户端的地址
TCP与UDP之间的区别
tcp 和udp
tcp:可靠,传输安全,粘包
通过连接传输:在发送数据时,会等到对方确定接收完成时,将数据删除,如果没有,就会保存到内存,等待确认
udp:不可靠,不须建立连接,不粘包,
发送数据,一旦发送,就会删除缓存数据,如果没收到,那就没收到
与TCP的区别 *****
不可靠传输
不需要建立连接
不会粘包
单次数据包不能太大

服务器端
服务器不需要监听 listen
不需要接收请求 accept
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)
客户端:
不需要建立连接
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)

import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
# while True:
data, addr = server.recvfrom(1024)
print(data)
data, addr1 = server.recvfrom(1024)
print(data)
data, addr2 = server.recvfrom(1024)
print(data)

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
# while True:
# msg = input('>>>:')
client.sendto(b'hello',server_address)
client.sendto(b'hello',server_address)
client.sendto(b'hello',server_address)
# data, server_addr = client.recvfrom(1024)
# print(data
UDP实现简易版本的QQ
实现多个用户同时发信息(实现并发)
并发:看起来像同时运行的并行:真正意义上的同时运行

import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
while True:
data, addr = server.recvfrom(1024)
print(data.decode('utf-8'))
msg = input('>>>:')
server.sendto(msg.encode('utf-8'),addr)

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
msg = input('>>>:')
msg = '来自客户端1的消息:%s'%msg
client.sendto(msg.encode('utf-8'),server_address)
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
msg = input('>>>:')
msg = '来自客户端2的消息:%s'%msg
client.sendto(msg.encode('utf-8'),server_address)
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
msg = input('>>>:')
msg = '来自客户端3的消息:%s'%msg
client.sendto(msg.encode('utf-8'),server_address)
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
socketserver模块
socketserver模块是基于socket而来的模块,它是在socket的基础上进行了一层封装,并且实现并发等功能。
ThreadingTCPServer的使用方法
1、创建一个继承socketserver.BaseRequestHandler的类 2、类中必须重写一个名为handler的方法 3、实例化一个服务器类,传入服务器的地址和请求处理程序类 4、调用serve_forever()事件循环监听
代码

#!/usr/bin/env python3
import socketserver
class Handler(socketserver.BaseRequestHandler): # 必须继承BaseRequestHandler
def handle(self): # 必须有handle方法
print('New connection:',self.client_address)
while True:
data = self.request.recv(1024)
if not data:break
print('Client data:',data.decode())
self.request.send(data)
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),Handler) # 实例化对象,实现多线程的socket
server.serve_forever() # 事件监听,并调用handler方法
让tcp也能够实现udp能够看起来多个客户在运行

import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
# print('来啦 老弟')
while True:
data = self.request.recv(1024)
print(self.client_address) # 客户端地址
print(data.decode('utf-8'))
self.request.send(data.upper())
if __name__ == '__main__':
"""只要有客户端连接 会自动交给自定义类中的handle方法去处理"""
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象
server.serve_forever() # 启动该服务对象
server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象
ThreadingTCPServer多线程tcpserver 会帮你实时监听代码中的地址,一旦有用户来进行请求,它会将这个用户交给MyServer类中的handl方法来处理,会自动触发方法.

import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
client.send(b'hello')
data = client.recv(1024)
print(data.decode('utf-8'))
开udp 支持并发

import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
# print('来啦 老弟')
while True:
data,sock = self.request
print(self.client_address) # 客户端地址
print(data.decode('utf-8'))
sock.sendto(data.upper(),self.client_address)
if __name__ == '__main__':
"""只要有客户端连接 会自动交给自定义类中的handle方法去处理"""
server = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象
server.serve_forever() # 启动该服务对象

import socket
import time
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080)
while True:
client.sendto(b'hello',server_address)
data,addr = client.recvfrom(1024)
print(data.decode('utf-8'),addr)
time.sleep(1)
