Python Socket编程:从原理到实践

在当今的网络世界中,Socket编程是构建网络通信应用的关键技术之一。从简单的聊天应用到复杂的分布式系统,Socket编程都扮演着至关重要的角色。本文将首先介绍Socket编程的基本原理,然后详细讲解如何使用Python进行Socket编程。

注意:文中谈到的TCP/IP协议可以去我另一篇文章看一下,本文主要讲我们怎么进行socket编程。

一、Socket编程原理

Socket编程是一种网络通信的API,它提供了跨平台、跨语言的网络通信能力。Socket编程接口实际上是一个广泛的编程接口,可以用于多种协议,包括TCP、UDP以及其他更底层的协议(如原始套接字用于IP层通信)。但是在大多数应用中,TCP和UDP是最常用的协议(因为它们提供了网络编程所需的基本特性),所以本文主要讲TCP Socket 编程和UDP Socket 编程。

TCP Socket 编程

当我们在使用Socket API进行编程时,我们写的代码实际上是在与TCP/IP协议栈进行交互。

具体流程如下:

  1. 创建Socket:当您创建一个Socket对象时,您实际上是在请求TCP/IP协议栈分配一个唯一的套接字标识符(socket ID)和相关的资源。
  2. 绑定地址:绑定操作告诉TCP/IP协议栈,您希望将哪个IP地址和端口号与您的Socket关联起来。这样,当远程主机尝试连接到您的Socket时,TCP/IP协议栈就知道如何处理这个连接请求。
  3. 监听连接:服务器端Socket的监听操作告诉TCP/IP协议栈,它应该接受来自客户端的连接请求。当有客户端发起连接时,TCP/IP协议栈会处理底层的连接建立过程,并通知您的代码连接已经建立。
  4. 数据交换:当您使用Socket发送或接收数据时,您的代码实际上是在将数据传递给TCP/IP协议栈,或者从TCP/IP协议栈接收数据。TCP/IP协议栈负责将数据打包成适当的格式(例如,TCP数据包或UDP数据报),并将其发送到目标主机。同样,当TCP/IP协议栈接收到来自远程主机的数据时,它会将数据传递给与Socket关联的代码。
  5. 关闭连接:当您关闭Socket连接时,您的代码实际上是在告诉TCP/IP协议栈释放与该连接关联的所有资源。TCP/IP协议栈会负责完成底层的连接关闭过程。

UDP Socket 编程

当使用Socket进行UDP(用户数据报协议)通信时,原理与TCP通信有所不同,因为UDP是一个无连接、不可靠的传输协议。

具体流程如下:

  1. 创建Socket
    在通信的两端(客户端和服务器),都需要创建一个UDP Socket对象。这个对象将负责UDP通信的相关操作。
  2. 绑定地址(可选)
    对于服务器端Socket,通常也需要绑定到一个具体的IP地址和端口号上,以便客户端能够找到它。但需要注意的是,UDP的绑定是可选的,如果服务器端不绑定到特定端口,那么操作系统会为其选择一个可用的临时端口。
    客户端Socket则不需要绑定,因为UDP是无连接的,客户端只需要知道服务器的IP地址和端口号就可以发送数据报。
  3. 发送数据报
    客户端使用UDP Socket的sendto方法向服务器发送数据报。在发送时,需要指定目标服务器的IP地址和端口号。
    服务器使用UDP Socket的recvfrom方法来接收来自客户端的数据报。这个方法会返回发送方的IP地址和端口号,以及接收到的数据内容。
  4. 数据交换
    UDP通信中,数据以数据报的形式发送和接收。每个数据报都是独立的,不依赖于之前的或之后的数据报。
    UDP不提供像TCP那样的流控制、错误检查和顺序保证。这意味着数据报可能会丢失、乱序或重复到达。因此,在UDP通信中,应用程序需要负责处理这些问题。
  5. 关闭连接
    与TCP不同,UDP没有显式的连接建立和关闭过程。当数据报发送或接收完成后,Socket对象通常可以继续使用,或者可以被销毁以释放资源。
  6. 广播和多播
    UDP还支持广播和多播功能。广播是指向本地网络上的所有主机发送数据报,而多播则是向一组特定的主机(属于同一个多播组)发送数据报。这些功能在需要向多个接收者发送相同数据的情况下非常有用。
  7. 缓冲区管理
    UDP Socket有一个接收缓冲区来存储接收到的数据报。如果应用程序没有及时读取接收缓冲区中的数据,那么新的数据报可能会覆盖旧的数据报(取决于缓冲区的大小和配置)。因此,在使用UDP时,应用程序需要确保及时读取接收到的数据报。

