python入门:套接字通信

136 阅读4分钟

在互联网通信中,主要通过确定对方计算机的IP地址和对应开放的端口号来进行对接;IP地址用来标识网络上一台主机,端口号用来标识主机上联网的进程,IP地址和端口号的组合称为套接字(Socket);双方通过某种约定好的传输协议进行通信,可以理解为某种数据传输格式。本文主要介绍利用Python实现TCP协议通信

TCP协议通信

基本函数

  1. 双方导入套接字包:import socket
  2. 双方创建套接字对象:s=socket(family, type),参数family=socket.AF_INET表示使用IPv4地址,family=socket.AF_INET6时表示使用IPv6地址;参数type=socket.SOCK_STREAM表示使用TCP协议,type=socket.SOCK_DGRAM表示使用UDP协议。套接字对象支持with关键字with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s
  3. 服务器端绑定套接字:s.bind(address),把套接字绑定到本地地址,address为服务器地址,形式为元组(host, port)
  4. 服务器端监听套接字:s.listen(backlog),声明自己为服务器端的监听套接字或被动套接字,开始监听并准备好接收客户端的连接请求,参数backlog表示能够同时连接的客户端数量,默认为1
  5. 客户端连接套接字:s.connect(address),address为服务器端地址,形式为(host, port)
  6. 服务器端接收套接字:conn, addr=s.accept()接收一个客户端的连接,返回元组(conn, addr),其中conn是可以实际用于收发数据的新套接字,addr是对方套接字地址,形式为(host, port)
  7. 发送字节串:
  1. s.send(data):向已连接的远程套接字发送字节串data,返回实际发送的字节串长度
  2. s.sendto(data, address):向参数address指定的套接字发送字节串,返回实际发送的字节串长度,其中参数address的格式为(hostaddr, port)
  3. s.sendall(data):向已连接的远程套接字发送字节串data,自动重复调用send()方法,确保data全部发送完成
  1. 读取字节串:
  1. recv(buffersize),从套接字中读取并返回最多buffersize个字节,如果对方已关闭并且已读取完缓冲区内所有数据,返回空字节串
  2. recvfrom(buffersize):从套接字接收并返回形式为(data, address)的元组,其中data为实际接收的完整报文的字节串数据,address为发送端的地址,格式为(hostaddr, port)
  3. recv_into(buffer, nbytes):从套接字读取最多nbytes个字节直接写入缓冲区,返回实际接收并写入缓冲区的字节串长度
  1. 关闭套接字:s.close(),把输出缓冲区中剩余的数据传输完成之后,关闭套接字

JSON

说到字节串,常用方式是将所需传输对象转换成json格式 JSON 是一种轻量级的文本数据交换格式,指的是 JavaScript 对象表示法,通过导入json库进行使用 json.dumps(data).encode()将data的数据转换成json字符串再编码为二进制格式 json.loads(data.decode())将所得二进制数据解码成json字符串再转换为python对象

基本流程

服务器端(server)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # 创建套接字
    s.bind((host, port)) # 绑定本地套接字(IP地址+端口号)
    s.listen() # 开始监听客户端的连接请求
    conn, addr = s.accept() # 监听到客户端请求时接受连接
    with conn:
        print('Connected by', addr) # 可以输出一下客户端的地址
        while True: # 保持一直执行
        json_data = json.dumps(data).encode() # 数据转码
        data_len = len(json_data) # 计算长度
        conn.sendall(str(data_len).zfill(10).encode()) # 先发送数据长度,便于对方判断接收
        conn.sendall(json_data) # 发送数据

客户端(client)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # 创建套接字
        s.connect((host, port)) # 连接服务器套接字
        while True: # 保持一直执行
            length_data = s.recv(10) # 先接收代表数据长度的数据
            if not length_data: 
                break
            data_length = int(length_data.decode()) # 转码为整数,得到字符数
            data = b'' # 准备保存数据
            while len(data) < data_length: # 因为读取数据不一定能成功读取到所设字符数
                received_data = s.recv(data_length - len(data)) # 所以设置循环直到全部读完
                if not received_data:
                    break
                data += received_data # 组装数据
            data = json.loads(data.decode()) # 数据解码

其他函数

socket.inet_aton(string):把圆点分隔数字形式的IPv4地址字符串转换为二进制形式 socket.inet_ntoa(packed_ip):把32位二进制格式的IPv4地址转换为圆点分隔数字的字符串格式 socket.setblocking(flag):设置套接字为阻塞模式(flag=True)或非阻塞模式(flag=False) socket.getblocking():查看套接字是否处于阻塞模式,对于非阻塞模式的套接字,调用accept()和recv()方法时如果没有数据会抛出异常。新创建的套接字默认为阻塞模式 socket.getpeername():返回套接字对象连接的另一端地址,可以用于获取远程套接字的IP地址和端口号 socket.getsockname():返回套接字对象的本地地址,可以用于获取系统随机分配给本地套接字的端口号 socket.gethostname():返回计算机名 socket.gethostbyname(host):根据计算机名获取并返回对应的IP地址 socket.gethostbyaddr(host):根据IP地址或计算机名,返回包含计算机名、别名和IP地址列表的元组