Socket:TCP/UDP通信详解

Socket

socket是计算机网络中用于实现进程间通信的一种机制。它提供了一种标准的接口 ,使得不同主机上的应用程序能够通过网络进行数据交换。Socket编程通常用于实现客户端-服务器模型的网络应用程序。

UDP编程

UDP(User Datagram Protocol,用户数据报协议)是一种无连接 的传输层协议。与TCP不同,UDP不提供可靠的数据传输机制,也不保证数据包的顺序和完整性。UDP适用于对实时性要求较高但对数据丢失不敏感的应用,如视频流、在线游戏和语音通信等。

UDP服务器端代码示例

python 复制代码
from socket import *
server_socket = socket(AF_INET, SOCK_DGRAM)# 创建UDP套接字,AF_INET表示使用IPv4协议,SOCK_DGRAM表示使用UDP协议
server_socket.bind(("127.0.0.1", 6789))
while True:
    redata, addr = server_socket.recvfrom(1024)# 接收客户端发送的数据,1024表示接收数据的最大字节数
    print("收到客户端数据:", redata.decode("utf-8"))
    if redata.decode("utf-8") == "exit":
        break
    send_data = "服务器已收到数据"
    server_socket.sendto(send_data.encode("utf-8"), addr)
server_socket.close()
print("服务器已关闭连接")

UDP客户端代码示例

python 复制代码
from socket import *
client_socket = socket(AF_INET, SOCK_DGRAM)# 创建UDP套接字,AF_INET表示使用IPv4协议,SOCK_DGRAM表示使用UDP协议
server_address = ("127.0.0.1", 6789)
while True:
    send_data = input("请输入要发送的数据:")
    client_socket.sendto(send_data.encode("utf-8"), server_address)
    if send_data == "exit":
        break
    redata, addr = client_socket.recvfrom(1024)# 接收服务器返回的数据,1024表示接收数据的最大字节数
    print("收到服务器数据:", redata.decode("utf-8"))
client_socket.close()
print("客户端已关闭连接")

其实例子中,客户端和服务端的代码高度相似,主要区别在于数据的发送和接收方式。UDP协议不需要建立连接,因此客户端可以直接向服务器发送数据,而服务器也可以直接接收来自客户端的数据。所以说,没有十分明显的客户端和服务器端代码区别。

TCP编程

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的传输层协议。它提供可靠的数据传输机制,确保数据包按顺序到达,并且不会丢失或重复。TCP适用于对数据完整性和可靠性要求较高的应用,如文件传输、电子邮件和网页浏览等。

TCP服务器端代码示例

python 复制代码
from socket import *
from threading import Thread

def handle_client(client_s, client_addr):
    print("客户端连接地址:", client_addr)
    while True:
        redata = client_s.recv(1024)
        if not redata:
            break
        print("收到客户端数据:", redata.decode("utf-8"))
        if redata.decode("utf-8") == "exit":
            break
        send_data = "服务器已收到数据"
        client_s.send(send_data.encode("utf-8"))
    client_s.close()
    print("客户端已关闭连接:", client_addr)

server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(("127.0.0.1", 6789))
server_socket.listen(5)
print("服务器已启动,等待客户端连接...")
while True:
    client_socket, client_addr = server_socket.accept()
    t = Thread(target=handle_client, args=(client_socket, client_addr))
    t.start()
server_socket.close()
print("服务器已关闭连接")

TCP客户端代码示例

python 复制代码
from socket import *
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 6789))
while True:
    send_data = input("请输入要发送的数据:")
    client_socket.send(send_data.encode("utf-8"))
    if send_data == "exit":
        break
    redata = client_socket.recv(1024)
    print("收到服务器数据:", redata.decode("utf-8"))
client_socket.close()
print("客户端已关闭连接")

对于上面的代码,运行一个客户端后,再运行多个客户端,可以看到服务器端会为每个客户端创建一个独立的线程来处理通信,从而实现多客户端同时连接和通信的功能。

实现客户端之间的通信

如果想要实现客户端之间的通信,可以通过服务器端进行转发 。具体来说,服务器端需要维护一个客户端连接的列表,当一个客户端发送数据时,服务器端将该数据转发给其他所有连接的客户端。这样,客户端之间就可以实现通信了。
服务端

python 复制代码
from socket import *
from threading import Thread

clients = []  # 用于存储所有连接的客户端套接字

def handle_client(client_s, client_addr):
    username = client_s.recv(1024).decode("utf-8")
    print(f"客户端连接地址: {client_addr}, 用户名: {username}")
    broadcast_message(f"{username} 已加入聊天", client_s)
    
    while True:
        redata = client_s.recv(1024).decode("utf-8")
        if not redata:
            break
        print("收到", username,"数据:", redata.split(":", 1)[1])
        if redata == "exit":
            break
        # 将收到的数据转发给其他所有客户端
        broadcast_message(redata, client_s)
        
    client_s.close()
    clients.remove(client_s)
    print("客户端已关闭连接:", client_addr)
    
    
