练习题:37

目录

Python题目

题目

题目分析

套接字概念剖析

通信原理分析

服务器 - 客户端连接建立过程:

基于套接字通信的底层机制:

代码实现

基于 TCP 的简单服务器 - 客户端通信示例

服务器端代码(tcp_server.py)

客户端代码(tcp_client.py)

基于 UDP 的简单服务器 - 客户端通信示例

服务器端代码(udp_server.py)

客户端代码(udp_client.py)

代码解释

基于 TCP 的代码解释

服务器端

导入模块与创建套接字对象:

绑定 IP 地址和端口号并监听:

接受客户端连接并处理通信:

客户端

导入模块与创建套接字对象:

连接服务器并发送接收消息:

基于 UDP 的代码解释

服务器端

导入模块与创建套接字对象:

绑定 IP 地址和端口号并监听:

接收并处理客户端消息:

客户端

导入模块与创建套接字对象:

发送消息并接收回复:

运行思路

基于 TCP 的代码分析

服务器端代码分析

导入模块与创建套接字对象:

绑定 IP 地址和端口号并监听:

接受客户端连接并处理通信:

客户端代码分析

导入模块与创建套接字对象:

连接服务器并发送接收消息:

基于 UDP 的代码分析

服务器端代码分析

导入模块与创建套接字对象:

绑定 IP 地址和端口号并监听:

接收并处理客户端消息:

客户端代码分析

导入模块与创建套接字对象:

发送消息并接收回复:

结束语

Python题目

题目

套接字是什么?为什么在服务器端宇客户端建立了套接字就可以通信了?

题目分析

  • 套接字概念剖析

    • 定义理解:套接字(Socket)是网络通信的基石,它是一种软件抽象层,用于在不同主机之间的进程进行通信。可以把套接字想象成是电话插孔,不同主机上的进程通过这个 “插孔”(套接字)来建立连接,就像两部电话通过插孔和线路连接起来进行通话一样。在网络编程中,它屏蔽了底层网络协议(如 TCP/IP 协议族)的复杂细节,为程序员提供了一个相对简单的接口来进行网络通信。
    • 类型区分:主要分为流套接字(Stream Socket,基于 TCP 协议)和数据报套接字(Datagram Socket,基于 UDP 协议)。流套接字提供面向连接、可靠的字节流服务,就像打电话一样,通信双方先建立连接,然后按顺序传输数据,数据不会丢失或乱序。数据报套接字则是无连接的、不可靠的通信方式,类似于发送短信,消息被封装成一个个独立的数据报发送,不保证数据一定能到达,也不保证顺序。
  • 通信原理分析

    • 服务器 - 客户端连接建立过程
      • 服务器端套接字创建与监听:服务器首先创建一个套接字,这个套接字绑定到一个特定的 IP 地址和端口号(IP 地址用于标识服务器在网络中的位置,端口号用于区分不同的服务)。然后,服务器通过这个套接字开始监听客户端的连接请求。例如,一个 Web 服务器监听在 80 端口(HTTP 服务默认端口)等待客户端浏览器的连接请求。
      • 客户端套接字创建与连接请求:客户端同样创建一个套接字,然后使用服务器的 IP 地址和端口号向服务器发送连接请求。这个请求通过网络传输,当服务器监听到这个请求后,就会接受这个请求,从而在服务器和客户端之间建立起一个连接。
    • 基于套接字通信的底层机制
      • 协议支持:一旦建立连接(对于流套接字),TCP/IP 协议就会确保数据在两个套接字之间可靠地传输。它通过一系列机制,如三次握手建立连接、数据确认和重传、流量控制等来保证数据的完整性和顺序性。例如,当客户端发送一个数据包时,TCP 协议会在数据包中添加序列号等信息,服务器收到后会发送确认信息,这样就保证了数据传输的可靠性。
      • 数据传输通道形成:套接字在连接建立后,就像在服务器和客户端之间建立了一条虚拟的数据传输通道。双方可以通过这个通道发送和接收数据,数据以字节流(对于流套接字)或数据报(对于数据报套接字)的形式在通道中传输。例如,客户端可以将用户输入的信息通过套接字发送给服务器,服务器接收到数据后进行处理,并将结果通过套接字返回给客户端。

代码实现

基于 TCP 的简单服务器 - 客户端通信示例

