d29天 上传电影练习 UDP使用 ScketServer模块

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'))
客户端1
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'))
客户端2
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'))
客户端3

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方法
View Code

让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)
客户端
原文地址:https://www.cnblogs.com/komorebi/p/11324065.html