← 返回首页
🌐

网络编程入门:Socket、TCP/UDP与客户端/服务器模型

📂 python ⏱ 3 min 462 words

网络编程入门:Socket、TCP/UDP与客户端/服务器模型

网络编程是现代软件开发的核心技能之一。Python内置的socket模块提供了底层网络通信能力,让你能够构建客户端和服务器应用。本文将带你从零开始掌握网络编程基础。

什么是Socket

Socket(套接字)是网络通信的端点,是应用程序与网络协议之间的接口。通过Socket,不同主机上的应用程序可以互相发送和接收数据。Python的socket模块封装了底层的网络操作,提供了简洁的API。

import socket

# 查看本机主机名
hostname = socket.gethostname()
print(f"主机名: {hostname}")

# 获取本机IP地址
ip_address = socket.gethostbyname(hostname)
print(f"IP地址: {ip_address}")

TCP与UDP的区别

TCP(传输控制协议)和UDP(用户数据报协议)是两种最主要的传输层协议:

特性 TCP UDP
连接方式 面向连接,三次握手 无连接
可靠性 可靠传输,保证顺序 不保证可靠
速度 较慢 较快
适用场景 网页、文件传输、邮件 视频流、游戏、DNS查询

TCP客户端编程

TCP客户端需要先连接到服务器,然后才能发送和接收数据:

import socket

def tcp_client():
    # 创建TCP套接字
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    try:
        # 连接服务器
        host = "www.example.com"
        port = 80
        client_socket.connect((host, port))
        print(f"已连接到 {host}:{port}")
        
        # 发送HTTP请求
        request = f"GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n"
        client_socket.send(request.encode())
        
        # 接收响应
        response = b""
        while True:
            data = client_socket.recv(1024)
            if not data:
                break
            response += data
        
        print(f"收到响应,长度: {len(response)} 字节")
        print(response.decode()[:200])
        
    finally:
        client_socket.close()

tcp_client()

TCP服务器编程

TCP服务器需要监听端口,等待客户端连接,然后处理请求:

import socket
import threading

def handle_client(client_socket, address):
    """处理客户端连接"""
    print(f"新连接来自: {address}")
    
    try:
        while True:
            # 接收数据
            data = client_socket.recv(1024)
            if not data:
                break
            
            message = data.decode()
            print(f"收到消息: {message}")
            
            # 回显消息(大写)
            response = message.upper()
            client_socket.send(response.encode())
            
    except ConnectionResetError:
        print(f"客户端 {address} 强制断开")
    finally:
        client_socket.close()
        print(f"连接关闭: {address}")

def tcp_server():
    # 创建服务器套接字
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 设置SO_REUSEADDR,避免"地址已被使用"错误
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
    # 绑定地址和端口
    host = "127.0.0.1"
    port = 8888
    server_socket.bind((host, port))
    
    # 开始监听
    server_socket.listen(5)
    print(f"服务器监听 {host}:{port}")
    
    try:
        while True:
            # 接受新连接
            client_socket, address = server_socket.accept()
            
            # 为每个客户端创建线程
            thread = threading.Thread(
                target=handle_client,
                args=(client_socket, address)
            )
            thread.daemon = True
            thread.start()
            
    except KeyboardInterrupt:
        print("\n服务器关闭")
    finally:
        server_socket.close()

tcp_server()

UDP编程

UDP是无连接的,不需要建立连接就可以直接发送数据:

import socket

def udp_server():
    """UDP服务器"""
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    host = "127.0.0.1"
    port = 9999
    server_socket.bind((host, port))
    print(f"UDP服务器监听 {host}:{port}")
    
    while True:
        # 接收数据(同时获取客户端地址)
        data, address = server_socket.recvfrom(1024)
        message = data.decode()
        print(f"收到来自 {address} 的消息: {message}")
        
        # 回复客户端
        response = f"服务器收到: {message}"
        server_socket.sendto(response.encode(), address)

def udp_client():
    """UDP客户端"""
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    server_address = ("127.0.0.1", 9999)
    
    # 发送消息
    message = "你好,UDP服务器!"
    client_socket.sendto(message.encode(), server_address)
    
    # 接收回复
    data, address = client_socket.recvfrom(1024)
    print(f"收到回复: {data.decode()}")
    
    client_socket.close()

非阻塞Socket

默认情况下Socket是阻塞的,可以设置为非阻塞模式:

import socket
import select

def non_blocking_example():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(("127.0.0.1", 7777))
    server_socket.listen(5)
    
    # 设置为非阻塞
    server_socket.setblocking(False)
    
    # 使用select进行I/O多路复用
    readable = [server_socket]
    writable = []
    exceptional = []
    
    while True:
        # 等待事件,超时1秒
        readable, writable, exceptional = select.select(
            readable, writable, exceptional, 1.0
        )
        
        for s in readable:
            if s is server_socket:
                # 新连接
                client, address = s.accept()
                print(f"新连接: {address}")
                readable.append(client)
            else:
                # 客户端数据
                try:
                    data = s.recv(1024)
                    if data:
                        print(f"收到数据: {data.decode()}")
                        s.send(data.upper())
                    else:
                        s.close()
                        readable.remove(s)
                except ConnectionResetError:
                    s.close()
                    readable.remove(s)

实战示例:简单聊天室

import socket
import threading
import json

class ChatServer:
    def __init__(self, host="127.0.0.1", port=8888):
        self.host = host
        self.port = port
        self.clients = {}
        self.server_socket = None
    
    def start(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(10)
        print(f"聊天室服务器启动: {self.host}:{self.port}")
        
        while True:
            client_socket, address = self.server_socket.accept()
            threading.Thread(
                target=self.handle_client,
                args=(client_socket, address),
                daemon=True
            ).start()
    
    def handle_client(self, client_socket, address):
        try:
            # 询问昵称
            client_socket.send("请输入你的昵称: ".encode())
            nickname = client_socket.recv(1024).decode().strip()
            self.clients[client_socket] = nickname
            
            self.broadcast(f"[系统] {nickname} 加入了聊天室", client_socket)
            print(f"{nickname} ({address}) 已连接")
            
            while True:
                data = client_socket.recv(4096)
                if not data:
                    break
                
                message = data.decode().strip()
                if message.lower() == "quit":
                    break
                self.broadcast(f"{nickname}: {message}", client_socket)
                
        except (ConnectionResetError, OSError):
            pass
        finally:
            nickname = self.clients.pop(client_socket, "未知用户")
            client_socket.close()
            self.broadcast(f"[系统] {nickname} 离开了聊天室")
            print(f"{nickname} 已断开")
    
    def broadcast(self, message, exclude=None):
        for sock in list(self.clients.keys()):
            if sock != exclude:
                try:
                    sock.send(message.encode())
                except OSError:
                    pass

if __name__ == "__main__":
    server = ChatServer()
    server.start()

总结

网络编程是构建分布式应用的基础。掌握Socket编程后,你可以进一步学习HTTP框架、WebSocket等高级网络技术。在实际开发中,通常使用现成的框架(如Flask、FastAPI)来处理网络通信,但理解底层原理对于调试和优化至关重要。