服务器端代码(tcp_server.py
import socket# 创建套接字对象,AF_INET表示使用IPv4地址族,SOCK_STREAM表示使用TCP协议(流套接字)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定IP地址和端口号,这里使用本地回环地址127.0.0.1和端口8888
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)# 开始监听,参数5表示允许的最大连接数
server_socket.listen(5)
print('服务器已启动,正在监听端口8888...')while True:# 接受客户端连接,返回一个新的套接字对象(用于与该客户端通信)和客户端地址client_socket, client_address = server_socket.accept()print(f'接受来自 {client_address} 的连接')try:# 接收客户端发送的数据,最多接收1024字节data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')# 处理客户端消息,这里简单将消息转换为大写后返回给客户端response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已将处理后的消息发送回客户端')except:print('接收或发送数据时出现错误')finally:# 关闭与该客户端的套接字连接client_socket.close()
客户端代码(tcp_client.py
import socket# 创建套接字对象,同样使用IPv4地址族和TCP协议
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服务器的IP地址和端口号,要与服务器端绑定的一致
server_address = ('127.0.0.1', 8888)try:# 连接服务器client_socket.connect(server_address)message = "Hello, Server!"# 向服务器发送消息,需先将字符串编码为字节流client_socket.send(message.encode('utf-8'))print(f'已向服务器发送消息: {message}')# 接收服务器返回的消息,最多接收1024字节data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')
except:print('连接服务器或通信过程中出现错误')
finally:# 关闭客户端套接字client_socket.close()

基于 UDP 的简单服务器 - 客户端通信示例

服务器端代码(udp_server.py
import socket# 创建套接字对象,AF_INET表示使用IPv4地址族,SOCK_DGRAM表示使用UDP协议(数据报套接字)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定IP地址和端口号,这里使用本地回环地址127.0.0.1和端口9999
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)print('UDP服务器已启动,正在监听端口9999...')while True:# 接收客户端发送的数据和客户端地址,最多接收1024字节data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')# 处理客户端消息,这里简单将消息转换为大写后返回给客户端response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已将处理后的消息发送回客户端')
客户端代码(udp_client.py
import socket# 创建套接字对象,使用IPv4地址族和UDP协议
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 服务器的IP地址和端口号,要与服务器端绑定的一致
server_address = ('127.0.0.1', 9999)message = "Hello, UDP Server!"
# 向服务器发送消息,需先将字符串编码为字节流
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服务器发送消息: {message}')# 接收服务器返回的消息,最多接收1024字节
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')# 关闭客户端套接字
client_socket.close()

代码解释

基于 TCP 的代码解释

服务器端
  • 导入模块与创建套接字对象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 首先通过 import socket 导入 Python 的 socket 模块,该模块提供了进行网络套接字编程所需的各种函数和类等资源。
  • 然后调用 socket.socket() 函数创建一个套接字对象 server_socket,参数 socket.AF_INET 表示使用 IPv4 地址族,socket.SOCK_STREAM 表示使用 TCP 协议(即创建的是流套接字),这个套接字将作为服务器与客户端进行通信的基础接口。
  • 绑定 IP 地址和端口号并监听
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)
server_socket.listen(5)
print('服务器已启动,正在监听端口8888...')
  • 定义 server_address 元组,其中第一个元素 '127.0.0.1' 是 IP 地址,这里使用本地回环地址(常用于在本地机器上测试网络程序,意味着只有本机上的程序可以访问该服务器),第二个元素 8888 是端口号(可以根据实际需求选择合适的未被占用的端口)。
  • 调用 server_socket.bind(server_address) 方法将创建好的套接字绑定到指定的 IP 地址和端口号上,这样服务器就在这个网络端点上等待客户端的连接请求了。
  • 接着调用 server_socket.listen(5) 方法开始监听客户端的连接请求,参数 5 表示服务器允许的最大连接数,即同时可以有多少个客户端连接到服务器等待处理。
  • 接受客户端连接并处理通信
while True:client_socket, client_address = server_socket.accept()print(f'接受来自 {client_address} 的连接')try:data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已将处理后的消息发送回客户端')except:print('接收或发送数据时出现错误')finally:client_socket.close()
  • 使用 while True 循环使得服务器可以持续处理多个客户端的连接请求。在循环内部,调用 server_socket.accept() 方法,该方法会阻塞程序执行,直到有客户端连接过来,当有客户端连接时,它会返回一个新的套接字对象 client_socket(这个套接字专门用于与该客户端进行后续的通信)以及客户端的地址 client_address(包含客户端的 IP 地址和端口号)。
  • 进入 try 块尝试接收客户端发送的数据,调用 client_socket.recv(1024) 方法从与客户端连接的套接字接收数据,参数 1024 表示最多接收 1024 字节的数据。如果接收到了数据,通过 data.decode('utf-8') 将接收到的字节数据解码为字符串形式赋值给 message 变量,并打印出来。
  • 接着对收到的消息进行处理,这里简单地将消息转换为大写形式,赋值给 response 变量,然后调用 client_socket.send(response.encode('utf-8')) 方法将处理后的消息编码为字节流并发送回客户端,同时打印发送提示信息。
  • 如果在接收或发送数据过程中出现错误,except 块会捕获异常并打印错误提示信息。
  • 无论是否出现错误,在 finally 块中都会调用 client_socket.close() 方法关闭与当前客户端的套接字连接,释放相关资源,准备处理下一个客户端的连接。
客户端
  • 导入模块与创建套接字对象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 同样先通过 import socket 导入 socket 模块,然后调用 socket.socket() 函数创建一个套接字对象 client_socket,使用的参数也是 socket.AF_INET(IPv4 地址族)和 socket.SOCK_STREAM(TCP 协议),用于与服务器进行通信。
  • 连接服务器并发送接收消息
server_address = ('127.0.0.1', 8888)
try:client_socket.connect(server_address)message = "Hello, Server!"client_socket.send(message.encode('utf-8'))print(f'已向服务器发送消息: {message}')data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')
except:print('连接服务器或通信过程中出现错误')
finally:client_socket.close()
  • 定义 server_address 元组,指定要连接的服务器的 IP 地址(这里同样是本地回环地址 127.0.0.1)和端口号(8888),要与服务器端绑定的地址和端口一致。
  • 调用 client_socket.connect(server_address) 方法向服务器发起连接请求,如果连接成功,程序继续往下执行。
  • 定义要发送给服务器的消息 message,然后调用 client_socket.send(message.encode('utf-8')) 方法将消息编码为字节流并发送给服务器,同时打印发送提示信息。
  • 接着调用 client_socket.recv(1024) 方法从服务器接收回复消息,最多接收 1024 字节的数据,若接收到了数据,将其解码为字符串形式赋值给 response 变量,并打印出来。
  • 如果在连接服务器或通信过程中出现错误,except 块会捕获异常并打印相应的错误提示信息。
  • 最后,在 finally 块中调用 client_socket.close() 方法关闭客户端套接字,释放资源。

基于 UDP 的代码解释

服务器端
  • 导入模块与创建套接字对象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 还是先通过 import socket 导入 socket 模块,然后调用 socket.socket() 函数创建套接字对象 server_socket,这次参数使用 socket.AF_INET(IPv4 地址族)和 socket.SOCK_DGRAM(表示使用 UDP 协议,即创建的数据报套接字),用于基于 UDP 协议进行通信。
  • 绑定 IP 地址和端口号并监听
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)
print('UDP服务器已启动,正在监听端口9999...')
  • 定义 server_address 元组,包含本地回环地址 '127.0.0.1' 和端口号 9999,用于指定服务器监听的网络端点。
  • 调用 server_socket.bind(server_address) 方法将套接字绑定到这个地址和端口上,使服务器可以在该端点接收客户端发送的数据报。
  • 接收并处理客户端消息
