【第八章】Python Socket 网络编程详解:从基础到实践​

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

Python Socket 网络编程详解:从基础到实践​



前言

在网络通信的世界里,Socket 就像一座桥梁,连接着不同的计算机,让数据能够在它们之间顺畅传递。Python 作为一门简洁而强大的编程语言,其内置的 socket 模块为网络编程提供了便捷的接口。本文将详细介绍 Python socket 网络编程的方方面面,通过多样的呈现方式和丰富的示例,帮助你深入理解并熟练运用这一技术。​


一、Socket 基础认知​

1.1 什么是 Socket​

Socket,通常被翻译为 “套接字”,是计算机网络中进行通信的端点。它就像一个插座,一端插在本地计算机上,另一端插在远程计算机上,通过这个 “插座”,两台计算机可以进行数据交换。在 Python 中,我们可以使用 socket 模块来创建和操作 Socket。​

1.2 Socket 的工作流程​

无论是客户端还是服务器,使用 Socket 进行通信都遵循一定的工作流程:​

  • 服务器端:创建 Socket -> 绑定 IP 地址和端口 -> 监听连接 -> 接受连接 -> 收发数据 -> 关闭连接​
  • 客户端:创建 Socket -> 连接服务器 -> 收发数据 -> 关闭连接​

1.3 Socket 的类型​

Python 中常用的 Socket 类型有两种:​

  • SOCK_STREAM:面向连接的 Socket,基于 TCP 协议。它提供可靠的、有序的、双向的字节流传输服务,在通信前需要建立连接,通信结束后需要关闭连接。​
  • SOCK_DGRAM:无连接的 Socket,基于 UDP 协议。它不保证数据的可靠传输和有序到达,但传输速度较快,适用于对实时性要求较高的场景。​

二、TCP Socket 编程​

TCP(传输控制协议)是一种面向连接的、可靠的传输层协议。下面通过具体示例来介绍 TCP Socket 编程的实现。​

2.1 TCP 服务器实现​

步骤解析:​

  • 创建 TCP Socket 对象,指定地址族为 AF_INET(IPv4),类型为 SOCK_STREAM。​
  • 使用 bind () 方法绑定服务器的 IP 地址和端口号,IP 地址可以是本地地址(localhost或 127.0.0.1),端口号选择 1024-65535 之间的未被占用端口。​
  • 使用 listen () 方法开始监听客户端的连接请求,参数指定最大等待连接数。​
  • 进入循环,使用 accept () 方法接受客户端的连接,该方法会返回一个新的 - Socket 对象(用于与该客户端通信)和客户端的地址。​
  • 通过新的 Socket 对象与客户端进行数据收发,使用 recv () 方法接收数据,send () 或 sendall () 方法发送数据。​
    通信结束后,关闭与客户端通信的 Socket 和服务器 Socket。​

示例代码:​

import socket

# 创建TCP Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定IP和端口
server_address = ('localhost', 8888)
print(f'服务器绑定到 {server_address[0]}:{server_address[1]}')
server_socket.bind(server_address)

# 监听连接,最大等待5个连接
server_socket.listen(5)

try:
    while True:
        print('等待客户端连接...')
        # 接受客户端连接
        client_socket, client_address = server_socket.accept()
        try:
            print(f'接收到来自 {client_address} 的连接')
            
            # 接收客户端数据
            data = client_socket.recv(1024)
            print(f'收到数据: {data.decode("utf-8")}')
            
            # 发送响应数据
            response = '已收到你的消息,谢谢!'
            client_socket.sendall(response.encode('utf-8'))
        finally:
            # 关闭客户端连接
            client_socket.close()
            print(f'与 {client_address} 的连接已关闭')
finally:
    # 关闭服务器Socket
    server_socket.close()
    print('服务器已关闭')

2.2 TCP 客户端实现​

步骤解析:​

  • 创建 TCP Socket 对象,同样指定地址族和类型。​
  • 使用 connect () 方法连接服务器,参数为服务器的 IP 地址和端口号。​
  • 与服务器进行数据收发。​
  • 通信结束后,关闭 Socket。​

示例代码:​

import socket

# 创建TCP Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
server_address = ('localhost', 8888)
print(f'连接到服务器 {server_address[0]}:{server_address[1]}')
client_socket.connect(server_address)

try:
    # 发送数据
    message = 'Hello, Server! 我是客户端'
    print(f'发送数据: {message}')
    client_socket.sendall(message.encode('utf-8'))
    
    # 接收服务器响应
    data = client_socket.recv(1024)
    print(f'收到服务器响应: {data.decode("utf-8")}')
finally:
    # 关闭Socket
    client_socket.close()
    print('客户端已关闭连接')

2.3 TCP 通信演示​

  • 先运行 TCP 服务器代码,服务器会开始监听连接。​
  • 再运行 TCP 客户端代码,客户端会连接到服务器并发送消息。​
  • 服务器收到消息后会发送响应,客户端收到响应后关闭连接,服务器也会关闭与该客户端的连接并继续等待新的连接。​

三、UDP Socket 编程​

UDP(用户数据报协议)是一种无连接的、不可靠的传输层协议。它不需要像 TCP 那样建立连接,直接发送数据报,适合对实时性要求高但对可靠性要求不高的场景,如视频直播、语音通话等。​

