• 计网课设-发送TCP数据包


    一、效果展示

    二、代码实现

    1. import nmap
    2. import socket
    3. import tkinter as tk
    4. from tkinter import messagebox,Listbox
    5. from threading import Thread
    6. #获取自身IP,从而确定当前局域网范围
    7. def get_ip_address():
    8. #创建了一个socket对象,socket.AF_INET表示使用IPv4地址族,socket.SOCK_DGRAM表示使用UDP
    9. #使用UDP是因为不需要真正发送数据,通过连接一个地址来促使系统分配一个端口
    10. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    11. try:
    12. # 不需要真正发送数据,所以使用一个不存在的地址
    13. #这段代码目的让操作系统分配一个本地端口,并返回当前机器IP
    14. s.connect(('255.255.255.255', 1))
    15. ip = s.getsockname()[0]
    16. print(s.getsockname())
    17. #print(type(s.getsockname()))
    18. except Exception as e:
    19. print(e)
    20. finally:
    21. s.close()
    22. # 找到第三个'.'的位置
    23. dot_index = ip.rfind('.')
    24. # print(dot_index)
    25. # 替换第三个'.'及其后的内容
    26. new_ip = ip[:dot_index] + '.0/24'
    27. return new_ip
    28. def scan_local_network(listbox):
    29. nm = nmap.PortScanner()
    30. try:
    31. # 执行主机发现扫描
    32. network = get_ip_address()
    33. nm.scan(hosts=network, arguments='-sn')
    34. #清空列表框
    35. listbox.delete(0,tk.END)
    36. # 打印扫描结果
    37. for host in nm.all_hosts():
    38. status = nm[host].state()
    39. if status == 'up':
    40. listbox.insert(tk.END,host)
    41. except nmap.PortScannerError as e:
    42. messagebox.showerror("扫描失败",f"An error occurred:{e}")
    43. # 发送TCP消息的函数
    44. def send_message():
    45. ip = ip_entry.get()
    46. port = int(port_entry.get())
    47. message = message_entry.get()
    48. try:
    49. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    50. sock.connect((ip, port))
    51. sock.sendall(message.encode())
    52. messagebox.showinfo("发送成功", "消息已发送到" + ip)
    53. except Exception as e:
    54. messagebox.showerror("发送失败", str(e))
    55. # 更新IP输入框的函数
    56. def update_ip(ip):
    57. ip_entry.delete(0, tk.END)
    58. ip_entry.insert(0, ip)
    59. # 定义启动TCP服务器的函数
    60. def start_server(port):
    61. global server_socket
    62. try:
    63. server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    64. server_socket.bind(('0.0.0.0', port)) # 监听所有接口上的端口
    65. server_socket.listen(1)
    66. messagebox.showinfo("服务器启动", f"服务器正在监听端口 {port}")
    67. while True:
    68. client_socket, addr = server_socket.accept()
    69. with client_socket:
    70. while True:
    71. data = client_socket.recv(1024)
    72. if not data:
    73. break
    74. message_text.insert(tk.END, f"从{addr}接收到消息: {data.decode()}\n")
    75. client_socket.close()
    76. except Exception as e:
    77. messagebox.showerror("接收消息失败", str(e))
    78. finally:
    79. if 'server_socket' in globals() and server_socket:
    80. server_socket.close()
    81. # 在GUI中启动服务器的按钮
    82. def start_server_button_clicked():
    83. port = int(port_entry.get()) # 获取用户输入的端口
    84. server_thread = Thread(target=start_server, args=(port,))
    85. server_thread.start() # 启动线程
    86. # 初始化GUI界面
    87. root = tk.Tk()
    88. root.title("TCP消息发送器")
    89. # 使用grid布局管理器
    90. root.columnconfigure(0, weight=1)
    91. root.columnconfigure(1, weight=2)
    92. # IP输入框
    93. ip_label = tk.Label(root, text="IP:")
    94. ip_label.grid(row=0, column=0, padx=10, pady=5, sticky='e')
    95. ip_entry = tk.Entry(root, width=20)
    96. ip_entry.grid(row=0, column=1, padx=10, pady=5)
    97. # 端口输入框
    98. port_label = tk.Label(root, text="PORT:")
    99. port_label.grid(row=1, column=0, padx=10, pady=5, sticky='e')
    100. port_entry = tk.Entry(root, width=20)
    101. port_entry.grid(row=1, column=1, padx=10, pady=5)
    102. # 消息输入框
    103. message_label = tk.Label(root, text="Message:")
    104. message_label.grid(row=2, column=0, padx=10, pady=5, sticky='e')
    105. message_entry = tk.Entry(root, width=50)
    106. message_entry.grid(row=2, column=1, padx=10, pady=5)
    107. # 扫描按钮
    108. scan_button = tk.Button(root, text="扫描局域网", command=lambda: scan_local_network(results_listbox))
    109. scan_button.grid(row=3, column=0, padx=10, pady=5, sticky='w')
    110. # 发送按钮
    111. send_button = tk.Button(root, text="发送", command=send_message)
    112. send_button.grid(row=3, column=1, padx=10, pady=5, sticky='w')
    113. # 启动服务器按钮
    114. start_server_button = tk.Button(root, text="启动服务器", command=start_server_button_clicked)
    115. start_server_button.grid(row=4, column=1, padx=10, pady=5, sticky='w')
    116. # 显示扫描结果的列表框
    117. results_listbox = tk.Listbox(root, width=35, height=10)
    118. results_listbox.grid(row=5, column=0, columnspan=2, padx=10, pady=5, sticky='nsew')
    119. # 消息显示区域
    120. message_text = tk.Text(root, width=50, height=10, wrap='word')
    121. message_text.grid(row=6, column=0, columnspan=2, padx=10, pady=5, sticky='nsew')
    122. # 监听端口标签和输入框(放置在Frame中以避免布局冲突)
    123. frame = tk.Frame(root)
    124. frame.grid(row=7, column=0, columnspan=2, padx=10, pady=5, sticky='ew')
    125. listen_port_label = tk.Label(frame, text="Listening Port:")
    126. listen_port_label.pack(side=tk.LEFT, padx=10, pady=5)
    127. listen_port_entry = tk.Entry(frame, width=20)
    128. listen_port_entry.pack(side=tk.LEFT, padx=10, pady=5)
    129. # 为扫描结果列表框添加选择事件
    130. #event参数是tkinter事件对象,包含了事件相关信息
    131. def on_select(event):
    132. #通过事件对象的widget属性获取触发事件的组件,即被点击的列表框
    133. w = event.widget
    134. #调用列表框w的curselection()方法,获取当前被选中的项目索引列表,如果没有项目被选中,则返回空列表
    135. selection = w.curselection()
    136. if selection:
    137. #事实上curselection返回的只有一个值
    138. index = selection[0]
    139. #获取IP
    140. ip = w.get(index)
    141. #更新IP
    142. update_ip(ip)
    143. #将on_select函数绑定到results_listbox列表框的<>事件上,意味着每当列表框中的项目被选中时,
    144. #就会触发on_select函数
    145. results_listbox.bind('<>', on_select)
    146. # 启动GUI主循环
    147. root.mainloop()

    三、注意事项

    <1>想要实现扫描局域网功能,主机必须安装有nmap

    <2>在发送消息前,接收端需要监听对应端口如上述展示中的1234,之后发送端可以向接收端监听的端口发送信息

    <3>在手机热点构成的局域网下,某些电脑将无法实现发送信息功能,即使对方已经监听对应端口(至少我的电脑就不可以)

  • 相关阅读:
    前端工具宝库,帮你解决99%的业务需求难题
    Linux中shell脚本中的运算
    Ubuntu 20.04 手动安装OpenStack
    音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析
    部署Ansible
    RestTemplate的GET多参数请求转发
    Vue基础二之全局API、实例属性和全局配置,以及组件进阶(mixins)的详细教程(案列实现,详细图解,附源码)
    【C++】构造函数初始化列表 ③ ( 构造函数 的 初始化列表 中 为 const 成员变量初始化 )
    Web前端开发技术课程大作业: 关于美食的HTML网页设计——HTML+CSS+JavaScript在线美食订餐网站html模板源码30个页面:
    Angular中的NgZone.run()有什么用?
  • 原文地址:https://blog.csdn.net/Fightever_/article/details/139843579