计算机网络基于Winsock实验

目录

知识背景简介

实验目的及环境

UDP通信实验

TCP通信实验

综合应用

小提示


知识背景简介

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进行查找哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值