while True:data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已将处理后的消息发送回客户端')
  • 通过 while True 循环持续等待接收客户端发送的数据报。在循环内,调用 server_socket.recvfrom(1024) 方法接收客户端发送的数据报,这个方法会阻塞程序执行,直到接收到数据报为止,它返回接收到的数据(字节形式)和客户端的地址(包含客户端的 IP 地址和端口号)。
  • 如果接收到了数据,将其解码为字符串形式赋值给 message 变量并打印出来。
  • 对消息进行处理(这里同样是转换为大写形式),得到 response 变量,然后调用 server_socket.sendto(response.encode('utf-8'), client_address) 方法将处理后的消息编码为字节流,并根据客户端的地址发送回客户端,同时打印发送提示信息。
客户端
  • 导入模块与创建套接字对象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 导入 socket 模块后创建一个套接字对象 client_socket,使用 socket.AF_INET(IPv4 地址族)和 socket.SOCK_DGRAM(UDP 协议)参数,用于向服务器发送和接收数据报。
  • 发送消息并接收回复
server_address = ('127.0.0.1', 9999)
message = "Hello, UDP Server!"
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服务器发送消息: {message}')
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')
client_socket.close()
  • 定义 server_address 元组,指定服务器的 IP 地址(127.0.0.1)和端口号(9999)。
  • 定义要发送给服务器的消息 message,然后调用 client_socket.sendto(message.encode('utf-8'), server_address) 方法将消息编码为字节流,并按照指定的服务器地址发送出去,同时打印发送提示信息。
  • 调用 client_socket.recvfrom(1024) 方法等待接收服务器返回的数据报,接收到后将数据解码为字符串赋值给 response 变量并打印出来。
  • 最后调用 client_socket.close() 方法关闭客户端套接字,释放资源。

