美文网首页
python学习笔记之--网络编程

python学习笔记之--网络编程

作者: itsenlin | 来源:发表于2022-03-26 18:06 被阅读0次

基本知识

  • 计算机网络知识,主要了解一下OSI的七层架构,以及TCP/IP的四层架构。
  • 网络通信中最基本的概念:套接字,也叫通信端点、socket
  • 网络通信地址表示:一般是主机+端口号
  • 最常用的两种连接方式:面向连接方式(TCP,SOCK_STREAM)和面向无连接方式(UDP, SOCK_DGRAM)

这些基本的知识,网上有很多相关介绍,不是本章重点,有需要可以在网上查找相关资料。
这里学习一下python语言对底层网络编程的支持,这里只学习最常用的socket编程相关知识,详细说明可以参考官方文档:底层网络接口

socket模块

python有一个专门的socket模块来支持底层网络通信。是对Unix系统调用和套接字库接口的直译。

socket编程基本套路

  1. TCP服务端
    s = socket()  # 创建一个服务端socket
    s.bind()      # 绑定socket地址(ip+port)
    s.listen()    # 监听此地址是否有连接
    loop:
        cs = s.accept() # 阻塞等待客户端连接,连接成功返回新的套接字对象,用于在此连接上收发数据
        comm_loop:
            cs.recv()/cs.send() # 通信:接收数据/发送数据
        cs.close() # 关闭通信套接字对象
    s.close() # 关闭服务端套接字对象
    
  2. TCP客户端
    cs = socket()  # 创建一个客户端socket
    cs.connect()      # 通过服务端地址(ip + port)尝试连接服务端
    comm_loop:
        cs.send()/cs.recv() # 通信:发送数据/接收数据
    cs.close() # 关闭客户端套接字对象
    
  3. UDP服务端
    ss = socket()  # 创建一个服务端socket
    ss.bind()      # 绑定服务端地址
    comm_loop:
        cs.recvfrom()/cs.sendto() # 通信:接收数据/发送数据
    cs.close() # 关闭客户端套接字对象
    

说明: UDP无连接,不需要监听。先被动接收数据,会返回客户端的地址,后面就可以根据客户端地址主动发送消息。

  1. UDP客户端
    cs = socket()  # 创建一个客户端socket
    comm_loop:
        cs.sendto()/cs.recvfrom() # 通信:发送数据/接收数据
    cs.close() # 关闭客户端套接字对象
    

socket通信实例

先从一个TCP实例开始学习,功能比较简单:

  1. 一个客户端,一个服务器端,两个python程序可以放在两台电脑上运行
  2. 客户端给服务端发送一个消息;服务器端接收到消息之后,加上一个时间戳再返回给客户端
  3. 无消息则结束
  • 服务端代码

    from socket import *
    from time import ctime
    
    HOST = ''
    PORT = 21567
    BUFF = 1024
    
    ADDR = (HOST, PORT)
    
    tcpSvrSocket = socket(AF_INET, SOCK_STREAM)
    tcpSvrSocket.bind(ADDR)
    tcpSvrSocket.listen(5)
    
    while True:
        print("Waiting for connect...")
        tcpCliSocket, addr = tcpSvrSocket.accept()
        print(f"...connected from {addr}")
    
        while True:
            data = tcpCliSocket.recv(BUFF)
            if not data:
                break;
    
            data = data.decode('utf-8')
    
            print(f'recive data from client: {data}')
    
            resMsg = f'[{ctime()}] {data}'
            tcpCliSocket.send(bytes(resMsg, 'utf-8'))
    
        tcpCliSocket.close()
    
    tcpSvrSocket.close()
    
  • 客户端代码

    from socket import *
    
    HOST = ''
    PORT = 21567
    BUFF = 1024
    
    ADDR = (HOST, PORT)
    
    tcpCliSocket = socket(AF_INET, SOCK_STREAM)
    tcpCliSocket.connect(ADDR)
    
    while True:
        data = input(">>> ")
        if not data:
            break
    
        tcpCliSocket.send(bytes(data, 'utf-8'))
        data = tcpCliSocket.recv(BUFF)
        if not data:
            break
    
        print(data.decode('utf-8'))
    
    tcpCliSocket.close()
    

说明:socket类实现了__enter____exit__,可以使用with语句自动实现close操作,也即使用with语句可以不用主动调用socket.close()接口

模块说明

  • 常量
    socket模块定义了很多常量,最常使用的如下表
