udp协议 及相关 利用tcp上传文件 socketserver服务

一、利用tcp协议上传大文件


服务端:
import socket
import json
import struct
import os

server = socket.socket()
server.bind(('127.0.0.1', 8082))
server.listen(5)

while True:
    conn,addr = server.accept()
    while True:
        try:
            #先接受报头
            header = conn.recv(4)
            #解析报头,获取字典长度
            header_len = struct.unpack('i', header)[0]
            #接收字典
            header_bytes = conn.recv(header_len)
            #将字节格式字典装换为字符串
            header_dic = json.loads(header_bytes.decode('utf-8'))
            print(header_dic)
            #循环接收文件 存储到本地
            file_size = header_dic.get('file_size')  #文件的长度
            file_name = header_dic.get('file_name')  #文件的名称
            recv_size = 0 #当前接收到的文件长度
            #文件操作
            with open(file_name,'wb') as f:
                #循环接收数据
                while recv_size<file_size:
                    data = conn.recv(1024)
                    f.write(data)
                    recv_size += len(data)
                print(header_dic.get('msg'))
        except ConnectionResetError:
            break
    conn.close()


#存在问题,无法解决  struct.error: unpack requires a buffer of 4 bytes


客户端

import socket
import os
import json
import struct


client = socket.socket()
client.connect(('127.0.0.1', 8082))

#文件大小
file_size = os.path.getsize(r'D:学习python上课相关day32代码day321 上节课复习.mp4')
#文件重新命名
file_name = '上传文件.mp4'

#定义一个字典
dic = {
    'file_name':file_name,
    'file_size':file_size,
    'msg':'上传成功!'
}

#将字典序列化后编码为字节
data_bytes = json.dumps(dic).encode('utf-8')

#制作字典的报头
header = struct.pack('i', len(data_bytes))

#发送报头
client.send(header)

#发送字典
client.send(data_bytes)

#发送真实数据(上传的文件)
with open(r'D:学习python上课相关day32代码day321 上节课复习.mp4','rb') as f:
    for line in f:
        client.send(line)

 

二、UDP协议

1.udp协议客户端允许发空
2.udp协议不会粘包
3.udp协议服务端不存在的情况下,客户端照样不会报错
4.udp协议支持并发

UDP叫数据报协议,意味着发消息都带有数据报头
udp的server不需要就行监听也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来,客户端发送消息的时候,要带上服务端的地址
服务端在回复消息的时候,也需要带上客户端的地址


服务端:

import socket

server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8083 ))

while True: #服务端需要始终运行
    data,addr = server.recvfrom(1024)
    print(data,addr) #b'hello' ('127.0.0.1', 54457)
    #data是接收的数据,addr是客户端的地址
    server.sendto(data.upper(),addr)
    #将数据发送过去,同时需要加上客户端的地址


客户端:

import socket


client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8083)
client.sendto(b'hello', server_addr)
#发送数据,需要加上接收端的地址
msg,addr = client.recvfrom(1024)
#msg是接收的数据,addr是发送方的地址
print(msg,addr) #b'HELLO' ('127.0.0.1', 8083)
udp协议补充点:

        1、无链接,类似于发短信,发了就行对方爱回不回,没有任何关系。
        2、将服务端关了,客户端起起来照样能够发数据。因为不需要考虑服务端能不能收到。

  

三、基于UDP实现简单版本的qq

服务端:

import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8086))

while True:
    data,addr = server.recvfrom(1024)
    print(data.decode('utf-8'))
    data = input('>>>:').encode('utf-8')   #angel
    server.sendto(data,addr)


存在多个客户端:

客户端1:

import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8086))

while True:
    data,addr = server.recvfrom(1024)
    print(data.decode('utf-8'))
    data = input('>>>:').encode('utf-8')      #hello
    server.sendto(data,addr)

客户端2:

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8086)

while True:
    data = input('>>>:')                 #逼格
    data = '来自客户2的消息:%s' %data
    client.sendto(data.encode('utf-8'),server_addr)
    data,addr = client.recvfrom(1024)
    print(data,addr)


当启动服务端,启动客户端1后,在客户端1里输入数据,服务端会有响应,再在服务端输入数据,客户端2输入的数据会与服务端响应。


来自客户1的消息:hello
>>>:angel
来自客户2的消息:逼格


小点补充:
               windows电脑和max电脑的时间同步功能,其实就是基于udp朝windows,max服务器发送请求获取标准时间。
    
    

  

四、SocketServer 模块  (可以让tcp也支持并发)


TCP:
    
服务端:
    import socketserver
class Base(socketserver.BaseRequestHandler):
    def handle(self):            此处的方法是固定的,不然就会报错!!!   用别人的模块,只能按照别人的方法去写。
        #通信循环
        while True:
            try:
                data = self.request.recv(1024)  #收消息
                #self.request相当于通道conn
                print(data)
                self.request.send(data.upper()) #发消息
            except ConnectionResetError:
                break


if __name__ == "__main__":
    server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080),Base)
    #socketserver.ThreadingTCPServer(('127.0.0.1') 为建立连接,后者的Base为一个类
    server.serve_forever()   #服务端永久工作

客户端:

import socket

client = socket.socket()
client.connect(('127.0.0.1', 8080))
while True:
    client.send(b'hello')
    data = client.recv(1024)
    print(data)



对UDP同样适用

服务端:

import time
import socketserver

class Base(socketserver.BaseRequestHandler):
    def handle(self):
        #通信循环
        while True:
            data,sock = self.request   #data是接收的数据,sock是套接字对象
            print(data,sock)    #此处的sock不只是一个地址
#b'hello' <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>
            time.sleep(1)
            sock.sendto(data.upper(),self.client_address)  #self.client_address 客户端地址

if __name__ == '__main__':
    server = socketserver.ThreadingUDPServer (('127.0.0.1', 8080),Base)
    server.serve_forever()


客户端:

import time
import socket
client = socket.socket(type=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) #b'HELLO' ('127.0.0.1', 8080)
    time.sleep(1)

 

SocketServer  模块补充

  

基于tcp的socketserver我们自己定义的类中的

  1.   self.server即套接字对象
  2.   self.request即一个链接
  3.   self.client_address即客户端地址

基于udp的socketserver我们自己定义的类中的

  1.   self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
  2.   self.client_address即客户端地址
          1、能够实现并发效果
                        并发:看起来像同时运行就称为并发   并行:同时运行   注:单核计算机无法实现并行
          2、udp在使用的时候,多个客户端需要存在一些io操作,不然容易卡死

  

并发编程:

后续补充!

  

 

  

 

原文地址:https://www.cnblogs.com/changwenjun-666/p/10815126.html