网络编程基础:TCP/IP与Socket编程详解

1. 网络编程基础概念

1.1 TCP/IP协议栈

Socket是应用层与传输层之间的抽象接口,基于TCP/IP协议栈实现。Python的socket模块提供了标准的BSD Socket接口实现,支持多种协议族:

  • AF_INET:IPv4网络协议
  • AF_INET6:IPv6网络协议
  • AF_UNIX:Unix域套接字(本地进程通信)

1.2 Socket类型

Python中常用的Socket类型包括:

  • SOCK_STREAM:面向连接的TCP套接字
  • SOCK_DGRAM:无连接的UDP套接字
  • SOCK_RAW:原始套接字(需要管理员权限)

2. TCP Socket编程详解

2.1 服务端实现

TCP服务端需要依次调用socket()、bind()、listen()和accept()方法:

python 复制代码
import socket

# 创建TCP套接字
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 设置SO_REUSEADDR选项,避免端口占用问题
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定地址和端口
server.bind(('localhost', 8888))

# 开始监听,设置最大连接数为5
server.listen(5)
print("服务端启动,监听端口8888...")

while True:
    # 接受客户端连接
    client_socket, addr = server.accept()
    print(f"连接来自 {addr}")
    
    try:
        # 接收数据(最多1024字节)
        data = client_socket.recv(1024)
        print(f"收到: {data.decode()}")
        
        # 发送响应(模拟HTTP响应)
        client_socket.send(b"HTTP/1.1 200 OK\r\n\r\nHello Client")
    finally:
        # 关闭客户端连接
        client_socket.close()

2.2 客户端实现

TCP客户端需要调用socket()和connect()方法:

python 复制代码
import socket

# 创建TCP套接字
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    # 连接服务器
    client.connect(('localhost', 8888))
    
    # 发送数据
    client.send(b"Hello Server")
    
    # 接收响应
    response = client.recv(1024)
    print(f"服务端响应: {response.decode()}")
finally:
    # 关闭连接
    client.close()

2.3 多客户端处理(多线程)

单线程服务端一次只能处理一个客户端,使用多线程可以同时处理多个连接:

python 复制代码
import socket
import threading

def handle_client(client_socket, addr):
    try:
        print(f"处理 {addr}")
        data = client_socket.recv(1024)
        print(f"收到: {data.decode()}")
        client_socket.send(b"ACK")
    finally:
        client_socket.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8888))
server.listen(5)

while True:
    client, addr = server.accept()
    # 为每个客户端创建新线程
    threading.Thread(target=handle_client, args=(client, addr)).start()

3. UDP Socket编程

UDP是无连接的协议,具有速度快但不可靠的特点,适用于实时性要求高但对可靠性要求不高的场景,如视频流、DNS查询等。

3.1 UDP服务端

python 复制代码
import socket

# 创建UDP套接字
udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定地址和端口
udp_server.bind(('localhost', 9999))
print("UDP服务端启动,监听端口9999...")

while True:
    # 接收数据和客户端地址
    data, addr = udp_server.recvfrom(1024)
    print(f"来自{addr}: {data.decode()}")
    
    # 发送响应到客户端地址
    udp_server.sendto(b"OK", addr)

3.2 UDP客户端

python 复制代码
import socket

# 创建UDP套接字
udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

try:
    # 发送数据(不需要先建立连接)
    udp_client.sendto(b"Hello UDP", ('localhost', 9999))
    
    # 接收响应
    response, _ = udp_client.recvfrom(1024)
    print(response.decode())
finally:
    udp_client.close()

4. 非阻塞Socket与select

使用非阻塞Socket配合select模块可以实现单线程处理多个连接:

python 复制代码
import socket
import select

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)  # 设置为非阻塞模式
server.bind(('localhost', 8888))
server.listen(5)

inputs = [server]  # 监控的可读socket列表

while True:
    # 使用select监控socket列表
    readable, _, _ = select.select(inputs, [], [])
    
    for s in readable:
        if s is server:
            # 有新连接
            client, addr = server.accept()
            print(f"新连接: {addr}")
            client.setblocking(False)
            inputs.append(client)
        else:
            # 有数据可读
            data = s.recv(1024)
            if data:
                print(f"收到: {data.decode()}")
                s.send(b"ACK")
            else:
                # 连接关闭
                print(f"关闭连接: {s.getpeername()}")
                inputs.remove(s)
                s.close()

5. HTTP协议与http.server

Python内置了简单的HTTP服务器实现:

python 复制代码
from http.server import HTTPServer, BaseHTTPRequestHandler

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # 设置响应状态码
        self.send_response(200)
        
        # 设置响应头
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        
        # 设置响应内容
        self.wfile.write(b"<h1>Hello World</h1>")
        self.wfile.write(f"<p>访问路径: {self.path}</p>".encode())

# 创建HTTP服务器
server = HTTPServer(('localhost', 8000), MyHandler)
print("HTTP服务器启动,访问 http://localhost:8000")
server.serve_forever()

6. 第三方HTTP库

6.1 requests库(同步)

python 复制代码
import requests

# GET请求示例
resp = requests.get('https://api.github.com')
print(f"状态码: {resp.status_code}")
print(f"响应内容: {resp.json()}")

# POST请求示例
data = {'key': 'value'}
resp = requests.post('https://httpbin.org/post', data=data)
print(resp.json())

6.2 aiohttp库(异步)

