Python 网络编程之 TCP 与 UDP

Python 网络编程之 TCP 与 UDP

Socket(套接字)

介绍 UDP 和 TCP 协议之前,先熟悉下 socket 的基本概念。

  • 基本概念

Socket 是通信的基石,是支持 TCP/IP 协议的网络通信的基本操作单元,在网络通信过程中端点的一种抽象表示。网络中使用 Socket 传输数据是一种特殊的网络 I/O。

  • 工作模式

打开open -> 读写write/read -> 关闭close

  • 五种信息

socket 包括了数据传输必须的五元组,分别为源IP、源端口、目的IP、目的端口和协议号

  • 通信机制

基于流(stream)或者基于数据报(datagram)

  • python 中使用
import socket

socket.socket(...)

UDP

UDP(User Datagram Protocol) 用户数据报协议,是一种面向无连接的协议,提供简单不可靠的信息传输服务,发送后不会确认信息是否到达。

UDP 通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中"写信"。

  • 使用 socket 使用 UDP 的收发数据,先看原理图

  • 客户端代码实现
# 客户端 发送数据
# 1.创建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
    send_data = input("请输入要发送的数据:")
    if send_data == 'exit':
        break
    # 2.发送数据
    udp_socket.sendto(send_data.encode(), ("192.168.1.110", 8080)) # 对方的ip和port
    # 3.关闭套接字
udp_socket.close()
  • 服务端代码实现
# 服务端 接收数据
HOST = '' # 空为localhost
PORT = 7788
ADDR = (HOST, PORT)
BUFSIZE = 1024  # 一次接收最大的尺寸

# 1.创建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.绑定本地套接字信息
udp_socket.bind(ADDR)  # 绑定必须是本机的ip和port
while True:
    # 3.接收数据
    data, addr = udp_socket.recvfrom(BUFSIZE)  # 返回接收数据和发送方的地址信息
    if data.decode() == 'exit':
        print("退出")
        break
    # 打印接收的数据
    print(f"{addr[0]}:{addr[1]}", data.decode())
# 4.关闭套接字
udp_socket.close()

套接字是可以同时收发数据,是全双工状态,在调用 recvform 之前收到数据,操作系统会将数据暂时储存起来,等到程序调用 recvform 取出数据,在调用 recvform 之后未接收到数据,程序会进入阻塞状态,等待数据的到来。

TCP

TCP(Transmission Control Protocol)传输控制协议,是一种面向连接的,可靠的基于字节流的传输层通信协议。

TCP 通信需要经过创建连接数据传送终止连接三个步骤。

TCP 通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中"打电话"。 这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用 UDP 协议

特点:

  • 面向连接(使用三次握手建立连接,连接已创建才作传输,采用四次挥手断开连接。)

  • 有序数据传输
  • 重发丢失的数据包
  • 舍弃重复的数据包
  • 无差错的数据传输
  • 阻塞/流量控制

使用 socket 实现 TCP 的收发数据,先看原理图

  • 客户端代码实现
# 客户端
IPADDR = "192.168.1.110"
PORT = 7788
# 创建套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器,创建连接
tcp_socket.connect((IPADDR, PORT))
# 收发数据
send_data = input("输入发送的数据:")
tcp_socket.send(send_data.encode())
# 关闭套接字,关闭连接
tcp_socket.close()
  • 服务端代码实现
# 服务端

IPADDR = “”  # 默认localhost
PORT = 7788
BUFSIZE = 1024  # 一次接收最大的尺寸
print('server start...')
# 创建套接字(监听套接字)  =》买个手机
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定本地信息 =》 插入手机卡
tcp_socket.bind((IPADDR, PORT))
# 将手机设为正常,能够响铃 =》 让默认的套接字由主动变为监听
tcp_socket.listen(128)  # 同一时间,最大允许连接128个客户端

while True:  # 循环为多个客户端服务
    print("waitting connected...")
    # 等待电话到来 =》 等待别人电话的到来,此时程序会被阻塞,等待客户端连接
    new_client_socket, client_addr = tcp_socket.accept()  # 新的专门为客户端服务的socket,客户端的addr
    # 接收客户端发送过来的请求,此时程序会阻塞等待客户端发送数据
    print("waitting recive data...")
    while True:  # 循环为同一个客户端服务多次
        recv_data = new_client_socket.recv(BUFSIZE)
        # 如果recv 解堵塞,有两种方式
        # 1.客户端发送过来数据
        # 2.客户端调用close断开连接
        if recv_data:
            print(f"【{client_addr[0]}:{client_addr[1]}】", recv_data.decode())
            # 回送一部分数据给客户端
            new_client_socket.send('收到'.encode())
        else:
            break
        # 关闭套接字
    new_client_socket.close()
    print("connect closed")
tcp_socket.close()
print('server end...')

监听套接字负责等待有新的客户端进行连接 accept 产生的新的套接字用来为客户端服务 如果将监听套接字关闭了,那么会导致不能再次等待新客户端的到来,accept() 会失败 关闭 accept 返回的新的套接字,意味着不再为这个客户端服务。