• python网络编程


    1,socket通信

     

    2,socket对象的参数

    socket families:网络层

        socket.AF_INETIPV4

        socket.AF_INET6IPV6

        socket.AF_UNIX unix本机进程间通信

    socket types:传输层

        socket.SOCK_STREAM #TCP

        socket.SOCK_DGRAM #UDP    

        socket.SOCK_RAW  #原始套接字,可以伪造IP头

        socket.SOCK_RDM #UDP,保证传到,但不保证顺序

    3,黏包

    场景:

    单次接收设置1024,但是实际数据大于1024,多于1024的内容会再下次接收到

    服务器连续两次send,可能会被放进缓冲区形成黏包

     

    解决方法

    计算发送内容大小,先把大小发送给接收方,发送完大小后服务器必须加阻塞确认,防止黏包

    拿到内容大小后,后续如果还有多次send多问题,就可以改用精确接收内容大小防止黏包了,无需每次阻塞确认

    特别注意中文str长度1,变成bytes后长度是3

    4,模拟SSH

    服务器端:

    import socket
    import os
    
    server = socket.socket()
    server.bind(('localhost', 19999))  # 绑定端口
    server.listen(3)  # 监听端口,这里不是并发
    while True:
        conn, addr = server.accept()  # 等待连接,进入阻塞状态
        while True:
            cmd = conn.recv(1024)  # 接收大小是1024
            if not cmd:  # linux会陷入recv死循环,建议都加上防止反复接收死循环
                break
            cmd = cmd.decode()
            cmd_res = os.popen(cmd).read().encode()
    
            cmd_lens = len(cmd_res)
            conn.send(str(cmd_lens).encode())
            client_ack = conn.recv(1024)   # 加个确认防止黏包
            conn.send(cmd_res) 
    
            # server.close()  # 这里如果加close,server不会立即关闭,会在当前client结束后再关
    

    客户端:

    import socket
    
    client = socket.socket()
    client.connect(('localhost', 19999))
    while True:
        msg = input('>>: ').strip()
        if not msg:
            continue
        elif msg == 'exit':  # 退出循环,关闭连接
            break
    
        client.send(msg.encode())  # 只能send bytes,默认utf-8
        cmd_res_size = client.recv(1024)
        cmd_res_size = int(cmd_res_size.decode())  # bytes -> str -> int
        client.send(b'ack')  # 发送确认,解决长度与实际data之间的黏包问题
    
        received_size = 0
        received_data = b''
        while received_size < cmd_res_size:
            data = client.recv(1024)
            received_size += len(data)
            received_data += data
    
        print(received_data.decode())
    
    client.close()
    

    5,socket接收文件

    流程:读取文件名,检测文件是否存在,打开文件,检测文件大小,发送文件大小给客户端,等待客户端确认,边读边发,发送MD5

    服务器端:

    import socket
    import os
    import hashlib
    
    server = socket.socket()
    server.bind(('localhost', 19999))
    server.listen()
    
    while True:
        conn, addr = server.accept()
        while True:
            data = conn.recv(1024)
            if not data:
                break
            cmd, filename = data.decode().split()
            if os.path.isfile(filename):
                f = open(filename, 'rb')  # 'rb'打开,后面不用encode了
                file_size = os.stat(filename).st_size
                conn.send(str(file_size).encode())  # 发送文件大小
                conn.recv(1024)  # 等待确认
                m = hashlib.md5()
                for line in f:   # 发送文件,f是迭代器
                    m.update(line)  # 逐行更新计算MD5
                    conn.send(line)
                f.close()
                conn.send(m.hexdigest().encode())  # 将MD5发送给客户端
    

    客户端:

    使用get + 文件名,获取文件

    import socket
    import hashlib
    
    client = socket.socket()
    client.connect(('localhost', 19999))
    
    while True:
        cmd = input('>>: ').strip()
        if not cmd:
            continue
        elif cmd == 'break':
            break
        elif cmd.startswith('get'):
            client.send(cmd.encode())  # 只能send bytes,默认utf-8
            file_size = client.recv(1024)
            file_size = int(file_size.decode())  # bytes -> str -> int
            client.send(b'ack')  # 发送确认,解决长度与实际data之间的黏包问题
    
            received_size = 0
            file_name = cmd.split()[1]
            m = hashlib.md5()
    
            f = open(file_name + '.new', 'wb')
            while received_size < file_size:
                size = 1024
                if file_size - received_size > 1024:
                    buff = file_size - received_size  # 最后一次收实际数据,防止黏包把服务器MD5写入文件
                data = client.recv(size)
                received_size += len(data)
                m.update(data)
                f.write(data)
            f.close()
    
            client_file_md5 = m.hexdigest()
            server_file_md5 = client.recv(1024).decode()
    
            print('客户端文件MD5:', client_file_md5)
            print('服务器端文件MD5:', server_file_md5)
    
    client.close()
    

    6,socketserver

    对socket进行二次封装,简化了socket服务器端编写,实现服务器端并发

    socketserver实现模拟SSH服务器端(客户端不变):

    class TCPHandler(socketserver.BaseRequestHandler):
    
        def handle(self):
            while True:  # 不写while True,每个连接只能处理一次命令
                cmd = self.request.recv(1024)
                if not cmd:  # cmd为空代表客户端断开了,不做非空判断客户端断开时可能会无限循环
                    break
                cmd = cmd.decode()
                cmd_res = os.popen(cmd).read().encode()
                cmd_lens = len(cmd_res)
                self.request.send(str(cmd_lens).encode())
                self.request.recv(1024)
                self.request.send(cmd_res)
    
    
    if __name__ == '__main__':
        server = socketserver.TCPServer(('localhost', 9999), TCPHandler)
        server.serve_forever()
    

     将TCPServer改成ThreadingTCPServer可以实现多并发

  • 相关阅读:
    理解字节序(转)
    《逆向分析实战》数据的存储及表示形式
    C语言——内存分配
    C语言编程基础学习字符型数据的ASCII码值为何是负数?
    你知道嵌入式C语言中各变量存储的位置吗?
    stm32入门(从51过渡到32)
    说说M451例程讲解之LED
    说说M451的例程库的说明
    STM32总线结构和存储器
    STM32学习之路入门篇之指令集及cortex——m3的存储系统
  • 原文地址:https://www.cnblogs.com/guxh/p/10687003.html
Copyright © 2020-2023  润新知