运行思路

基于 TCP 的代码分析

服务器端代码分析
  • 导入模块与创建套接字对象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 导入模块import socket 语句将 Python 标准库中的 socket 模块引入程序,该模块提供了操作网络套接字的各种函数、类等资源,是实现网络通信编程的基础。若没有正确导入此模块,后续使用套接字相关功能时会引发 ModuleNotFoundError 异常,导致程序无法继续运行。
  • 创建套接字对象socket.socket(socket.AF_INET, socket.SOCK_STREAM) 调用创建了一个套接字对象 server_socket。其中,socket.AF_INET 表示选用 IPv4 地址族,意味着这个套接字将基于 IPv4 网络进行通信,适用于大多数常见的网络环境;socket.SOCK_STREAM 表明创建的是流套接字,它基于 TCP 协议,提供面向连接、可靠的字节流通信服务,就像打电话一样,通信双方先建立稳定连接后再传输数据,且能保证数据的完整性和顺序性。
  • 绑定 IP 地址和端口号并监听
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)
server_socket.listen(5)
print('服务器已启动,正在监听端口8888...')
  • 定义服务器地址server_address = ('127.0.0.1', 8888) 创建了一个包含 IP 地址和端口号的元组。127.0.0.1 是本地回环地址,代表本机,常用于在本地进行网络程序的测试,只有本机上的其他程序可以通过这个地址访问该服务器;8888 是端口号,它用于在网络通信中区分不同的服务或应用程序,取值范围是 0 到 65535,这里选择 8888 作为自定义的服务端口(需确保该端口未被其他程序占用)。
  • 绑定套接字到地址server_socket.bind(server_address) 方法将之前创建的 server_socket 套接字绑定到指定的 server_address 上,使得服务器在网络中通过这个特定的 IP 地址和端口号来接收客户端的连接请求。若绑定的端口已被其他程序占用,会抛出 socket.error 异常,提示地址已在使用中。
  • 开始监听客户端连接server_socket.listen(5) 调用让服务器套接字进入监听状态,参数 5 表示服务器允许的最大连接数,即同时可以有最多 5 个客户端连接到服务器并处于等待处理的状态。一旦进入监听状态,服务器就开始等待客户端的连接请求,此时程序会阻塞在 accept 方法(后续调用处),直到有客户端发起连接。
  • 接受客户端连接并处理通信
while True:client_socket, client_address = server_socket.accept()print(f'接受来自 {client_address} 的连接')try:data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已将处理后的消息发送回客户端')except:print('接收或发送数据时出现错误')finally:client_socket.close()
  • 循环接受客户端连接while True 构建了一个无限循环,使得服务器能够持续不断地处理多个客户端的连接请求,只要服务器在运行,就会一直等待并处理新的连接。
  • 接受客户端连接client_socket, client_address = server_socket.accept() 这行代码是整个服务器通信流程的关键部分,accept 方法会阻塞程序执行,直到有客户端连接过来。当有客户端发起连接时,它会返回两个值:一个新的套接字对象 client_socket(这个套接字专门用于与该客户端进行后续的一对一通信,与之前用于监听的 server_socket 不同)和客户端的地址 client_address(包含客户端的 IP 地址和端口号,以元组形式呈现,例如 ('192.168.1.100', 56789)),通过打印 client_address 可以知晓客户端的来源信息。
  • 接收客户端数据:在 try 块内,data = client_socket.recv(1024) 调用尝试从与客户端连接的 client_socket 套接字接收数据,参数 1024 表示最多接收 1024 字节的数据,这是一种常见的设置,可根据实际需求调整大小。如果成功接收到了数据(即 data 不为空),message = data.decode('utf-8') 会将接收到的字节数据按照 UTF-8 编码格式解码为字符串形式,赋值给 message 变量,然后通过 print(f'从客户端收到消息: {message}') 将消息内容打印出来,方便查看客户端发送的内容。
  • 处理并返回数据:接着,response = message.upper() 将接收到的消息转换为大写形式,作为对客户端的响应内容。然后,client_socket.send(response.encode('utf-8')) 调用把处理后的响应消息 response 先编码为字节流(使用 UTF-8 编码,确保与接收时的解码格式一致),再通过 client_socket 发送回客户端,同时通过 print(f'已将处理后的消息发送回客户端') 打印发送提示信息,告知服务器端已成功发送响应。
  • 异常处理与资源释放:如果在接收或发送数据过程中出现任何错误(如网络中断、客户端意外关闭连接等),except 块会捕获异常并通过 print('接收或发送数据时出现错误') 打印相应的错误提示信息,便于排查问题。无论是否出现错误,在 finally 块中都会调用 client_socket.close() 方法关闭与当前客户端的套接字连接,释放相关资源,避免资源泄漏,并准备好处理下一个客户端的连接请求。