二、Python Socket编程实践

Python提供了丰富的Socket编程库,使得开发者可以轻松地实现网络通信功能。

基于TCP协议的Python socket编程

TCP 服务器
import socket


def tcp_server():
    # 创建一个TCP/IP socket
    # 参数 AF_INET 表示该socket网络层使用IP协议
    # 参数 SOCK_STREAM 表示该socket传输层使用TCP协议
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 绑定到地址和端口
    server_address = ('localhost', 12345)
    sock.bind(server_address)

    # 监听连接,等待客户端的连接请求
    # 最多接受2个等待连接的客户端
    sock.listen(2)
    print('等待连接...')
    connection, client_address = sock.accept()
    print(f"接受一个客户端连接 {client_address}")

    # 接收数据并返回
    while True:
        # 指定从接收缓冲里最多读取100个字节
        recv_data = connection.recv(100)   # 返回的数据是bytes类型的,需要用decode()解码
        print(f"收到信息: {recv_data.decode()}")
        if recv_data.decode() == "退出":
            break

        send_data = input("输入发送给客户端的数据: ")
        connection.send(send_data.encode())   # 发送的数据也是bytes类型的,所以这里需要encode()编码
        if send_data == "退出":
            break

    connection.close()
    sock.close()


if __name__ == '__main__':
    tcp_server()
TCP 客户端
import socket


def tcp_client():
    # 创建一个TCP/IP socket
    # 参数 AF_INET 表示该socket网络层使用IP协议
    # 参数 SOCK_STREAM 表示该socket传输层使用TCP协议
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 连接到服务器
    server_address = ('localhost', 12345)
    sock.connect(server_address)

    while True:
        # 发送数据
        # send_data = b'123'
        send_data = input("输入发送的数据: ")
        sock.send(send_data.encode())
        if send_data == "退出":
            break

        # 接收数据
        recv_data = sock.recv(100)
        print(f"接收到的数据 {recv_data.decode()}")
        if recv_data.decode() == "退出":
            break

    sock.close()


if __name__ == '__main__':
    tcp_client()

基于UDP协议的Python socket编程

UDP 服务器
import socket


def udp_server():
    # 创建一个UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 绑定到地址和端口
    server_address = ('localhost', 12345)
    sock.bind(server_address)

    while True:
        # 接收数据
        # print('等待接收数据...')
        recv_data, address = sock.recvfrom(4096)
        print(f"地址: {address} 数据: {recv_data.decode()}")
        if recv_data.decode() == "退出":
            break

        # 发送数据
        send_data = input("请输入要发送的数据: ")
        sent = sock.sendto(send_data.encode(), address)
        print(f"向{address}发送{sent}字节")
        if send_data == "退出":
            break

    sock.close()


if __name__ == '__main__':
    udp_server()
UDP 客户端
import socket


def udp_client():
    # 创建一个UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 服务器地址和端口
    server_address = ('localhost', 12345)

    while True:
        # 发送数据
        # send_data = b'123'  # 这种方式是不能写中文的,所以还是建议用encode()
        send_data = input("请输入要发送的数据: ")
        sent = sock.sendto(send_data.encode(), server_address)

        # 接收数据
        # print('等待接收数据...')
        recv_data, server = sock.recvfrom(4096)
        print(f"地址: {server} 数据: {recv_data.decode()}")
        if recv_data.decode() == "退出":
            break

    sock.close()


if __name__ == '__main__':
    udp_client()

相关推荐

  1. Python Socket编程原理实践

    2024-06-05 20:50:03       11 阅读
  2. 深入探索Python异步编程原理实践

    2024-06-05 20:50:03       17 阅读
  3. 事件相机角点检测:原理演示编程

    2024-06-05 20:50:03       40 阅读
  4. Python编程:入门实践(第二版)

    2024-06-05 20:50:03       27 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-05 20:50:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-05 20:50:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-05 20:50:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-05 20:50:03       20 阅读

热门阅读

  1. MacOS下Python开发环境的深度构建与优化

    2024-06-05 20:50:03       10 阅读
  2. 爆字段名中select * from users as a join users解释

    2024-06-05 20:50:03       10 阅读
  3. lllllll

    2024-06-05 20:50:03       8 阅读
  4. 安卓手机APP开发___设置闹钟

    2024-06-05 20:50:03       10 阅读
  5. 外界的声音都是参考,你不开心就不要参考

    2024-06-05 20:50:03       13 阅读
  6. 网络协议学习笔记

    2024-06-05 20:50:03       11 阅读