实验目的与要求
1.熟悉Socket 编程;
2.掌握基于Socket的网络通信程序的设计与开发方法;
3.掌握Python界面设计技术;
4.掌握多线程/多进程编程的基本概念与实现方法;
5.掌握消息的网络传输的基本原理与实现方法;
6.添加异常捕捉,规范信息填写。
实验原理与内容
实验原理
一般来说,存放重要数据的服务器必须要进行强有力的保护,并且只能允许特定的机器进行访问,不能让所有客户端自由访问。可以考虑编写一个代理程序,设置服务器只允许代理程序所在计算机的IP地址访问,所有客户端对服务器的访问都由代理程序负责转发。编写程序模拟这个过程,在中间代理程序中使用端口映射技术实现数据转发。
图1 正常转发情况
图2 恶意代码攻击
编写代码,实现GUI界面,如图1和图2所示
图 1 服务端
图 2 客户端
实验内容
为实现简单网络通讯软件的基本功能,本实验需要可在实验二的实现方法基础上进行扩展设计,添加异常捕捉和GUI界面设计,并设置代理服务端,来隔绝潜在的风险。
编写一个代理程序,设置服务器只允许代理程序所在计算机的IP地址访问,所有客户端对服务器的访问都由代理程序负责转发。
在完成上述要求的基础上,自行构造GUI界面,界面布局参考图1,图2。
实验步骤
- 创建端口映射客户端和服务端;
- 利用Socket()、bind()、listen()、accept()、recv()、send()等函数完成套接字创建,并进行数据通信。
- 编写映射中间人代码,实现数据转发。
- 编写GUI代码,完成代理服务程序的聊天系统设计。
实验设备与软件环境
1.Windows 2003/ win7/win8/win10 操作系统。
2.Visual Studio/Pycharm及以上
实验过程与结果
服务器代码
import tkinter as tk
from tkinter import scrolledtext
import socket
import threading
class ChatServerGUI:
def __init__(self):
self.server = None # 服务器实例
self.client_connections = [] # 客户端连接列表
self.server_running = False # 服务器运行状态
self.root = tk.Tk()
self.root.title("服务端")
self.create_widgets()
self.root.mainloop()
def create_widgets(self):
# IP 地址输入框
ip_label = tk.Label(self.root, text="IP 地址:")
ip_label.pack()
self.ip_entry = tk.Entry(self.root)
self.ip_entry.pack()
# 端口输入框
port_label = tk.Label(self.root, text="端口:")
port_label.pack()
self.port_entry = tk.Entry(self.root)
self.port_entry.pack()
# 最大监听个数输入框
listen_label = tk.Label(self.root, text="最大监听个数:")
listen_label.pack()
self.listen_entry = tk.Entry(self.root)
self.listen_entry.pack()
# 消息展示区域
self.text_area = scrolledtext.ScrolledText(self.root, state=tk.DISABLED, width=60, height=20, wrap=tk.WORD)
self.text_area.pack(padx=10, pady=5)
# 启动服务器按钮
start_button = tk.Button(self.root, text="开启服务端", command=self.start_server)
start_button.pack()
# 关闭服务器按钮
stop_button = tk.Button(self.root, text="关闭服务端", command=self.stop_server)
stop_button.pack()
def start_server(self):
"""启动服务器"""
host = self.ip_entry.get()
port = int(self.port_entry.get())
backlog = int(self.listen_entry.get())
self.server = ChatServer(host, port, backlog, self)
self.server_running = True
server_thread = threading.Thread(target=self.server.start_server)
server_thread.start()
def stop_server(self):
"""停止服务器"""
if self. Server:
self.server_running = False
self.server.stop_server()
def display_message(self, message):
"""在GUI中显示消息"""
self.text_area.config(state=tk.NORMAL)
self.text_area.insert(tk.END, message + '\n')
self.text_area.config(state=tk.DISABLED)
self.text_area.yview(tk.END)
def send_message_to_clients(self, message):
"""发送消息给所有客户端"""
for conn in self.client_connections:
try:
conn.send(message.encode('utf-8'))
except Exception as e:
print(f"发送消息失败: {e}")
class ChatServer:
def __init__(self, host, port, backlog, gui):
self.host = host # 主机地址
self.port = port # 端口号
self.backlog = backlog # 最大监听个数
self.server_socket = None # 服务器套接字
self.running = False # 服务器运行状态
self.gui = gui # GUI实例
def start_server(self):
"""启动服务器"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(self.backlog)
self.running = True
self.gui.display_message(f"服务器启动,监听地址: {self.host}, 端口: {self.port}, 最大监听个数: {self.backlog}")
while self.running:
try:
client_socket, client_address = self.server_socket.accept()
if not self.running:
break
self.gui.display_message(f"接受来自 {client_address} 的连接")
self.gui.client_connections.append(client_socket)
client_thread = threading.Thread(target=self.handle_client, args=(client_socket,))
client_thread.start()
except Exception as e:
print(f"服务器接受连接错误: {e}")
def handle_client(self, client_socket):
"""处理客户端连接"""
while self.running:
try:
data = client_socket.recv(1024)
if not data:
break
message = data.decode('utf-8')
self.gui.display_message(f"客户端: {message}")
self.gui.send_message_to_clients(message)
except Exception as e:
self.gui.display_message(f"错误: {e}")
break
client_socket.close()
self.gui.client_connections.remove(client_socket)
def stop_server(self):
"""停止服务器"""
self.running = False
for conn in self.gui.client_connections:
conn.close()
self.gui.client_connections.clear()
if self.server_socket:
self.server_socket.close()
self.gui.display_message("服务器已停止")
if __name__ == "__main__":
ChatServerGUI()
整体流程
启动程序后,显示服务器的 GUI 界面。
用户输入 IP 地址、端口和最大监听个数,然后点击 "开启服务端" 按钮。
服务器启动并开始监听客户端连接。
当客户端连接时,服务器接受连接并启动新线程处理客户端消息。
客户端发送的消息显示在服务器 GUI 界面中,并转发给所有已连接的客户端。
用户点击 "关闭服务端" 按钮停止服务器,关闭所有客户端连接并停止接收新连接。
这个实现是一个简易的聊天服务器,能够处理多客户端的连接和消息转发,并通过图形界面显示和控制服务器的状态。
客户端代码
import tkinter as tk
from tkinter import scrolledtext
import socket
import threading
class ChatClient:
def __init__(self, root):
self.root = root
self.root.title("聊天客户端")
# 创建IP地址、端口和消息发送内容填空区
self.label_ip = tk.Label(root, text="IP地址:")
self.label_ip.grid(row=0, column=0, padx=10, pady=5)
self.entry_ip = tk.Entry(root, width=20)
self.entry_ip.grid(row=0, column=1, padx=10, pady=5)
self.label_port = tk.Label(root, text="端口:")
self.label_port.grid(row=0, column=2, padx=10, pady=5)
self.entry_port = tk.Entry(root, width=10)
self.entry_port.grid(row=0, column=3, padx=10, pady=5)
self.label_message = tk.Label(root, text="消息:")
self.label_message.grid(row=1, column=0, padx=10, pady=5)
self.entry_message = tk.Entry(root, width=40)
self.entry_message.grid(row=1, column=1, columnspan=3, padx=10, pady=5)
# 创建消息展示区域
self.text_area = scrolledtext.ScrolledText(root, state=tk.DISABLED, width=60, height=20, wrap=tk.WORD)
self.text_area.grid(row=2, column=0, columnspan=4, padx=10, pady=5)
# 创建发送消息按钮
self.send_button = tk.Button(root, text="发送消息", command=self.send_message)
self.send_button.grid(row=3, column=0, columnspan=4, padx=10, pady=5)
# 默认服务器地址和端口
self.entry_ip.insert(0, '127.0.0.1')
self.entry_port.insert(0, '9999')
# 创建客户端套接字
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connected = False
def display_message(self, message):
"""在文本区域显示消息"""
self.text_area.config(state=tk.NORMAL)
self.text_area.insert(tk.END, message + '\n')
self.text_area.config(state=tk.DISABLED)
self.text_area.yview(tk.END)
def receive_messages(self):
"""接收消息并显示在GUI界面上"""
while True:
try:
message = self.client_socket.recv(1024).decode('utf-8')
if not message:
break
self.display_message(message)
except Exception as e:
print(f"错误: {e}")
break
def send_message(self):
"""发送消息"""
if not self.connected:
ip = self.entry_ip.get()
port = int(self.entry_port.get())
try:
# 连接到服务器
self.client_socket.connect((ip, port))
self.connected = True
# 启动接收消息线程
receive_thread = threading.Thread(target=self.receive_messages)
receive_thread.start()
except Exception as e:
print(f"错误: {e}")
return
message = self.entry_message.get()
try:
# 发送消息
self.client_socket.send(message.encode('utf-8'))
self.display_message(f"你: {message}")
# 清空消息输入框
self.entry_message.delete(0, tk.END)
except Exception as e:
print(f"错误: {e}")
def main():
root = tk.Tk()
client = ChatClient(root)
root.mainloop()
if __name__ == "__main__":
main()
整体流程
启动程序后,显示客户端的 GUI 界面。
用户输入服务器的 IP 地址和端口,输入消息内容后点击 "发送消息" 按钮。
客户端尝试连接服务器并启动接收消息的线程。
客户端发送消息到服务器并在 GUI 界面中显示发送的消息。
接收服务器的消息并在 GUI 界面中显示。