网络编程是实现跨设备数据交互的核心技术,爬虫、后端服务、即时通信、数据传输均高度依赖。
本文由浅入深讲解Socket通信原理、TCP/UDP协议实战,从单线程TCP入门,再到多线程并发服务,手把手搭建多人聊天室。

一、网络编程核心基础

1. 基础概念

  • IP地址:网络中主机唯一标识,分为内网IP、公网IP
  • 端口号:0~65535,用于区分同一主机内不同应用程序
  • Socket(套接字):网络通信的端点,是实现数据收发的工具
  • TCP/UDP:互联网最核心的两种传输层协议

2. TCP 与 UDP 深度对比

维度 TCP UDP
连接 面向连接(三次握手/四次挥手) 无连接
可靠性 可靠传输,无丢失、无重复、有序 不可靠,可能丢包、乱序
效率 较低,开销大 极高,开销极小
适用场景 文件传输、接口请求、登录注册 直播、游戏、语音、广播

二、单线程 TCP 编程

单线程 TCP 是网络编程入门核心,一次只能处理一个客户端

1. 单线程 TCP 服务端

import socket

# 1. 创建 TCP 套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 端口复用(解决重启端口占用)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 3. 绑定 IP 和端口
server_socket.bind(("127.0.0.1", 8888))
# 4. 监听连接
server_socket.listen(5)
print("单线程TCP服务已启动,等待客户端连接...")

# 5. 阻塞等待客户端连接
client_socket, addr = server_socket.accept()
print(f"客户端 {addr} 已连接")

# 6. 循环收发消息
while True:
    # 接收数据
    recv_data = client_socket.recv(1024).decode("utf-8")
    # 客户端断开
    if not recv_data:
        break
    print(f"客户端:{recv_data}")

    # 回复数据
    send_data = input("服务端:")
    client_socket.send(send_data.encode("utf-8"))

# 7. 关闭资源
client_socket.close()
server_socket.close()

2. 单线程 TCP 客户端

import socket

# 1. 创建客户端套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务端
client_socket.connect(("127.0.0.1", 8888))

# 3. 循环收发消息
while True:
    # 发送消息
    send_msg = input("我:")
    client_socket.send(send_msg.encode("utf-8"))

    # 接收消息
    recv_msg = client_socket.recv(1024).decode("utf-8")
    if not recv_msg:
        break
    print(f"服务端:{recv_msg}")

# 4. 关闭
client_socket.close()

单线程 TCP 缺点

  • 只能连接一个客户端
  • 收发消息会互相阻塞

三、多线程 TCP 并发服务端

解决单线程阻塞问题,一个服务端 → 同时处理多个客户端,是聊天室、服务器标准架构。

import socket
import threading

# 保存所有在线客户端
clients = []

# 处理单个客户端连接
def handle_client(client_socket, addr):
    print(f"新客户端接入:{addr}")
    clients.append(client_socket)

    while True:
        try:
            data = client_socket.recv(1024).decode("utf-8")
            if not data:
                break
            print(f"{addr}{data}")
            # 广播给所有人
            broadcast(f"客户端{addr}{data}", client_socket)
        except:
            break

    # 客户端退出清理
    clients.remove(client_socket)
    client_socket.close()
    print(f"客户端 {addr} 已退出")

# 广播消息(发给所有客户端)
def broadcast(msg, exclude_socket=None):
    for client in clients:
        if client != exclude_socket:
            try:
                client.send(msg.encode("utf-8"))
            except:
                clients.remove(client)

# 启动服务
def run_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(("127.0.0.1", 8888))
    server.listen(10)
    print("多线程聊天室服务已启动,等待客户端连接...")

    while True:
        client_sock, addr = server.accept()
        # 每来一个客户端,开一个线程
        t = threading.Thread(target=handle_client, args=(client_sock, addr))
        t.start()

if __name__ == '__main__':
    run_server()

四、TCP 聊天室客户端

支持收发消息不阻塞,一边收消息一边发消息。

import socket
import threading

def receive_msg(client):
    while True:
        try:
            msg = client.recv(1024).decode("utf-8")
            print("\n" + msg)
        except:
            print("与服务器断开连接")
            client.close()
            break

def send_msg(client):
    while True:
        content = input("我:")
        client.send(content.encode("utf-8"))

def run_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 8888))

    # 收消息线程
    t1 = threading.Thread(target=receive_msg, args=(client,))
    t1.start()
    # 发消息主线程
    send_msg(client)

if __name__ == '__main__':
    run_client()

五、UDP 网络编程

无需连接,直接收发数据报,适合实时性要求高的场景。

UDP 服务端

import socket
udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_server.bind(("127.0.0.1", 9999))
print("UDP服务端已启动")

while True:
    data, addr = udp_server.recvfrom(1024)
    print(f"{addr}{data.decode()}")
    udp_server.sendto(b"消息已接收", addr)

UDP 客户端

import socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
    msg = input("发送:")
    client.sendto(msg.encode(), ("127.0.0.1", 9999))

六、网络编程高频难点

1. 粘包问题

  • 原因:TCP 是流式协议,数据无边界,会自动合并小包
  • 解决方案
    1. 消息长度 + 消息内容
    2. 自定义分隔符
    3. 固定长度报文

2. 端口复用

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

3. 多线程 + Socket 优势

  • 支持多客户端同时在线
  • 收发消息互不阻塞
  • 即时通讯、游戏服务器、客服系统标准架构

全文总结

本文从单线程TCP基础入门,逐步进阶到多线程并发聊天室,深入讲解Socket、TCP/UDP、广播、粘包、端口复用等核心要点。

更多推荐