zoukankan      html  css  js  c++  java
  • Python 之网络编程

    # 流程描述:
    #
    # 1. 服务器根据地址类型(ipv4, ipv6), socket类型, 协议创建socket;
    #
    # 2. 服务器为socket绑定ip地址和端口号;
    #
    # 3. 服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开;
    #
    # 4. 客户端创建socket
    #
    # 5. 客户端打开socket, 根据服务器ip地址和端口号试图连接服务器socket;
    #
    # 6. 服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket
    #   进入阻塞状态, 所谓阻塞即accept()方法一直等到客户端返回连接信息后才返回,开始接收下一个客户端连接请求。
    #
    # 7. 客户端连接成功,向服务器发送连接状态信息;
    #
    # 8. 服务器 accept 方法返回,连接成功;
    #
    # 9. 客户端向socket写入信息(或服务端向socket写入信息)
    #
    # 10. 服务器读取信息(或客户端读取信息)
    #
    # 11. 客户端关闭
    # 12. 服务端关闭
    

    1.1 相关方法及参数介绍

    import socket
    sk = socket.socket()
    
    • sk.bind(address):将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式
      表示地址;
    • sk.listen(backlog):开始监听传入的连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
      backlog等于5,表示内核已经接到的连接请求,但服务器还没有调用accept进行处理的连接个数最大为5。这个值不可能无限大,因为要在内核中维护连接队列;
    • sk.setblocking(bool): 是否阻塞(默认为True), 如果设置为False, 那么accept和recv时,一旦无数据,则报错;
    • sk.accept(): 接收连接并返回(conn, address), 其中conn是新的套接字对象,可以用来接收和发送数据;address是
      连接客户端的地址;
    • sk.connect(address): 连接到address处的套接字。一般,address的格式为元组(hostname, port),如果连接出错,
      返回socket.error错误;
    • sk.connect_ex(address): 同上,只不过会有返回值,连接成功时,返回0; 连接失败的时候,返回编码,例如: 10061
    • sk.close(): 关闭套接字;
    • sk.recv(bufsize[, flag]): 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。
    • sk.recvfrom(bufsize[,flag]): 与recv()类似,但返回值是 (data, address), 其中,data是包含接收数据的字符串,
      address 是发送数据的套接字地址;
    • sk.send(string[,flag]): 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字
      节大小。即: 可能未将指定内容全部发送。
    • sk.sendall(string[,flag]): 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功,返回None;
      失败则抛出异常。内部通过递归调用 send, 将所有内容发送出去。
    • sk.sendto(string[,flag], address): 将数据发送到套接字,address的形式为元组(ipaddr, port)。返回值是发送的
      字节数,该函数主要用于UDP协议;
    • sk.settimeout(timeout):设置套接字操作的超时时长,timeout是一个浮点数,单位是秒。值为None,表示没有超时期。
    • sk.getpeername():返回连接套接字的远程地址。返回值通常是元组(ipaddr, port);
    • sk.getsockname():返回套接字自己的地址,通常是一个元组(ipaddr, port);
    • sk.fileno(): 套接字的文件描述符;
    # 示例一: 建立连接
    # sever 端
    import socket
    
    # 创建socket对象
    sk = socket.socket()
    
    # 绑定ip和端口
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    
    sk.listen(3)
    print("waiting......")
    conn = sk.accept()    # 此时,返回一个包含两个元素的元组
    print(conn)
    
    
    # client 端
    import socket
    
    sk = socket.socket()
    
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    
    # 示例二: 服务端发送,客户端接收
    # server 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    
    sk.listen(3)
    print('waiting......')
    conn, addr = sk.accept()
    
    # 发送消息
    msg = input('>>>')
    # conn.send(msg), 此处会报错: a bytes-like object is required, not 'str'
    conn.send(bytes(msg, 'utf8'))
    
    
    # client 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    # 接收消息
    data = sk.recv(1024)    # 此处,客户端会阻塞,等待服务端发送消息
    print(str(data, 'utf8'))
    
    
    # 示例三: 客户端发送, 服务端接收
    # server 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    
    sk.listen(3)
    print('waiting......')
    conn, addr = sk.accept()
    
    # 接收消息
    data = conn.recv(1024)  # 注意,服务端使用 conn 来收发数据
    print(data)
    conn.close()    # 关闭连接
    
    
    # client 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    # 发送消息
    sk.send(bytes('大家好', 'utf8'))
    
    sk.close()
    
    
    # 示例四: 不间断聊天
    # server 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    
    sk.listen(3)
    print('waiting......')
    conn, addr = sk.accept()
    
    while True:
        data = conn.recv(1024)
        if not data:
            break
        print(str(data, 'utf8'))
        msg = input('>>>')
        conn.send(bytes(msg, 'utf8'))
    
    conn.close()
    
    # client 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    while True:
        msg = input('>>>')
        if msg == 'exit':
            break
        sk.send(bytes(msg, 'utf8'))
        data = sk.recv(1024)
        print(str(data, 'utf8'))
    
    sk.close()
    
    
    # 示例五:
    # server 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    
    sk.listen(3)
    print('waiting......')
    
    while True:
        conn, addr = sk.accept()    # 某一个客户端关闭后,可以接收另外一个客户端发来的消息
        print(addr)
    
        while True:
            try:
                data = conn.recv(1024)
            except Exception:
                break
            if not data:
                break
            print(str(data, 'utf8'))
            msg = input('>>>')
            conn.send(bytes(msg, 'utf8'))
    
    sk.close()
    
    
    # 示例六: 远程执行命令
    # server 端
    import socket
    import subprocess
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    sk.listen(3)
    print('waiting......')
    
    while True:
        conn, addr = sk.accept()
        print(addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            print(str(data, 'utf8'))
    
            obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE)
            cmd_result = obj.stdout.read()
            # 只能传输bytes类型数据,int首先转成str,然后转成bytes
            result_len = bytes(str(len(cmd_result)), 'utf8')        # 获取传输内容的大小
            conn.sendall(result_len)
    
            conn.recv(1024)     # 解决粘包问题
    
            conn.sendall(cmd_result)
    
    sk.close()
    
    
    
    # client 端
    import socket
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    while True:
        msg = input('>>>')
        if msg == 'exit':
            break
        sk.send(bytes(msg, 'utf8'))
    
        result_len = int(str(sk.recv(1024), 'utf8'))
    
        sk.sendall('ok')
    
        data = bytes()      # 空字节
        while len(data) != result_len:
            data += sk.recv(1024)
    
        print(str(data, 'utf8'))
    
    
    # 示例七: 上传文件
    # server 端
    import socket
    import os
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.bind(address)
    sk.listen(3)
    print('waiting......')
    
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    
    while True:
        conn, addr = sk.accept()
    
        while True:
            data = conn.recv(1024)
            cmd, file_name, file_size = str(data, 'utf8').split('|')
            path = os.path.join(BASE_DIR, 'a', file_name)
            file_size = int(file_size)
    
            f = open(path, 'ab')
            has_receive = 0
            while has_receive != file_size:
                data = conn.recv(1024)
                f.write(data)
                has_receive += len(data)
            f.close()
    
    sk.close()
    
    
    # client 端
    import socket
    import os
    
    sk = socket.socket()
    address = ('127.0.0.1', 8000)
    sk.connect(address)
    
    BASE_DIR=os.path.dirname(os.path.abspath(__file__))
    
    while True:
        msg = input('>>>').strip()  # 规定输入格式为: post|a.jpg(命令|路径), strip()去除首尾空格
    
        cmd, path = msg.split('|')
        # 路径拼接, 因为传入的path可能为 a/b/a.jpg
        path = os.path.join(BASE_DIR, path)
    
        # 获取文件名称
        filename = os.path.basename(path)
        # 获取文件大小
        file_size = os.stat(path).st_size
    
        file_info = 'post|%s|%s' % (filename,file_size)
        # 向服务端发送消息
        sk.sendall(bytes(file_info, 'utf8'))
    
        # 向服务端发送文件内容
        f = open(path, 'rb')    # 以bytes的格式读取数据
        has_sent = 0
        while has_sent != file_size:
            data = f.read(1024)     # 每次发送1024字节数据
            sk.sendall(data)
            has_sent += len(data)
    
        f.close()
        print('上传成功!')
    

    2. socketserver 模块

    # 示例一: server端并发聊天
    # server 端
    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):
    
        def handle(self):
            print('服务端启动...')
            while True:
                conn = self.request
                print(self.client_address)
                while True:
                    client_data = conn.recv(1024)
                    print(str(client_data, 'utf8'))
                    print('waiting......')
                    conn.sendall(client_data)
                conn.close()
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('127.0.0.1', 8000), MyServer)
        server.serve_forever()
    
    
    # client 端
    import socket
    
    ip_port = ('127.0.0.1', 8000)
    sk = socket.socket()
    sk.connect(ip_port)
    print('客户端启动:')
    
    while True:
        msg = input('>>>')
        sk.sendall(bytes(msg, 'utf8'))
        if msg == 'exit':
            break
        server_response = sk.recv(1024)
        print(str(server_response, 'utf8'))
    
    sk.close()
    

    参考资料:

  • 相关阅读:
    线程中一些常用方法的分析
    Java中获取路径的方法_自我分析
    Mybatis 一对一,一对多,多对一,多对多的理解
    页面刷新两次为什么取不到值
    排序--插入
    排序--冒泡
    页面刷新两次为什么取不到值
    容器模式
    java操作oracle的blob,clob数据
    Redis基本操作命令
  • 原文地址:https://www.cnblogs.com/linkworld/p/8628229.html
Copyright © 2011-2022 走看看