udp 使用connect优点_Python 中的 socket 深入使用

本文介绍了Python中UDP协议的使用,包括通过connect解决服务端持续等待问题,实现文件上传,多人聊天功能,并探讨了UDP与TCP的区别,以及如何使用socketserver实现TCP并发。
摘要由CSDN通过智能技术生成

7306252b2b5c1bb7e08a9c5e1177a1c1.png
封面图片来源:沙沙野

内容概览

  1. 服务端持续等待的解决方式
  2. 使用 socket 上传文件
  3. UDP 协议基本用法
  4. 使用 UDP 协议实现多人聊天功能
  5. 验证客户端的合法性
  6. 使用 socketserver 来实现 TCP 协议 socket 的并发

服务端持续等待的解决方式

  1. 之前的示例都是如果客户端停止通信了,服务端也会跟着停止。那么,如何让服务端与一个客户端的用户聊天退出后,还能继续等待下一个客户端的用户聊天呢?——解决思路:还是可以尝试用 whie 循环
  2. server.py 中的内容
import socket

sk = socket.socket()

sk.bind(('127.0.0.1',9000))
sk.listen()

while 1: 
    conn,addr = sk.accept()
    while 1:
        send_msg = input('请输入消息:')
        conn.send(send_msg.encode())
        if send_msg == 'q':break
        msg = conn.recv(1024).decode()
        if msg == 'q':break
        print(msg)
    conn.close()

sk.close()

3. client.py 中的内容

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9000))

while True:
    msg = sk.recv(1024).decode()
    if msg == 'q':break
    print(msg)
    send_msg = input('请输入消息: ')
    sk.send(send_msg.encode())
    if send_msg == 'q':break

sk.close()

4. 运行两个文件,发现如果客户端输入 q 退出程序后,再次重启客户端(相当于另一个用户进入),还能继续和服务端通信

# server.py
(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python server.py 
# 与客户端的第一次交流
请输入消息:晚上好
你好
请输入消息:再见
# 此时客户端已经退出,但服务端没有停止运行
# 只是在等待另一个客户端的链接请求,除非手动强制关掉它
请输入消息:你好
晚上好
请输入消息:再见


# client.py
# 第一次链接服务端
(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
晚上好
请输入消息:你好
再见
请输入消息:q
# 第二次链接服务端
(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
你好
请输入消息:晚上好
再见
请输入消息:q

使用 socket 上传文件

  1. 整体的解决思路先捋一遍,首先客户端在上传文件之前:
  • 先向服务端传递该文件的相关信息,比如文件大小、文件名以及要做的操作等,这时就要用到 send
  • 但是又有可能与下一个 send 发生粘包现象,因此还要使用 struct 模块。

2. 下一步,客户端要开始向服务端传文件,大概步骤:

  • 打开文件
  • 读取文件内容:按照固定字节读取,比如该文件有 5000 个字节,每次向服务端发送 1024 个字节,这样的话要 send 五次,但是不用担心粘包问题

3. 最后,服务端的接收步骤:

  • 先接收 4 字节,知道了文件信息的长度
  • 按照长度接收文件信息
  • 从文件信息中得到文件的大小
  • 开始接收,直到收完文件大小这么多的数据

4. 按照上面的思路,先写 client.py 的内容

import os
import json
import struct
import socket


# 假设同一目录下有个视频文件
# 注意:r 表示字符串里面的内容写的什么就是什么
# 不会被 Python 误以为比如  是转义字符
file_path = r"G:learntest.mp4"
file_size = os.path.getsize(file_path)
file_name = os.path.basename(file_path)
print(file_size)    # 143799118
print(file_name)    # test.mp4

file_info = {"filesize": file_size, "filename": file_name, "operate": "upload"}
json_info = json.dumps(file_info)
file_info_bytes = json_info.encode()
bytes_len = len(file_info_bytes)
sk = socket.socket()
sk.connect(("127.0.0.1", 8080))
# 先发送文件信息的长度,再发送文件信息
sk.send(struct.pack("i", bytes_len))
sk.send(file_info_bytes)

with open(file_path, "rb") as f:
    while filesize > 0:
        content = f.read(1024)
        filesize -= len(content)
        # 这里已经是bytes类型
        sk.send(content)

sk.close()

5. 接着写 server.py 的内容

import struct
import socket
import json

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))
sk.listen()

conn, addr = sk.accept()
bytes_len = conn.recv(4)
info_len = struct.unpack("i", bytes_len)[0]
json_info = conn.recv(info_len).decode()
info_dic = json.loads(json_info)
print(info_dic)

# 把接收到的视频文件放在与 server.py 同一目录下
with open(info_dic["filename"], "wb") as f:
    while info_dic["filesize"]:
        content = conn.recv(1024)
        f.write(content)
        info_dic["filesize"] -= 1024

conn.close()
sk.close()

6. 这里有问题,如果把这里的『 content = conn.recv(1024) 』的 1024 和 client.py 里的 1024 都改为 2048 或者更大的数,运行结束后发现查看该文件时文件大小变小了。原因是这里虽然指定每次接收 1024 个字节,但不代表每次一定就能接收到 1024 个字节,比如最后还剩 200 多字节,就会发生拆包现象。为了解决这个问题,可以这样:

# server.py
import struct
import socket
import json

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))
sk.listen()

