Socket:TCP/UDP通信详解
Socket
socket是计算机网络中用于实现进程间通信的一种机制。它提供了一种标准的接口,使得不同主机上的应用程序能够通过网络进行数据交换。Socket编程通常用于实现客户端-服务器模型的网络应用程序。
UDP编程
UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议。与TCP不同,UDP不提供可靠的数据传输机制,也不保证数据包的顺序和完整性。UDP适用于对实时性要求较高但对数据丢失不敏感的应用,如视频流、在线游戏和语音通信等。
UDP服务器端代码示例
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":breaksend_data = "服务器已收到数据"server_socket.sendto(send_data.encode("utf-8"), addr)
server_socket.close()
print("服务器已关闭连接")
UDP客户端代码示例
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":breakredata, addr = client_socket.recvfrom(1024)# 接收服务器返回的数据,1024表示接收数据的最大字节数print("收到服务器数据:", redata.decode("utf-8"))
client_socket.close()
print("客户端已关闭连接")
其实例子中,客户端和服务端的代码高度相似,主要区别在于数据的发送和接收方式。UDP协议不需要建立连接,因此客户端可以直接向服务器发送数据,而服务器也可以直接接收来自客户端的数据。所以说,没有十分明显的客户端和服务器端代码区别。
TCP编程
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的传输层协议。它提供可靠的数据传输机制,确保数据包按顺序到达,并且不会丢失或重复。TCP适用于对数据完整性和可靠性要求较高的应用,如文件传输、电子邮件和网页浏览等。
TCP服务器端代码示例
from socket import *
from threading import Threaddef handle_client(client_s, client_addr):print("客户端连接地址:", client_addr)while True:redata = client_s.recv(1024)if not redata:breakprint("收到客户端数据:", redata.decode("utf-8"))if redata.decode("utf-8") == "exit":breaksend_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客户端代码示例
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":breakredata = client_socket.recv(1024)print("收到服务器数据:", redata.decode("utf-8"))
client_socket.close()
print("客户端已关闭连接")
对于上面的代码,运行一个客户端后,再运行多个客户端,可以看到服务器端会为每个客户端创建一个独立的线程来处理通信,从而实现多客户端同时连接和通信的功能。
实现客户端之间的通信
如果想要实现客户端之间的通信,可以通过服务器端进行转发。具体来说,服务器端需要维护一个客户端连接的列表,当一个客户端发送数据时,服务器端将该数据转发给其他所有连接的客户端。这样,客户端之间就可以实现通信了。
服务端:
from socket import *
from threading import Threadclients = [] # 用于存储所有连接的客户端套接字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:breakprint("收到", 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("服务器已关闭连接")
客户端:
from socket import *
from threading import Thread
import jsonclient_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:breakmessage = redata.decode("utf-8")if message == "exit":breakprint(message)def send_messages():while True:send_data = input()if send_data == "exit":client_socket.send(send_data.encode("utf-8"))# Notify server about exitbreakmessage = 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应用的正常运行。
示例代码如下:
from socket import *
from threading import ThreadWEBROOT = "../"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" + contentexcept 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()
