网络编程 基础二

subprocess模块

​ 作用:
​ 1、可以通过代码执行操作系统的终端命令
​ 2、并返回终端执行命令后的结果

    用法:
    import subprocess
    
    cmd = inpu('CMD >>>')
    obj = subprocess.Popen(
        cmd.decode('utf8'), 
        shell=True,
        stdout= subprocess.PIPE,
        stderr= subprocess.PIPE
        )
    result = obj.stdout.read() + obj.stderr.read()
    
    print(result.decode('gbk'))

TCP协议

特性:是一个流式协议,会将多次连续发送的数据量小、间隔时间短的数据一次性打包发送

黏包问题

​ 原因:

​ 1、无法预测需要接收的数据的长度
​ 2、多次连续发送数据量小、并且时间间隔短的数据一次性打包发送。

解决黏包问题: 告诉对端数据的确切长度。
-struct模块:

​ 必须先发送报头,再发送数据:
​ 1、自定义报头,将数据长度等数据的描述信息制作成字典,
​ 2、将字典json.dumps序列化并编码转为bytes,用于传输
​ 3、先将报头的长度,用struct.pack格式化成固定长度(4字节)
​ 4、发送:先发送报头长度,再发送报头,然后发送数据正文

​ 接收:

​ 1、先接收到4个字节的报头长度信息,unpack解压得到报头的长度值

​ 2、根据报头长度值接收报头,解码并json.load反序列得到数据正文的描述信息(包含数据长度)

​ 3、根据数据长度接收数据正文,

​ 用法:

import json,struct
#假设通过客户端上传10G:10000000的文件:小视频.txt

#为避免粘包,必须自定制报头
header={'file_size':10000000,'file_name':'小视频.txt', 'md5':'8f6fbf8347faa4924a76856701edb0f3'} #10G数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

上传大文件

以上传视频为例:

客户端:

import socket
import struct
import json

client = socket.socket()

client.connect(
    ('127.0.0.1', 9527)
)

with open(r'C:\day 027\5 上传大文件.mp4', 'rb')as f:
    movie_bytes = f.read()

head_dic = {
    'file_name': '5 上传大文件.mp4',
    'file_size': len(movie_bytes)
}

json_head = json.dumps(head_dic)

bytes_head = json_head.encode('utf-8')

headers = struct.pack('i', len(bytes_head))

client.send(headers)
client.send(bytes_head)

init_data = 0
num = 1
with open(r'C:\day 027\5 上传大文件.mp4', 'rb')as f:
    while init_data < len(movie_bytes):
        send_data = f.read(1024)
        num += 1
        client.send(send_data)
        print(send_data, f'第{num}段数据发送完成')
        init_data += len(send_data)
    print('发送完毕!')


服务端:

import socket
import json
import struct

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

while True:
    conn, addr = server.accept()
    try:

        headers = conn.recv(4)

        head_len = struct.unpack('i', headers)[0]

        byte_head = conn.recv(head_len)

        head_dic = json.loads(byte_head.decode('utf-8'))
        print(head_dic)

        file_name = head_dic.get('file_name')
        file_size = head_dic.get('file_size')

        init_data = 0
        num = 1
        with open(file_name, 'wb') as f:

            while init_data < file_size:
                data = conn.recv(1024)
                print(f'第{num}段',data)
                f.write(data)
                init_data += len(data)
            print(f'{file_name}接收完毕!')

    except Exception as e:
        print('error:', e)
        break
conn.close()

UDP

​ UDP是一种传输协议
​ 1、不需要建立双向管道
​ 2、不会黏包
​ 3、客户端给服务端发送数据,不需要等服务端的回执
​ 4、服务端只接收需要接收的数据,不管客户端传过来多少

服务端:

import socket

server = socket.socket(type=socket.SOCK_DGRAM)

server.bind(
    ('127.0.0.1', 9527)
)

msg1 = server.recvfrom(1024)
msg2 = server.recvfrom(1024)
msg3 = server.recvfrom(1024)

print(msg1, msg2, msg3)

客户端:

import socket

client =socket.socket(type=socket.SOCK_DGRAM)

server_ip_port = ('127.0.0.1', 9527)

client.sendto(b'hello!', server_ip_port)
client.sendto(b'hello!', server_ip_port)
client.sendto(b'hello!', server_ip_port)
client.sendto(b'hello!', server_ip_port)
client.sendto(b'hello!', server_ip_port)

执行结果:(b'hello!', ('127.0.0.1', 57182)) (b'hello!', ('127.0.0.1', 57182)) (b'hello!', ('127.0.0.1', 57182))

简易聊天室

基于TCP协议,用socket套接字实现一个服务端对多个客户端的简易聊天室:

服务端:

import socket

server = socket.socket(type=socket.SOCK_DGRAM)

server.bind(
    ('127.0.0.1', 9527)
)

while True:

    # 服务端接收客户端传过来的消息
    msg, addr = server.recvfrom(1024)  # (消息,客户端地址)
    msg1, addr1 = server.recvfrom(1024)  # (消息,客户端地址)
    msg2, addr2 = server.recvfrom(1024)  # (消息,客户端地址)

    print(addr)
    print(addr1)
    print(addr2)
    print(msg.decode('utf-8'))
    print(msg1.decode('utf-8'))
    print(msg2.decode('utf-8'))

    # 服务端往客户端发送消息
    send_msg = input('服务端发送消息:').encode('utf-8')
    server.sendto(send_msg, addr)
    server.sendto(send_msg, addr1)
    server.sendto(send_msg, addr2)

客户端1:

import socket

client = socket.socket(type=socket.SOCK_DGRAM)

server_ip_port = ('127.0.0.1', 9527)

while True:
    send_msg = input('客户端1: ').encode('utf-8')

    # 发送消息必须要加上对方地址
    client.sendto(send_msg, server_ip_port)

    # 能接收任何人的消息
    msg = client.recv(1024)

    print(msg.decode('utf-8'))

客户端2:

import socket

client = socket.socket(type=socket.SOCK_DGRAM)

server_ip_port = ('127.0.0.1', 9527)

while True:
    send_msg = input('客户端2: ').encode('utf-8')

    # 发送消息必须要加上对方地址
    client.sendto(send_msg, server_ip_port)

    # 能接收任何人的消息
    msg = client.recv(1024)

    print(msg.decode('utf-8'))

客户端3:

import socket

client = socket.socket(type=socket.SOCK_DGRAM)

server_ip_port = ('127.0.0.1', 9527)

while True:
    send_msg = input('客户端3: ').encode('utf-8')

    # 发送消息必须要加上对方地址
    client.sendto(send_msg, server_ip_port)

    # 能接收任何人的消息
    msg = client.recv(1024)

    print(msg.decode('utf-8'))

先启动服务端,再依次启动客户端,可开始模拟聊天室聊天

SocketServer

​ 是python内置模块,可以用来简化socket套接字服务端的代码

import socket

class MyTcpServer(socketserver, BaseRequestHandler):
    def handler(self):
        
        data = self.request.recv(1024)
        pirnt(data)
        send_msg = input('服务端:').encode('utf8')
        self.request.send(send_msg)

猜你喜欢

转载自www.cnblogs.com/allenchen168/p/11711218.html