def broadcast_message(message, sender_socket):
    for client in clients:
        if client != sender_socket:
            client.send(message.encode("utf-8"))


server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(("127.0.0.1", 6789))
server_socket.listen(5)
print("服务器已启动,等待客户端连接...")
while True:
    client_socket, client_addr = server_socket.accept()
    clients.append(client_socket)  # 将新连接的客户端套接字添加到列表中
    t = Thread(target=handle_client, args=(client_socket, client_addr))
    t.start()
server_socket.close()
print("服务器已关闭连接")

客户端

python 复制代码
from socket import *
from threading import Thread
import json

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 6789))

username = input("请输入用户名:")
client_socket.send(username.encode("utf-8"))

def recv_messages():
    while True:
        redata = client_socket.recv(1024)
        if not redata:
            break
        message = redata.decode("utf-8")
        if message == "exit":
            break
        print(message)

def send_messages():
    while True:
        send_data = input()
        if send_data == "exit":
            client_socket.send(send_data.encode("utf-8"))# Notify server about exit
            break
        message = f"{username}: {send_data}"
        client_socket.send(message.encode("utf-8"))
    
recv_thread = Thread(target=recv_messages)
send_thread = Thread(target=send_messages)
recv_thread.start()
send_thread.start()

TCP与HTTP的结合

TCP协议是HTTP协议的基础。HTTP(Hypertext Transfer Protocol,超文本传输协议)是一种应用层协议,主要用于在Web浏览器和Web服务器之间传输超文本数据 。HTTP协议通常使用TCP作为传输层协议,以确保数据的可靠传输。

在实际应用中,HTTP请求和响应都是通过TCP连接进行传输 的。当客户端(如Web浏览器)向服务器发送HTTP请求时,首先会建立一个TCP连接 ,然后通过该连接发送HTTP请求数据 。服务器接收到请求后,会处理请求并生成HTTP响应数据,然后通过同一TCP连接将响应数据发送回客户端。最后,TCP连接可以根据需要保持打开状态以进行后续请求,或者关闭以释放资源。

因此,TCP协议为HTTP协议提供了可靠的数据传输机制,确保HTTP请求和响应数据能够完整、按顺序地到达目的地,从而实现Web应用的正常运行。

示例代码如下:

python 复制代码
from socket import *
from threading import Thread

WEBROOT = "../"

def handle_client(client_s, client_addr):
    print("客户端连接地址:", client_addr)
    
    with client_s:
        request = client_s.recv(1024)
        headers = request.split(b"\r\n")# 因为HTTP请求头是以\r\n分隔的(回车换行)
        filename = headers[0].split(b" ")[1].decode()#  提取客户端请求的文件名
        print("客户端请求文件:", filename)
        
        if filename == "/":
            filename = "/index.html"
        try:
            with open(WEBROOT + filename, "rb") as f:
                content = f.read()
            response = b"HTTP/1.1 200 OK\r\n\r\n" + content
        except FileNotFoundError:
            response = b"HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>"
            
        client_s.send(response)

with socket(AF_INET, SOCK_STREAM) as server_s:
    server_s.bind(("127.0.0.1", 6789))
    server_s.listen(5)
    print("服务器启动,等待客户端连接...")
    
    while True:
        client_s, client_addr = server_s.accept()
        
        t = Thread(target=handle_client, args=(client_s, client_addr))# 创建线程处理客户端请求,target 指定线程函数, args 指定线程函数参数
        t.start()
相关推荐
The_Second_Coming1 小时前
Python 学习笔记:基础篇
运维·笔记·python·学习
诗句藏于尽头1 小时前
python实战学习记录
python·学习
AI松子6661 小时前
PyTorch-混合精度训练(amp)
人工智能·pytorch·python
MDLZH1 小时前
Pytorch性能调优简单总结
人工智能·pytorch·python
程序员爱钓鱼2 小时前
Python 编程实战 · 实用工具与库 — Flask 路由与模板
前端·后端·python
程序员爱钓鱼2 小时前
Python 编程实战 · 实用工具与库 — Django 项目结构简介
后端·python·面试
新之助小锅3 小时前
java版连接汇川PLC,发送数据,读取数据,保持重新链接,适用安卓
android·java·python
海琴烟Sunshine3 小时前
leetcode 383. 赎金信 python
python·算法·leetcode
惊讶的猫9 小时前
LSTM论文解读
开发语言·python