目录
知识背景简介
Winsock
Winsock(Windows Sockets)是一组在 Windows 操作系统上提供网络编程接口的应用程序编程接口(API)的总称。它把网络协议栈(如 TCP/IP)暴露给应用程序,使得开发者可以通过一致的方式进行网络通信,不同于直接使用底层的 Socket 系统调用。
UDP
UDP(User Datagram Protocol,用户数据报协议)是一种简单的、无连接的传输层协议,主要用于在网络中传输数据。 UDP广泛应用于需要快速传输而不需要保证可靠性的场景,如视频会议、在线游戏和实时数据传输等。其主要特点包括:
无连接性:只需知道对端的IP和端口号即可发送数据,无需建立连接。
不可靠性:UDP不提供数据传输的确认机制和重传机制,数据包可能会丢失或失真。
低延迟:因没有连接建立和确认过程,UDP适合实时性要求较高的应用,如视频流和在线游戏。
报文导向:UDP以报文为单位进行数据传输,适合发送小量数据。
TCP
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接、可靠的传输层协议,主要用于在计算机网络中提供可靠的数据传输服务。它确保数据在传输过程中不丢失、不重复,并且按顺序到达。TCP是互联网的基础协议之一,广泛应用于各种网络应用中,如HTTP、FTP等。 TCP协议的主要特点包括:
连接导向:在数据传输之前,TCP需要建立连接(通过三次握手)。
可靠性:TCP通过确认应答和重传机制确保数据的可靠传输。
流量控制:TCP使用流量控制机制来防止发送方过快地发送数据,导致接收方缓冲区溢出。
拥塞控制:TCP能够根据网络的拥塞情况调整数据传输速率,以提高网络的整体性能。
实验目的及环境
实验目的:了解socket编程原理;掌握socket网络编程接口;
实验环境:python
UDP通信实验
实验内容:利用UDP实现客户端与服务端的通信
UDP服务端工作源码
import socket
def udp_server():
# 创建一个UDP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定IP地址和端口
server_socket.bind(('192.168.32.14', 1845)) # 请自己查询自己本机的网络接口及IP
print("UDP 服务器已启动,等待消息...")
while True:
# 接收数据
# data, addr = server_socket.recvfrom(1024)
# print(f"接收到来自 {addr} 的消息: {data.decode('utf-8')}")
while True:
data, addr = server_socket.recvfrom(1024) # 接收数据
print(f"接收到来自 {addr} 的消息: {data.decode()}")
reply = f"服务器已收到: {data.decode()}"
server_socket.sendto(reply.encode(), addr) # 发送回复
# 发送响应
response_message = "服务器已收到消息".encode('utf-8')
server_socket.sendto(response_message, addr)
if __name__ == "__main__":
udp_server()
1. 初始化和创建套接字
使用`socket`模块创建一个UDP套接字。
将套接字绑定到指定的IP地址和端口
2. 接收客户端消息
进入无限循环,调用`recvfrom()`方法接收来自客户端的消息。
3. 处理并响应消息:
打印接收到的消息,记录发送方地址。
发送响应消息确认已收到。
4. 启动服务器:
通过`__name__ == "__main__"`条件语句启动服务器。
UDP客户端工作源码
import socket
def udp_client():
# 创建一个UDP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('192.168.1.11', 1845)
try:
# # 发送数据
# message = "Hello"
# print(f"发送消息: {message}")
# sent = client_socket.sendto(message.encode('utf-8'), server_address)
while True:
message = input("请输入要发送的消息(输入exit退出):")
if message.lower() == 'exit':
break
client_socket.sendto(message.encode(), server_address)
data, _ = client_socket.recvfrom(1024)
print(f"来自服务器的响应: {data.decode()}")
# 接收响应
data, server = client_socket.recvfrom(1024)
print(f"接收到来自服务器的消息: {data.decode('utf-8')}")
finally:
# 关闭套接字
client_socket.close()
if __name__ == "__main__":
udp_client()
1. 初始化和创建套接字:
使用`socket`模块创建一个UDP套接字。
定义服务器的IP地址和端口。
2. 发送消息到服务器:
定义并编码要发送的消息。
使用`sendto()`方法将消息发送到服务器。
3. 接收服务器响应:
调用`recvfrom()`方法接收来自服务器的响应消息。
解码并打印接收到的消息。
4. 关闭套接字:
在`finally`块中关闭套接字以确保资源释放。
5. 启动客户端:
通过`__name__ == "__main__"`条件语句启动客户端。
实验结果展示