客户端代码分析
  • 导入模块与创建套接字对象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 导入模块:同服务器端一样,通过 import socket 导入 socket 模块,为后续创建套接字和进行网络通信操作提供必要的功能支持。
  • 创建套接字对象client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 创建了一个客户端使用的套接字对象 client_socket,同样使用 socket.AF_INET(IPv4 地址族)和 socket.SOCK_STREAM(TCP 协议)参数,这使得客户端创建的套接字与服务器端创建的用于监听和接受连接的套接字类型匹配,能够基于 TCP 协议建立可靠的连接进行通信。
  • 连接服务器并发送接收消息
server_address = ('127.0.0.1', 8888)
try:client_socket.connect(server_address)message = "Hello, Server!"client_socket.send(message.encode('utf-8'))print(f'已向服务器发送消息: {message}')data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')
except:print('连接服务器或通信过程中出现错误')
finally:client_socket.close()
  • 定义服务器地址server_address = ('127.0.0.1', 8888) 定义了要连接的服务器的 IP 地址和端口号,这里的地址和端口号必须与服务器端绑定并监听的地址端口完全一致,否则客户端无法正确连接到服务器。
  • 连接服务器client_socket.connect(server_address) 调用尝试向指定的 server_address 对应的服务器发起连接请求,若服务器正常监听且网络可达,连接会成功建立,程序继续往下执行;若连接出现问题(如服务器未启动、网络故障、端口号错误等),会抛出异常,被后续的 except 块捕获处理。
  • 发送消息给服务器:成功连接服务器后,定义要发送的消息 message = "Hello, Server!",然后通过 client_socket.send(message.encode('utf-8')) 调用将消息先编码为字节流(采用 UTF-8 编码格式),再通过 client_socket 发送给服务器,同时通过 print(f'已向服务器发送消息: {message}') 打印发送提示信息,方便查看发送情况。
  • 接收服务器回复:接着调用 client_socket.recv(1024) 尝试从服务器接收回复消息,最多接收 1024 字节的数据,若接收到了数据(即 data 不为空),则通过 response = data.decode('utf-8') 将接收到的字节数据按照 UTF-8 编码格式解码为字符串形式,赋值给 response 变量,并通过 print(f'从服务器收到回复: {response}') 打印出服务器回复的内容,实现客户端与服务器之间的消息交互。
  • 异常处理与资源释放:在整个连接服务器和通信过程中,如果出现任何错误(如连接超时、服务器意外关闭连接等),except 块会捕获异常并通过 print('连接服务器或通信过程中出现错误') 打印相应的错误提示信息,有助于定位问题所在。最后,无论通信是否成功,在 finally 块中都会调用 client_socket.close() 方法关闭客户端套接字,释放相关资源,确保程序的资源管理规范和正常结束。

基于 UDP 的代码分析

