基于代理服务程序的TCP通信

实验目的与要求

1.熟悉Socket 编程;

2.掌握基于Socket的网络通信程序的设计与开发方法;

3.掌握Python界面设计技术;

4.掌握多线程/多进程编程的基本概念与实现方法;

5.掌握消息的网络传输的基本原理与实现方法;

6.添加异常捕捉,规范信息填写。

实验原理与内容

实验原理

一般来说,存放重要数据的服务器必须要进行强有力的保护,并且只能允许特定的机器进行访问,不能让所有客户端自由访问。可以考虑编写一个代理程序,设置服务器只允许代理程序所在计算机的IP地址访问,所有客户端对服务器的访问都由代理程序负责转发。编写程序模拟这个过程,在中间代理程序中使用端口映射技术实现数据转发。

图1  正常转发情况

图2   恶意代码攻击

编写代码,实现GUI界面,如图1和图2所示

图 1 服务端

图 2 客户端

实验内容

为实现简单网络通讯软件的基本功能,本实验需要可在实验二的实现方法基础上进行扩展设计,添加异常捕捉和GUI界面设计,并设置代理服务端,来隔绝潜在的风险。

编写一个代理程序,设置服务器只允许代理程序所在计算机的IP地址访问,所有客户端对服务器的访问都由代理程序负责转发。

在完成上述要求的基础上,自行构造GUI界面,界面布局参考图1,图2。

实验步骤

  1. 创建端口映射客户端和服务端;
  2. 利用Socket()、bind()、listen()、accept()、recv()、send()等函数完成套接字创建,并进行数据通信。
  3. 编写映射中间人代码,实现数据转发。
  4. 编写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 界面中显示。

程序运行图例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

榴某

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值