1.可能用得到的库

import socketimport threadingimport binasciiimport time
  1. socket: 这是Python的一个内置库,用于网络通信。它提供了基于TCP和UDP的套接字接口。

  2. threading: 这也是Python的一个内置库,用于多线程编程。它提供了高级接口来处理线程,包括启动、停止、同步和锁定等功能。

  3. binascii: 这个库提供了一些方法来转换二进制数据和ASCII表示之间的转换。例如,它可以用于将二进制数据转换为十六进制字符串。

  4. time: 这是Python的一个内置库,用于处理时间。它提供了获取当前时间、延迟执行、格式化时间等功能。

2.确定使用哪个端口进行模拟

local_ip01 = "127.0.0.1"local_port01 = 2345local_ip02 = "127.0.0.1"local_port02 = 2346remote_ip01 = "127.0.0.1"remote_port01 = 2347remote_ip02 = "127.0.0.1"remote_port02 = 2348

用自己电脑本机模拟,IP地址就定了是localhost或者127.0.0.1了

当你在本地机器上模拟客户端和服务器时,通常使用localhost127.0.0.1作为IP地址。这两者都指向你的本地机器。

对于端口号,其范围是从0到65535。然而,并非所有的端口都可以自由使用:

  • 0到1023号端口被称为“知名端口”,这些端口通常被系统进程或者重要的服务使用。例如,HTTP服务默认使用80端口,HTTPS服务默认使用443端口。

  • 1024到49151号端口被称为“注册端口”,这些端口主要用于软件应用程序。你的应用程序也可以使用这些端口,但最好避免使用已经被其他应用程序注册的端口。

  • 49152到65535号端口被称为“动态”或“私有”端口,这些端口通常由动态端口选择的系统或应用程序使用。

因此,如果你在编写脚本并需要选择一个端口,建议选择1024以上的端口,以避免与系统服务冲突。如果你的应用程序需要与其他人共享,那么最好在1024到49151之间选择一个未被使用的端口。

3.保险起见,看看这些端口是否确定可用

感觉可能没有必要,但是不测测感觉不舒服,hhh

def is_port_available(ip, port):with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:try:s.bind((ip, port))return Trueexcept socket.error:return False# 测试端口是否可用for port in [2345, 2346, 2347, 2348]:print(f"Port {port} is available: {is_port_available('127.0.0.1', port)}")

3.创建套接字和它的行为

def udp_communication01():# 创建一个UDP套接字sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定到本地IP和端口sock.bind((local_ip01, local_port01))# 设置超时sock.settimeout(0.5)# 0.5 seconds timeoutwhile True:time.sleep(0.5)try:# 尝试接收另外一端的数据data, addr = sock.recvfrom(1024)# buffer size is 1024 byteshex_data = binascii.hexlify(data).decode()# 转换为十六进制字符串print("port 21 received message:", hex_data, "from:", addr)except ConnectionResetError:# 尝试发送数据到另外一端message = "想要发送什么填写什么"# 换为字节或者其他格式的数据(看项目需求)message_bytes = binascii.unhexlify(message)# 向远程端口发送消息sock.sendto(message_bytes, (remote_ip01, remote_port01))except socket.timeout:# 尝试发送数据到另外一端message = "想要发送什么填写什么"# 换为字节或者其他格式的数据(看项目需求)message_bytes = binascii.unhexlify(message)# 向远程端口发送消息sock.sendto(message_bytes, (remote_ip01, remote_port01))

他在干嘛?

这个函数的主要目的是创建一个UDP套接字,绑定到本地的IP和端口,并在一个无限循环中接收和发送数据。

  1. 首先,函数创建一个UDP套接字,并绑定到本地的IP和端口。

  2. 然后,它设置套接字的超时时间为0.5秒。这意味着如果sock.recvfrom方法在0.5秒内没有接收到任何数据,它将引发一个socket.timeout异常。

  3. 函数进入一个无限循环,在每次迭代中,它首先暂停0.5秒,然后尝试从套接字接收数据。

  4. 如果接收到数据,它将数据转换为十六进制字符串,并打印出接收到的消息和发送者的地址。

  5. 如果在尝试接收数据时发生ConnectionResetError异常,函数将构造一个消息,并将其发送到远程的IP和端口。这个消息是一个字符串,你可以根据你的需求修改它。

  6. 如果在尝试接收数据时发生socket.timeout异常,函数将做同样的事情:构造一个消息,并将其发送到远程的IP和端口。

需要注意的是,local_ip01local_port01remote_ip01,和remote_port01这四个变量在这个函数中没有被定义,你需要在调用这个函数之前定义这些变量,或者将它们作为参数传递给这个函数。

4.套接字线程 启动 !

# 创建一个子线程thread01 = threading.Thread(target=udp_communication01)# 启动子线程thread01.start()

创建一个新的线程来运行udp_communication01函数可以使得这个函数在后台运行,而主线程可以继续执行其他任务。

Because

程序有一些可能会阻塞的操作时。例如,udp_communication01函数中的sock.recvfrom方法会阻塞,直到它接收到数据。如果这个函数在主线程中运行,那么整个程序将会被阻塞。但是,如果这个函数在一个单独的线程中运行,那么即使它被阻塞,主线程也可以继续执行其他任务。

通过创建一个新的线程来运行udp_communication01函数,你的程序可以在等待数据的同时,继续执行其他任务。

敲黑板,划重点

threading.Thread(target=udp_communication01)

这种写法是将udp_communication01函数作为目标函数传递给Thread类的构造函数,但并不立即调用它。当你调用thread01.start()时,新的线程会开始运行,并调用udp_communication01函数。

threading.Thread(target=udp_communication01())

这种写法会立即调用udp_communication01函数,并将其返回值作为目标函数传递给Thread类的构造函数。这通常不是你想要的,除非udp_communication01函数返回的是另一个函数,你想在新的线程中调用这个返回的函数。

在大多数情况下,你应该使用第一种写法,因为你通常希望在新的线程中调用目标函数,而不是在创建Thread对象时立即调用它。