服务器端代码分析
  • 导入模块与创建套接字对象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 导入模块:通过 import socket 导入 socket 模块,为后续创建 UDP 套接字及相关网络通信操作提供功能支持,若模块导入失败会导致程序无法使用套接字相关功能。
  • 创建套接字对象server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 创建了一个基于 UDP 协议的数据报套接字对象 server_socket,这里使用 socket.AF_INET 表示 IPv4 地址族,说明基于 IPv4 网络进行通信,而 socket.SOCK_DGRAM 明确了该套接字采用 UDP 协议,UDP 协议是一种无连接、不可靠的通信方式,类似于发送短信,数据被封装成独立的数据报进行发送,不保证数据一定能到达目的地,也不保证数据的顺序性,但它具有开销小、传输速度快的特点,适用于一些对实时性要求较高、对数据完整性要求相对不那么严格的场景。
  • 绑定 IP 地址和端口号并监听
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)
print('UDP服务器已启动,正在监听端口9999...')
  • 定义服务器地址server_address = ('127.0.0.1', 9999) 创建了一个包含 IP 地址(本地回环地址 127.0.0.1,用于在本地进行测试,只有本机上的程序能访问)和端口号 9999 的元组,9999 作为服务器监听的端口号,需确保该端口未被其他程序占用,否则绑定会失败并抛出异常。
  • 绑定套接字到地址server_socket.bind(server_address) 方法将创建好的 server_socket 套接字绑定到指定的 server_address 上,使得服务器在该网络端点上等待接收客户端发送的数据报,一旦绑定成功,服务器就开始在这个端口监听客户端的数据发送。
  • 接收并处理客户端消息
while True:data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'从客户端收到消息: {message}')response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已将处理后的消息发送回客户端')
  • 循环接收客户端数据报while True 构建了一个无限循环,让服务器持续处于监听状态,能够不断接收不同客户端发送的数据报,只要服务器运行,就会一直等待并处理新收到的数据。
  • 接收客户端数据报及地址data, client_address = server_socket.recvfrom(1024) 调用是 UDP 服务器接收数据报的关键操作,它会阻塞程序执行,直到接收到客户端发送的数据报为止,然后返回接收到的数据(字节形式)和客户端的地址(包含客户端的 IP 地址和端口号,以元组形式呈现)。参数 1024 表示最多接收 1024 字节的数据,可根据实际需求调整这个值。
  • 处理并返回数据报:如果接收到了数据(即 data 不为空),首先通过 message = data.decode('utf-8') 将接收到的字节数据按照 UTF-8 编码格式解码为字符串形式,赋值给 message 变量,并通过 print(f'从客户端收到消息: {message}') 打印出客户端发送的消息内容。接着,对消息进行处理(这里同样是转换为大写形式),得到 response 变量,然后调用 server_socket.sendto(response.encode('utf-8'), client_address) 方法将处理后的消息 response 先编码为字节流(使用 UTF-8 编码),再根据接收到的客户端地址 client_address 将数据报发送回客户端,同时通过 print(f'已将处理后的消息发送回客户端') 打印发送提示信息,告知服务器已成功回复客户端。
客户端代码分析
  • 导入模块与创建套接字对象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 导入模块:和前面一样,通过 import socket 导入 socket 模块,以便后续使用套接字相关的功能来创建 UDP 套接字并进行通信操作。
  • 创建套接字对象client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 创建了一个客户端使用的基于 UDP 协议的数据报套接字对象 client_socket,使用 socket.AF_INET(IPv4 地址族)和 socket.SOCK_DGRAM(UDP 协议)参数,使其能够与服务器端创建的 UDP 套接字进行相应的数据报通信。
  • 发送消息并接收回复
server_address = ('127.0.0.1', 9999)
message = "Hello, UDP Server!"
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服务器发送消息: {message}')
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'从服务器收到回复: {response}')
client_socket.close()
  • 定义服务器地址server_address = ('127.0.0.1', 9999) 定义了要发送数据报的目标服务器的 IP 地址和端口号,该地址必须与服务器端绑定并监听的地址端口一致,这样才能确保数据报能准确发送到服务器。
  • 发送消息给服务器:定义要发送的消息 message = "Hello, UDP Server!",然后通过 client_socket.sendto(message.encode('utf-8'), server_address) 调用将消息先编码为字节流(采用 UTF-8 编码格式),再根据指定的 server_address 将数据报发送给服务器,同时通过 print(f'已向服务器发送消息: {message}') 打印发送提示信息,方便查看发送情况。
  • 接收服务器回复:接着调用 client_socket.recvfrom(1024) 尝试从服务器接收回复的数据报,最多接收 1024 字节的数据,当接收到数据报后(即 data 不为空),通过 response = data.decode('utf-8') 将接收到的字节数据按照 UTF-8 编码格式解码为字符串形式,赋值给 response 变量,并通过 print(f'从服务器收到回复: {response}') 打印出服务器回复的内容,实现与服务器之间的简单消息交互。
  • 关闭套接字释放资源:最后调用 client_socket.close() 方法关闭客户端套接字,释放相关资源,确保程序结束时资源被正确回收,避免资源浪费或潜在的资源泄漏问题。