python 复制代码
import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        html = await fetch(session, 'http://example.com')
        print(html[:100])  # 打印前100个字符

asyncio.run(main())

7. WebSocket编程

WebSocket实现了全双工通信,适用于实时应用如聊天室、在线游戏等。

7.1 服务端实现

python 复制代码
import asyncio
import websockets

async def echo(websocket, path):
    async for message in websocket:
        print(f"收到消息: {message}")
        await websocket.send(f"Echo: {message}")

start_server = websockets.serve(echo, "localhost", 8765)
print("WebSocket服务器启动,监听端口8765...")

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

7.2 客户端实现(浏览器)

html 复制代码
<script>
    const ws = new WebSocket("ws://localhost:8765");
    
    ws.onopen = () => {
        console.log("连接已建立");
        ws.send("Hello Server");
    };
    
    ws.onmessage = (event) => {
        console.log(`收到消息: ${event.data}`);
    };
    
    ws.onclose = () => {
        console.log("连接已关闭");
    };
</script>

8. 异步IO网络编程实战

使用asyncio实现高并发TCP服务器:

python 复制代码
import asyncio

async def handle_client(reader, writer):
    addr = writer.get_extra_info('peername')
    print(f"新连接: {addr}")
    
    try:
        while True:
            data = await reader.read(100)
            if not data:
                break
            print(f"收到: {data.decode()}")
            writer.write(b"ACK: " + data)
            await writer.drain()  # 确保数据已发送
    except Exception as e:
        print(f"错误: {e}")
    finally:
        print(f"关闭连接: {addr}")
        writer.close()
        await writer.wait_closed()

async def main():
    server = await asyncio.start_server(
        handle_client, 'localhost', 8888)
    
    addr = server.sockets[0].getsockname()
    print(f"服务器启动,监听 {addr}")
    
    async with server:
        await server.serve_forever()

asyncio.run(main())

9. 实战项目:简易聊天室

结合asyncio和WebSocket实现多用户聊天室:

9.1 服务端代码

python 复制代码
import asyncio
import websockets

# 存储所有连接的客户端
connected = set()

async def chat_handler(websocket, path):
    # 新客户端连接
    connected.add(websocket)
    print(f"新用户加入,当前用户数: {len(connected)}")
    
    try:
        async for message in websocket:
            # 广播消息给所有其他客户端
            for conn in connected:
                if conn != websocket:
                    await conn.send(f"用户说: {message}")
    finally:
        # 客户端断开连接
        connected.remove(websocket)
        print(f"用户离开,剩余用户数: {len(connected)}")

start_server = websockets.serve(chat_handler, "localhost", 6789)
print("聊天室服务器启动,监听端口6789...")

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

9.2 客户端代码(HTML/JavaScript)

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>简易聊天室</title>
    <style>
        #messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; }
    </style>
</head>
<body>
    <div id="messages"></div>
    <input id="message" type="text" placeholder="输入消息">
    <button onclick="sendMessage()">发送</button>

    <script>
        const ws = new WebSocket("ws://localhost:6789");
        const messages = document.getElementById('messages');
        const messageInput = document.getElementById('message');
        
        ws.onmessage = (event) => {
            const message = document.createElement('div');
            message.textContent = event.data;
            messages.appendChild(message);
            messages.scrollTop = messages.scrollHeight;
        };
        
        function sendMessage() {
            const message = messageInput.value;
            if (message) {
                ws.send(message);
                messageInput.value = '';
            }
        }
        
        messageInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>

10. 总结与最佳实践

10.1 TCP与UDP比较

特性 TCP UDP
连接方式 面向连接 无连接
可靠性 可靠 不可靠
顺序保证 保证 不保证
流量控制
传输速度 较慢 较快
适用场景 文件传输、网页浏览等 视频流、DNS查询等

10.2 网络编程最佳实践

  1. 错误处理:始终处理网络异常(连接超时、断开等)
  2. 资源管理:使用try/finally或with语句确保socket关闭
  3. 性能优化
    • 对于IO密集型应用使用异步编程
    • 合理设置缓冲区大小
    • 考虑使用连接池
  4. 安全性
    • 验证输入数据
    • 使用TLS加密敏感数据
    • 防范DDoS攻击

10.3 技术选型建议

  • 简单REST API:requests + Flask/Django
  • 高并发服务:aiohttp/asyncio
  • 实时应用:WebSocket
  • 底层网络操作:socket模块
相关推荐
神明9311 小时前
mysql索引排序规则设置方法_mysqlCollation对索引影响
jvm·数据库·python
神明9311 小时前
CSS如何实现打字机效果_利用animation与宽度变化
jvm·数据库·python
2303_821287381 小时前
bootstrap如何实现平滑滚动到页面顶部
jvm·数据库·python
ftpeak1 小时前
AI开发之LangGraph教程4~记忆 (Memory)
python·ai·langchain·langgraph
2301_812539671 小时前
Tailwind CSS如何设置不同断点的内边距_使用p-4 md-p-8类.txt
jvm·数据库·python
m0_596749091 小时前
CSS实现动态悬浮菜单位置_JS计算配合CSS绝对定位
jvm·数据库·python
2301_812539672 小时前
golang如何实现最小堆定时器_golang最小堆定时器实现总结
jvm·数据库·python
lyc87802 小时前
【Qwen3.5-2B-Base】本地模型部署和验证联动千帆api
大数据·python
m0_690825822 小时前
检测三位随机数中重复数字的Python实现方法
jvm·数据库·python