TCP通信实验
TCP服务端工作源码
def handle_client(client_socket, client_address):
print(f"客户端 {client_address} 已连接")
while True:
try:
data = client_socket.recv(1024)
if not data:
print(f"客户端 {client_address} 断开连接")
break
message = data.decode()
print(f"收到来自 {client_address} 的消息:{message}")
reply = f"服务器已收到:{message}"
client_socket.sendall(reply.encode())
except ConnectionResetError:
print(f"客户端 {client_address} 异常断开")
break
client_socket.close()
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 1238))
server_socket.listen(5)
print("服务器启动,监听端口 1238...")
while True:
client_socket, client_address = server_socket.accept()
client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address))
client_thread.start()
if __name__ == "__main__":
start_server()
TCP客户端工作源码
import socket
def start_client():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
client_socket.connect(('localhost', 1238))
print("已连接服务器。")
while True:
msg = input("请输入消息(输入exit退出):")
if msg.lower() == 'exit':
break
client_socket.sendall(msg.encode())
data = client_socket.recv(1024)
print(f"来自服务器的响应:{data.decode()}")
except ConnectionRefusedError:
print("无法连接到服务器,请确保服务器已启动。")
finally:
client_socket.close()
print("连接已关闭。")
if __name__ == "__main__":
start_client()
实验结果展示