结束语

希望以上对 “套接字是什么?为什么在服务器端与客户端建立了套接字就可以通信了?” 这一问题的全方位解析,包括题目剖析、代码实现与深入的代码分析,能让你顺利敲开网络编程中套接字这扇关键大门。套接字作为网络通信的核心枢纽,无论是基于 TCP 协议构建如网页浏览、文件传输这般稳定可靠的交互场景,还是利用 UDP 协议实现实时性强的游戏、视频流传输等应用,它都展现出无可比拟的灵活性与强大功能,完美架起服务器与客户端之间的信息桥梁。

在后续的编程探索之旅,倘若你立志投身网络应用开发、分布式系统构建等前沿领域,对套接字扎实且深入的理解都将成为你披荆斩棘的有力武器。若在前行路上遭遇网络编程的迷雾,或是渴望拓展更精妙的通信技巧,随时回溯这些知识结晶,我也将一如既往地为你答疑解惑,伴你一路奋进,迈向编程新高峰!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/65041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2017年IMO几何预选题第7题

凸四边形 A B C D ABCD ABCD 有内切圆 I I I, △ D A B \triangle DAB △DAB, △ A B C \triangle ABC △ABC, △ B C D \triangle BCD △BCD, △ C D A \triangle CDA △CDA 的内心分别为 I a I_a Ia​, I b I_b Ib​, I c I_c Ic​, I d I_d Id​. △ A I b I d \…

RabbitMQ案例

1. 导入依赖 <!--AMQP依赖&#xff0c;包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency> 发送消息 注入RabbitTemplate Autowired RabbitT…

特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值

title: 特殊数据类型的深度分析:JSON、数组和 HSTORE 的实用价值 date: 2025/1/4 updated: 2025/1/4 author: cmdragon excerpt: 随着数据管理需求的多样化,许多现代数据库系统开始支持特殊数据类型,以满足更多复杂应用场景的需求。在 PostgreSQL 中,JSON、数组和 HSTOR…

#渗透测试#漏洞挖掘#WAF分类及绕过思路

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

【Logstash02】企业级日志分析系统ELK之Logstash 输入 Input 插件

Logstash 使用 Logstash 命令 官方文档 https://www.elastic.co/guide/en/logstash/current/first-event.html #各种插件 https://www.elastic.co/guide/en/logstash/current/input-plugins.html https://www.elastic.co/guide/en/logstash/current/filter-plugins.html htt…

1.4 java反射机制 简单的java反射机制实践

这是一个项目用于学习反射 第一个demo是利用反射构建一个对象转换为JSON 第二个demo是用于利用类的名字以及方法名就可以直接执行的实例 package com.zy.reflectiondemo.utils;import com.zy.reflectiondemo.annotation.JsonField;import java.lang.reflect.Field; import jav…

C#设计模式(行为型模式):观察者模式

C#设计模式&#xff1a;观察者模式&#xff0c;让对象间通信更优雅 在软件开发中&#xff0c;我们经常会遇到一个对象的状态发生改变&#xff0c;其他对象需要自动更新或做出相应反应的场景。例如&#xff1a; GUI事件处理&#xff1a; 当用户点击按钮时&#xff0c;按钮需要…

【Vue】:解决动态更新 <video> 标签 src 属性后视频未刷新的问题

问题描述 在 Vue.js 项目&#xff0c;当尝试动态更新 <video> 标签的 <source> 元素 src 属性来切换视频时&#xff0c;遇到了一个问题&#xff1a;即使 src 属性已更改&#xff0c;浏览器仍显示旧视频。具体表现为用户选择新视频后&#xff0c;视频区域继续显示之…

BerOS 文件系统路径归一化问题及其 Python 实现

题目背景 本文将讨论一道与操作系统路径归一化有关的问题&#xff0c;该问题来自 BerOS 文件系统 的设计。BerOS 是一个新型操作系统&#xff0c;其文件路径系统允许路径中的分隔符 / 重复出现。例如&#xff0c;以下路径被视为等价的&#xff1a; /usr//local//nginx/sbin//…

