• Python学习笔记之 网络编程(socket套接字编程)


    socket套接字编程

    服务端&客户端流程

    TCP服务端&客户端示例代码:

    # 服务端
    import socket,threading
    def jieshou(a,b):
        print(f'建立连接{a}{b}')
        while True:
            c = a.recv(1024) # 接收消息
            if c:
                shuju = c.decode('gbk') # 解码
                print(f'{b}数据:{shuju}')
            else:
                break
        a.close()
    
    if __name__ == '__main__':
        tcp = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        tcp.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)  # 设置端口号复用
        tcp.bind(("",9090)) # 绑定地址,端口号
        tcp.listen(128) # 设置监听
        while True:
            a, b = tcp.accept()
            thread1 = threading.Thread(target=jieshou,args=(a,b)) # 创建子线程
            thread1.daemon = True # 设置为守护线程
            thread1.start() # 执行
        tcp.close()
    
    # 客户端
    import socket
    
    if __name__ == '__main__':
        my_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        my_socket.connect(("172.40.91.185",9090)) # 连接服务端
        my_socket.send('Hello'.encode('gbk')) # 发送消息
        my_socket.close()
    

    运行结果:

    客户端:

    服务端:

    UDP客户端&服务端示例:

    # 客户端
    import socket
    
    udp = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建套接字
    udp.sendto('Hello'.encode('gbk'),("172.40.91.185",10000)) # 发送消息
    udp.close()
    
    # 服务端
    import socket
    '''
    UDP不用连接,直接接收数据
    '''
    if __name__ == '__main__':
        udp = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建套接字
        udp.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True) # 设置端口号复用
        udp.bind(("",10000)) # 绑定地址
        while True:
            data, addr = udp.recvfrom(2048) # 接收消息
            if not data:
                break
            print(f'来自{addr}的数据:{data}')
        udp.close()
    

    运行结果:

    客户端:

    服务端:

    解析

    1.创建套接字

    sockfd = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM,proto=0,fileno=None)

    功能:创建套接字

    参数:

    family -> 网络地址类型: AF_INET_表示_IPv4、 AF_INET6_表示_IPv6

    type -> 套接字类型: SOCK_STREA(数据流,基于TCP)、 SOCK_DGRAM(数据包,基于UDP)

    _proto -> _通常为0, 选择子协议

    返回值: 套接字对象

    2.设置端口号复用

    sockfd.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    

    3.绑定地址

    本地地址:'localhost', '127.0.0.1'

    网络地址:'172.40.91.185'

    自动获取地址:'0.0.0.0'

    sockfd.bind(("",9090)) # bind((IP地址,端口号))
    

    4.设置监听(TCP)

    sockfd.listen(n)

    功能: 将套接字设置为监听套接字,确定监听队列大小

    参数: 监听队列大小(没什么用,摆设而已,一般会开多线程连接)

    5.等待处理客户端连接请求(TCP)

    connfd, addr = sockfd.accept()

    功能: 阻塞等待处理客户端请求

    返回值: 元组

    _connfd -> _ 客户端连接套接字

    _addr -> _ 连接的客户端地址

    6.连接服务端(TCP)

    sockfd.connect((IP地址,端口号))

    功能: 连接指定的服务端

    7.收发消息

    TCP版: data = connfd.recv(buffersize)

    UDP版: data = connfd.recvfrom(buffersize)

    功能: 接收消息, 如果一方断开连接,则会返回空字符串

    参数: 每次传输的数据大小

    TCP粘包

    原因: TCP以字节流方式传输,没有边界消息。多次发送的消息被一次接收,此时就会形成粘包

    影响: 如果每次发送的内容是一个独立含义,需要接收端独立解析,此时粘包会有影响

    处理方法

    1. 人为的添加消息边界

    2. 控制发送速度
    3. 使用struct

    import struct
    
    struct.pack(format,v)
    功能:打包字节对象(bytes)
    参数:format 格式化字符 'i' int; 'q' long long
    返回:bytes
    

    实例:文件传输助手

    # 发送端
    import socket,os
    from struct import pack
    
    def send_file(file_name,file_socket:socket.socket):
        try:
            f = open(file_name,'rb')
            size = os.path.getsize(file_name)
            if size < 1024:
                read_size = 500
            elif size < 1024*1024 and size >= 1024:
                read_size = 500*1024
            else:
                read_size = 500*1024*1024
            file_socket.send(pack('q',size))
            del size
            file_socket.recv(1024)
            while True:
                data = f.read(read_size)
                if not data:
                    break
                file_socket.send(data)
            f.close()
        except FileNotFoundError:
            print(f'没有找到{file_name}')
    
    if __name__ == '__main__':
        try:
            file_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            file_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
            ip = str(input('请输入接收端IP:'))
            port = int(input('请输入接收端端口号:'))
            file_socket.connect((ip,port))
            print('连接成功,准备开始传输。。。')
            path = r'%s' % (input('请输入文件所在文件夹:') + '/')
            print(file_socket.recv(1024).decode('gbk'))
            file_name = str(input())
            file_socket.send(file_name.encode('gbk'))
            send_file(path + file_name,file_socket)
        except ConnectionResetError:
            print('接收端断开连接')
    
    # 接收端
    import socket,os
    from time import time
    from struct import unpack
    from tqdm import tqdm
    
    DOWNLOAD_PATH = 'D:/Python文件传输/' # 传输目录
    
    def download(file_name,file_socket:socket.socket):
        file_size = unpack('q',file_socket.recv(1024))[0]
        if file_size < 1024:
            print(f'文件大小:{file_size} B')
        elif file_size < 1024*1024 and file_size >= 1024:
            print(f'文件大小:{file_size/1024} KB')
        else:
            print(f'文件大小:{file_size/(1024*1024)} MB')
        f = open(file_name,'wb')
        print('开始传输...')
        download_size = 2048
        file_socket.send('开始传输'.encode('gbk'))
        start = time()
        for i in tqdm(range(int(file_size/download_size) + 1)):
            data = file_socket.recv(download_size)
            f.write(data)
        end = time()
        f.close()
        print('传输完成!')
        print(f'大约耗时{end - start}秒')
    
    if __name__ == '__main__':
        os.chdir(DOWNLOAD_PATH)
        try:
            file_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            file_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
            file_socket_port = int(input('请输入端口号:'))
            file_socket.bind(('',file_socket_port))
            print('成功启动,等待连接。。。')
            file_socket.listen(1)
            f_socket, f_addr = file_socket.accept()
            print(f'建立连接{f_addr}')
            f_socket.send('请输入文件名'.encode('gbk'))
            file_name = f_socket.recv(1024)
            download(file_name.decode('gbk'),f_socket)
            f_socket.close()
            file_socket.close()
        except ConnectionResetError:
            print('发送端已断开连接')
        except UnicodeDecodeError:
            print('文件编码错误,请检查文件格式是否正确')
    
  • 相关阅读:
    Castle Core 4.0.0 alpha001发布
    URL安全的Base64编码
    .NET Core RC2/RTM 明确了时间表
    一个免费的、跨平台的、开源音频编辑器Audacity
    Azure Service Fabric 开发环境搭建
    Microsoft Loves Linux
    微软将向Linux用户提供SQL Server程序
    微软收购Xamarin,你怎么看?
    我的梦幻2015,祝大家猴年吉祥,万事如意,幸福安康
    通用的序列号生成器库
  • 原文地址:https://www.cnblogs.com/zhujiangyu/p/13542390.html
Copyright © 2020-2023  润新知