3.1 UDP 服务器实现​

步骤解析:​

  • 创建 UDP Socket 对象,指定地址族为 AF_INET,类型为 SOCK_DGRAM。​
  • 使用 bind () 方法绑定服务器的 IP 地址和端口号。​
  • 进入循环,使用 recvfrom () 方法接收客户端发送的数据报,该方法会返回数据和客户端的地址。​
  • 使用 sendto () 方法向客户端发送响应数据报,需要指定客户端的地址。​
  • 通信结束后,关闭服务器 Socket。​

示例代码:​

import socket

# 创建UDP Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定IP和端口
server_address = ('localhost', 9999)
print(f'服务器绑定到 {server_address[0]}:{server_address[1]}')
server_socket.bind(server_address)

try:
    while True:
        print('等待客户端数据...')
        # 接收客户端数据和地址
        data, client_address = server_socket.recvfrom(1024)
        print(f'收到来自 {client_address} 的数据: {data.decode("utf-8")}')
        
        # 发送响应数据
        response = '已收到你的数据'
        server_socket.sendto(response.encode('utf-8'), client_address)
        print(f'已向 {client_address} 发送响应')
finally:
    # 关闭服务器Socket
    server_socket.close()
    print('服务器已关闭')

3.2 UDP 客户端实现​

步骤解析:​

  • 创建 UDP Socket 对象。​
  • 直接使用 sendto () 方法向服务器发送数据报,需要指定服务器的地址。​
  • 使用 recvfrom () 方法接收服务器的响应数据报。​
  • 通信结束后,关闭 Socket。​

示例代码:​

import socket

# 创建UDP Socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

server_address = ('localhost', 9999)
message = 'Hello, UDP Server!'

try:
    # 发送数据
    print(f'发送数据: {message}{server_address}')
    sent = client_socket.sendto(message.encode('utf-8'), server_address)
    
    # 接收响应
    print('等待响应...')
    data, server = client_socket.recvfrom(1024)
    print(f'收到来自 {server} 的响应: {data.decode("utf-8")}')
finally:
    print('客户端关闭')
    client_socket.close()

四、Socket 编程高级应用​

4.1 多线程处理多个客户端连接​

在 TCP 服务器中,默认情况下一次只能处理一个客户端连接。为了能同时处理多个客户端,可以使用多线程技术。​

示例代码:​

import socket
import threading

def handle_client(client_socket, client_address):
    try:
        print(f'新线程处理来自 {client_address} 的连接')
        data = client_socket.recv(1024)
        print(f'收到 {client_address} 的数据: {data.decode("utf-8")}')
        response = '已收到消息,正在处理...'
        client_socket.sendall(response.encode('utf-8'))
    finally:
        client_socket.close()
        print(f'与 {client_address} 的连接已关闭')

# 创建TCP Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 8888)
server_socket.bind(server_address)
server_socket.listen(5)

print(f'多线程服务器启动,监听 {server_address}')

try:
    while True:
        client_socket, client_address = server_socket.accept()
        # 创建线程处理客户端
        client_thread = threading.Thread(
            target=handle_client,
            args=(client_socket, client_address)
        )
        client_thread.start()
finally:
    server_socket.close()

4.2 带超时设置的 Socket​

在网络通信中,有时需要设置超时时间,避免程序一直阻塞在等待数据的状态。可以使用 settimeout () 方法为 Socket 设置超时时间。​

示例代码(客户端):​

import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.settimeout(5)  # 设置超时时间为5秒

server_address = ('localhost', 8888)

try:
    client_socket.connect(server_address)
    client_socket.sendall('Hello, Server!'.encode('utf-8'))
    data = client_socket.recv(1024)
    print(f'收到数据: {data.decode("utf-8")}')
except socket.timeout:
    print('连接或接收数据超时')
finally:
    client_socket.close()

五、Socket 编程常见问题及解决方法​

5.1 地址已被使用(Address already in use)​

当关闭服务器后立即重新启动,可能会遇到 “Address already in use” 的错误。这是因为关闭 Socket 后,端口不会立即释放,处于 TIME_WAIT 状态。可以通过设置 SO_REUSEADDR 选项来解决。​

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)​
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 允许端口重用​

5.2 数据粘包问题​

在 TCP 通信中,由于 TCP 是流式传输,可能会出现数据粘包的问题,即多个数据包被合并成一个接收。解决方法可以是在数据前加上数据长度,或者使用分隔符等。​

示例(使用分隔符):​

# 发送方
message = 'Hello|World|Python'
client_socket.sendall(message.encode('utf-8'))

# 接收方
data = client_socket.recv(1024).decode('utf-8')
messages = data.split('|')
for msg in messages:
    print(msg)

总结​

Python socket 网络编程是实现网络通信的基础,通过本文的介绍,我们了解了 Socket 的基本概念、TCP 和 UDP 两种通信方式的实现,以及一些高级应用和常见问题的解决方法。​

在实际开发中,还需要考虑网络异常处理、数据加密、并发控制等问题。希望本文能为你打开 Python socket 网络编程的大门,通过不断实践和探索,掌握更多网络编程技巧。​

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值