Halcon 显示异常

//For Halcon System HOperatorSet.SetSystem("clip_region", "false"); set_system( clip_region, false) *旋转 hom_mat2d_identity (HomMat2DIdentity1) hom_mat2d_rotate (HomMat2DIdentity1, rad( 90), 0, 0, HomMat2DRotate) affine_trans_region …

window11 wsl mysql8 错误分析:1698 - Access denied for user ‘root‘@‘kong.mshome.net‘

&#x1f6a8; 错误分析&#xff1a;1698 - Access denied for user rootkong.mshome.net 这个错误是因为 MySQL 的 root 用户 使用 auth_socket 插件进行身份验证&#xff0c;而不是使用密码。因此&#xff0c;当你尝试从 远程主机 连接时&#xff0c;MySQL 会拒绝访问。 ✅ …

CentOS 7安装Docker详细教程

本文以 CentOS7.8 为例安装 Docker 26.1.4 、Docker Compose、以及 Docker 镜像仓库。 安装方式1&#xff1a;自动安装(使用官方脚本) 使用官网一键安装命令&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 或 使用国内 daocloud 一键安…

Java:缓存:LinkedHashMap实现Lru

文章目录 Lru源码分析 ​​​​​​​​​​​​​​LinkedHashMap维护一个LinkedHashMapEntry<K,V>的双向链表对LinkedHashMap的增删查操作,也会对链表进行相同的操作并改变链表的链接顺序小结使用方法​​​​​​​​​​​​​​应用总结Lru Least Recently Used,…

通过代理用户功能可以实现生产用户的应用滚动修改密码

Oracle通过代理用户功能可以实现生产用户的应用滚动修改密码。 测试例子&#xff1a; 生产用户为jyc密码为jyc 现在要求修改jyc的密码为abc&#xff0c;意味着所有应用都得停止同时修改。 此时可以考虑新建代理用户proxy_jyc&#xff0c;密码为jyc1&#xff08;实际修改建议…

Git 仓库与文件管理笔记

Git 的三种仓库概念 本地仓库 (Local Repository) 位于本地 .git 文件夹中通过 git init 或 git clone 创建存储完整的项目历史和分支信息 远程仓库 (Remote Repository) 位于 GitHub、GitLab 等平台服务器使用 git remote -v 查看所有远程仓库默认远程仓库名通常为 origin 工…

【人工智能数据科学与数据处理】——深入详解人工智能数据科学与数据处理之数据可视化与数据库技术

深入详解人工智能数据科学与数据处理 在人工智能&#xff08;AI&#xff09;的数据科学与数据处理中&#xff0c;数据可视化与数据库技术是两项至关重要的技能。本文将深入探讨数据可视化中的可视化技巧及其应用&#xff0c;以及关系型数据库&#xff08;如MySQL、PostgreSQL&…

DES密码的安全性分析(简化版本)

DES仍是世界上使用最广的&#xff08;DES发行后20年&#xff0c;互联网的兴起&#xff0c;人们开始觉得DES不安全了&#xff0c;但DES的实现成本也越来越低&#xff09; 宏观分析&#xff1a; 密钥空间方面&#xff1a; 密钥长度&#xff1a;DES 算法使用 56 位的密钥对数据…

Elasticsearch 文档批处理 混合处理 批量操作

介绍 在 Elasticsearch 中&#xff0c;批量操作&#xff08;Bulk API&#xff09;允许你一次执行多个文档操作&#xff08;如索引、更新、删除&#xff09;以提高效率。批量操作对于大规模数据的插入、更新或删除尤其有用&#xff0c;可以显著提高处理速度。 批量操作通常是通…

计算机网络原理(谢希仁第八版)第4章课后习题答案

第四章 网络层 详细计算机网络&#xff08;谢希仁-第八版&#xff09;第四章习题全解_计算机网络第八版谢希仁课后答案-CSDN博客 1.网络层向上提供的服务有哪两种&#xff1f;是比较其优缺点。网络层向运输层提供 “面向连接”虚电路&#xff08;Virtual Circuit&#xff09;服…

实现单例模式的五种方式

如何实现一个单例 1、构造器需要私有化 2、提供一个私有的静态变量 3、暴露一个公共的获取单例对象的接口 需要考虑的两个问题 1、是否支持懒加载 2、是否线程安全 1、饿汉式 public class EagerSingleton {private static final EagerSingleton INSTANCE new EagerSi…