conn, addr = sk.accept()
bytes_len = conn.recv(4)
info_len = struct.unpack("i", bytes_len)[0]
json_info = conn.recv(info_len).decode()
info_dic = json.loads(json_info)
print(info_dic)

with open(info_dic["filename"], "wb") as f:
    while info_dic["filesize"]:
        content = conn.recv(1024)
        f.write(content)
        # info_dic["filesize"] -= 1024
        info_dic["filesize"] -= len(content)

conn.close()
sk.close()

UDP 协议基本用法

  1. 还记得之前发的 TCP 协议与 UDP 协议的流程图吗?再来回顾一下,看看两者之间的区别

4cdbd01aaf1c55f666c9eb23c58d7a02.png

2. server.py 内容

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1", 8080))
# 这里不用写sk.listen(),没有三次握手

msg, client_addr = sk.recvfrom(1024)
print(msg)      # b'hello'
sk.sendto(b"hello", client_addr)

sk.close()

3. client.py 的内容

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

sk.sendto(b"hello", ("127.0.0.1", 8080))
msg, addr = sk.recvfrom(1024)
print(msg)      # b'hello'

sk.close()

使用 UDP 协议实现多人聊天功能

  1. server.py
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1", 8080))

# 和一个人多次聊天
while True:
    msg, client_addr = sk.recvfrom(1024)
    print(msg.decode())
    content = input(">>>")
    sk.sendto(content.encode(), client_addr)

sk.close()

2. client.py

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

# 和一个人多次聊天
# 可以再创建一个client.py,内容一模一样,然后依次运行
# 都可以跟server端聊天
while True:
    content = input(">>>")
    # 多人聊天时,可以这样区分谁跟server端聊天
    content = "%s : %s" % ("小明", content)
    sk.sendto(content.encode(), ("127.0.0.1", 8080))
    msg, addr = sk.recvfrom(1024)
    print(msg.decode())

sk.close()

验证客户端的合法性 (了解)

  1. server.py
import os
import hmac
import socket

def auth(conn):
    secret_key = b"jane"
    rand_b = os.urandom(32)
    conn.send(rand_b)
    obj = hmac.new(secret_key, rand_b)
    res1 = obj.digest()
    res2 = conn.recv(1024)
    cmp_res = hmac.compare_digest(res1, res2)
    return cmp_res

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))
sk.listen()

conn, addr = sk.accept()
res = auth(conn)
if res:
    print("是合法的客户端")
    conn.send("您好".encode())
else:
    conn.close()

sk.close()

2. client.py

import socket
import hmac

def auth(sk):
    secret_key = b"jane"
    rand_b = sk.recv(32)  # 注意这里
    obj = hmac.new(secret_key, rand_b)
    res2 = obj.digest()
    sk.send(res2)

sk = socket.socket()
sk.connect(("127.0.0.1", 8080))
auth(sk)
msg = sk.recv(1024)
print(msg.decode())

sk.close()

3. 运行后得出结果:是合法的客户端。只要两边的secret_key的值不一样,服务端就知道不是合法的客户端

使用 socketserver 来实现 TCP 协议 socket 的并发

  1. socket是基于socketserver的下层模块,socketserver是基于socket的上层模块,因此 socketserver 可以实现并发
  2. server.py
import socketserver


class Myserver(socketserver.BaseRequestHandler):

    # 注意:有多少个client端,就相当于这里有多少个handle
    def handle(self):
        # 可以与多个client端运行,即并发
        while True:
            conn = self.request
            conn.send(b"hello")

# 这里不需要实例化一个对象
server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), Myserver)
server.serve_forever()

3. client.py,写几个内容一样的客户端文件,进行测试即可

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8080))

while True:
    msg = sk.recv(1024)
    print(msg)

sk.close()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值