常量名 说明
socket.AF_INET 一种用于ipv6的地址族,一个四元组(host, port, flowinfo, scope_id)表示地址
socket.AF_INET6 一种socket地址族(用于ipv4,也是目前常用的)使用一个(ip, port)元组表示地址
socket.SOCK_STREAM 面向连接(TCP)的socket类型
socket.SOCK_DGRAM 面向无连接(UDP)的socket类型
  • 接口
    socket模块中有一些函数可以使用,但是最常用的还是socket类生成socket对象,以及类中的接口。类原型如下:
    class socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    

常用接口信息如下表

接口名 连接方式 说明
socket.bind(address) TCP/UDP 服务端socket绑定到地址address
socket.listen([backlog]) TCP 启动一个服务器用于接受连接。backlog表示系统允许暂未 accept 的连接数,超过后将拒绝新连接。最低为 0(小于 0 会被置为 0),未指定则自动设为合理的默认值。
socket.accept() TCP 接受一个连接。此 socket 必须绑定到一个地址上并且监听连接。返回一个 (conn, address) 对:其中 conn 是一个新的套接字对象,用于在此连接上收发数据,address 是连接另一端的套接字所绑定的地址。
socket.connect(address) TCP/UDP 连接到 address 处的远程套接字。
socket.recv(bufsize[, flags]) TCP 从套接字接收数据。返回值是一个字节对象,表示接收到的数据。bufsize 指定一次接收的最大数据量。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。
socket.send(bytes[, flags]) TCP 发送数据给套接字。本套接字必须已连接到远程套接字。可选参数 flags 的含义与上述 recv() 中的相同。
socket.recvfrom(bufsize[, flags]) UDP 从套接字接收数据。返回值是一对 (bytes, address),其中 bytes 是字节对象,表示接收到的数据,address 是发送端套接字的地址。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。
socket.sendto(bytes, address) UDP 发送数据给套接字。由 address 指定目标套接字。可选参数 flags 的含义与上述 recv() 中的相同。
  • 异常
异常名 说明
socket.error 一个被弃用的 OSError 的别名。
socket.herror OSError的子类,本异常通常表示与地址相关的错误。
socket.gaierror OSError的子类,来自 getaddrinfo()getnameinfo(),表示与地址相关的错误。附带的值是一对 (error, string),代表库调用返回的错误。
socket.timeout OSError的子类,当套接字发生超时,且事先已调用过 settimeout()(或隐式地通过 setdefaulttimeout())启用了超时,则会抛出此异常。

SocketServer模块

socketserver模块是一个用于网络服务器编写的框架,简化了编写网络服务器的任务。例如上面TCP服务器端代码可以使用此模块改写如下:

import socketserver

class MyTCPHandler(socketserver.StreamRequestHandler):

    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 21567

    # Create the server, binding to localhost on port 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        server.serve_forever()

模块说明

常用实体服务器类:

  • TCPServer类,该类使用互联网 TCP 协议,它可以提供客户端与服务器之间的连续数据流。函数原型如下:
    class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
  • UDPServer类,该类使用数据包,即一系列离散的信息分包,它们可能会无序地到达或在传输中丢失。函数原型如下:
    class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

请求处理句柄对象:

  • BaseRequestHandler类,这是所有请求处理句柄对象的超类。 它定义了下文列出的接口。 一个实体请求处理句柄子类必须定义新的 handle()方法,并可重载任何其他方法。 对于每个请求都会创建一个新的子类的实例。

    接口名 说明
    setup() 会在 handle() 方法之前被调用以执行任何必要的初始化操作。 默认实现不执行任何操作。
    handle() 此函数必须执行为请求提供服务所需的全部操作。 默认实现不执行任何操作。 它有几个可用的实例属性;请求为 self.request;客户端地址为 self.client_address;服务器实例为 self.server,如果它需要访问特定服务器信息的话。
    finish() handle()方法之后调用以执行任何需要的清理操作。 默认实现不执行任何操作。
  • StreamRequestHandler和DatagramRequestHandler类,是BaseRequestHandler的子类,重载了 setup()finish()方法,并提供了 self.rfile(读取以获取请求数据) 和 self.wfile(写入以将数据返回给客户端) 属性。

当然此框架还提供了更加强大的功能,具体可以参考官方文档

相关文章

网友评论

      本文标题:python学习笔记之--网络编程

      本文链接:https://www.haomeiwen.com/subject/ethsjrtx.html