综合应用
实验内容:编写一个C/S结构的通讯程序(考虑界面)。
1. 多个客户端连接同一台服务器。
2. 各客户端间的通信需经过服务器的中转。
3. 客户端可以向服务器请求当前的在线用户列表。
客户端功能:1)连接服务器,向服务器注册用户信息;2)向服务器请求在线用户列表;3)向其它用户发送消息。
服务器功能:1)接收客户端的连接请求,维护用户列表;2)接收在线用户列表请求,返回用户列表信息;3)转发用户间的通信消息。
server代码
import socket
import threading
import tkinter as tk
from tkinter import scrolledtext
class ServerApp:
def __init__(self, master):
# 初始化服务器应用程序
self.master = master
self.master.title("服务器")
# 设置IP地址标签和输入框
self.label_ip = tk.Label(master, text="IP地址")
self.label_ip.grid(row=0, column=0)
self.entry_ip = tk.Entry(master)
self.entry_ip.grid(row=0, column=1)
# 设置端口标签和输入框
self.label_port = tk.Label(master, text="端口")
self.label_port.grid(row=0, column=2)
self.entry_port = tk.Entry(master)
self.entry_port.grid(row=0, column=3)
# 开启服务器按钮
self.button_start = tk.Button(master, text="开启服务器", command=self.start_server)
self.button_start.grid(row=0, column=4)
# 文本区域,用于显示服务器日志
self.text_area = scrolledtext.ScrolledText(master, wrap=tk.WORD, width=50, height=20)
self.text_area.grid(row=1, column=0, columnspan=5, padx=10, pady=10)
# 初始化服务器套接字和运行状态
self.server_socket = None
self.running = False
self.clients = {} # 保存客户端连接的字典,key形式为(ip, port)
def start_server(self):
# 启动服务器
host = self.entry_ip.get() # 获取输入的IP地址
port = int(self.entry_port.get()) # 获取输入的端口号
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP套接字
self.server_socket.bind((host, port)) # 绑定套接字到指定地址和端口
self.server_socket.listen(5) # 开始监听连接
self.running = True # 设置运行状态为True
self.text_area.insert(tk.END, f"服务器启动,正在监听 {host}:{port}\n") # 显示服务器启动信息
threading.Thread(target=self.accept_clients, daemon=True).start() # 线程接受客户端连接
def accept_clients(self):
# 接受客户端连接
while self.running:
try:
client_socket, client_address = self.server_socket.accept() # 接受客户端连接
except:
break
self.text_area.insert(tk.END, f"接受到来自 {client_address} 的连接\n")
self.clients[client_address] = client_socket
threading.Thread(target=self.handle_client, args=(client_socket, client_address), daemon=True).start()
def handle_client(self, client_socket, client_address):
# 处理客户端消息
while self.running:
try:
data = client_socket.recv(1024)
if data:
message = data.decode('utf-8')
self.text_area.insert(tk.END, f"接收到的数据: {message} 来自 {client_address}\n")
# 新增功能:接收请求用户列表指令
if message.startswith("get_users"):
user_list = ', '.join([f"{ip}:{port}" for (ip, port) in self.clients.keys()])
reply = f"在线用户列表: {user_list}"
client_socket.sendall(reply.encode('utf-8'))
continue
target_ip, target_port, msg = message.split(':', 2)
if target_ip == "broadcast":
self.broadcast_message(client_address, msg)
else:
target_port = int(target_port)
self.forward_message(client_address, (target_ip, target_port), msg)
else:
break
except:
break
client_socket.close()
if client_address in self.clients:
del self.clients[client_address]
def forward_message(self, source_address, target_address, message):
# 转发消息到指定目标
if target_address in self.clients:
target_socket = self.clients[target_address]
source_ip, source_port = source_address
full_message = f"来自 {source_ip}:{source_port} 的消息: {message}"
try:
target_socket.sendall(full_message.encode('utf-8'))
except:
pass
else:
self.text_area.insert(tk.END, f"目标 {target_address} 未连接\n")
def broadcast_message(self, source_address, message):
# 广播消息到所有客户端
source_ip, source_port = source_address
full_message = f"来自 {source_ip}:{source_port} 的广播消息: {message}"
for client_address, client_socket in list(self.clients.items()):
if client_address != source_address:
try:
client_socket.sendall(full_message.encode('utf-8'))
except:
pass
def stop_server(self):
# 停止服务器
self.running = False
if self.server_socket:
self.server_socket.close()
for client_socket in self.clients.values():
client_socket.close()
def on_closing(self):
# 处理窗口关闭事件
self.stop_server()
self.master.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = ServerApp(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
client代码
import socket
import threading
import tkinter as tk
from tkinter import scrolledtext
class ClientApp:
def __init__(self, master):
# 初始化客户端应用程序
self.master = master
self.master.title("客户端")
# 设置服务器IP地址标签和输入框
self.label_ip = tk.Label(master, text="服务器IP地址")
self.label_ip.grid(row=0, column=0)
self.entry_ip = tk.Entry(master)
self.entry_ip.grid(row=0, column=1)
# 设置服务器端口标签和输入框
self.label_port = tk.Label(master, text="端口")
self.label_port.grid(row=0, column=2)
self.entry_port = tk.Entry(master)
self.entry_port.grid(row=0, column=3)
# 连接服务器按钮
self.button_connect = tk.Button(master, text="连接服务器", command=self.connect_to_server)
self.button_connect.grid(row=0, column=4)
# 文本区域,用于显示聊天信息
self.text_area = scrolledtext.ScrolledText(master, wrap=tk.WORD, width=50, height=20)
self.text_area.grid(row=1, column=0, columnspan=5, padx=10, pady=10)
# 设置消息输入框、目标IP和目标端口输入框
self.entry_message = tk.Entry(master, width=30)
self.entry_message.grid(row=2, column=0, columnspan=2, padx=10, pady=10)
self.entry_target_ip = tk.Entry(master, width=15)
self.entry_target_ip.grid(row=2, column=2, padx=10, pady=10)
self.entry_target_port = tk.Entry(master, width=5)
self.entry_target_port.grid(row=2, column=3, padx=10, pady=10)
# 发送消息按钮
self.button_send = tk.Button(master, text="发送", command=self.send_message)
self.button_send.grid(row=2, column=4)
# 广播消息按钮
self.button_broadcast = tk.Button(master, text="广播", command=self.broadcast_message)
self.button_broadcast.grid(row=3, column=0, columnspan=5)
# 新增:获取在线用户列表按钮
self.button_get_users = tk.Button(master, text="获取在线用户列表", command=self.request_user_list)
self.button_get_users.grid(row=4, column=0, columnspan=5)
# 初始化客户端套接字和运行状态
self.client_socket = None
self.running = False
def connect_to_server(self):
# 连接到服务器
host = self.entry_ip.get()
port = int(self.entry_port.get())
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.client_socket.connect((host, port))
except Exception as e:
self.text_area.insert(tk.END, f"连接失败: {e}\n")
return
self.running = True
self.text_area.insert(tk.END, f"已连接到服务器 {host}:{port}\n")
threading.Thread(target=self.receive_messages, daemon=True).start()
def send_message(self):
# 发送消息到指定目标
if not self.client_socket:
self.text_area.insert(tk.END, "未连接服务器\n")
return
message = self.entry_message.get()
target_ip = self.entry_target_ip.get()
target_port = self.entry_target_port.get()
if not (target_ip and target_port):
self.text_area.insert(tk.END, "请输入目标IP和端口\n")
return
full_message = f"{target_ip}:{target_port}:{message}"
try:
self.client_socket.sendall(full_message.encode('utf-8'))
self.text_area.insert(tk.END, f"发送到 {target_ip}:{target_port}: {message}\n")
self.entry_message.delete(0, tk.END)
except Exception as e:
self.text_area.insert(tk.END, f"发送失败: {e}\n")
def broadcast_message(self):
# 广播消息
if not self.client_socket:
self.text_area.insert(tk.END, "未连接服务器\n")
return
message = self.entry_message.get()
full_message = f"broadcast:0:{message}"
try:
self.client_socket.sendall(full_message.encode('utf-8'))
self.text_area.insert(tk.END, f"广播消息: {message}\n")
self.entry_message.delete(0, tk.END)
except Exception as e:
self.text_area.insert(tk.END, f"广播失败: {e}\n")
def request_user_list(self):
# 发送请求获取在线用户列表
if not self.client_socket:
self.text_area.insert(tk.END, "未连接服务器\n")
return
try:
self.client_socket.sendall("get_users:0:0".encode('utf-8'))
self.text_area.insert(tk.END, "请求在线用户列表...\n")
except Exception as e:
self.text_area.insert(tk.END, f"请求失败: {e}\n")
def receive_messages(self):
# 接收来自服务器的消息
while self.running:
try:
data = self.client_socket.recv(1024)
if data:
message = data.decode('utf-8')
self.text_area.insert(tk.END, f"接收: {message}\n")
else:
break
except:
break
self.text_area.insert(tk.END, "连接已关闭\n")
def stop_client(self):
# 停止客户端
self.running = False
if self.client_socket:
self.client_socket.close()
def on_closing(self):
self.stop_client()
self.master.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = ClientApp(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
实验结果展示

小提示
查找自己的本机网络接口及ip,需要在自己的控制面板中输入ipconfig进行查找哦~



1144

被折叠的 条评论
为什么被折叠?



