练习题: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 协议实现实时性强的游戏、视频流传输等应用,它都展现出无可比拟的灵活性与强大功能,完美架起服务器与客户端之间的信息桥梁。
在后续的编程探索之旅,倘若你立志投身网络应用开发、分布式系统构建等前沿领域,对套接字扎实且深入的理解都将成为你披荆斩棘的有力武器。若在前行路上遭遇网络编程的迷雾,或是渴望拓展更精妙的通信技巧,随时回溯这些知识结晶,我也将一如既往地为你答疑解惑,伴你一路奋进,迈向编程新高峰!
相关文章:

练习题:37
目录 Python题目 题目 题目分析 套接字概念剖析 通信原理分析 服务器 - 客户端连接建立过程: 基于套接字通信的底层机制: 代码实现 基于 TCP 的简单服务器 - 客户端通信示例 服务器端代码(tcp_server.py) 客户端代码&a…...

Unity热更文件比较工具类
打包出来的热更文件,如果每次都要全部上传到CDN文件服务器,不进耗费时间长,还浪费流量。 所以让AI写了个简单的文件比较工具类,然后修改了一下可用。记录一下。 路径可自行更改。校验算法这里使用的是MD5,如果使用SH…...

【hustoj注意事项】函数返回值问题
原文 https://lg.h-fmc.cn/index.php/BC/27.html 问题回顾 此题目选自HFMC_OJ:4312: 简单递归操作 hustoj测试 此问题错误的代码是 #include<bits/stdc.h> using namespace std; int a[10000];int n; int b[10000]{0}; int pailie(int deep) {int i; for(…...

实现一个通用的树形结构构建工具
文章目录 1. 前言2. 树结构3. 具体实现逻辑3.1 TreeNode3.2 TreeUtils3.3 例子 4. 小结 1. 前言 树结构的生成在项目中应该都比较常见,比如部门结构树的生成,目录结构树的生成,但是大家有没有想过,如果在一个项目中有多个树结构&…...

数势科技:解锁数据分析 Agent 的智能密码(14/30)
一、数势科技引领数据分析变革 在当今数字化浪潮中,数据已然成为企业的核心资产,而数据分析则是挖掘这一资产价值的关键钥匙。数势科技,作为数据智能领域的领军者,以其前沿的技术与创新的产品,为企业开启了高效数据分析…...

机器学习之过采样和下采样调整不均衡样本的逻辑回归模型
过采样和下采样调整不均衡样本的逻辑回归模型 目录 过采样和下采样调整不均衡样本的逻辑回归模型1 过采样1.1 样本不均衡1.2 概念1.3 图片理解1.4 SMOTE算法1.5 算法导入1.6 函数及格式1.7 样本类别可视化理解 2 下采样2.1 概念2.2 图片理解2.3 数据处理理解2.4 样本类别可视化…...

解决 ssh connect to host github.com port 22 Connection timed out
一、问题描述 本地 pull/push 推送代码到 github 项目报 22 端口连接超时,测试连接也是 22 端口连接超时 ssh 密钥没问题、也开了 Watt Toolkit 网络是通的,因此可以强制将端口切换为 443 二、解决方案 1、测试连接 ssh -T gitgithub.com意味着无法通…...

mybatis/mybatis-plus中mysql报错
文章目录 一、sql执行正常,mybatis报错二、sql执行正常,mybatis-plus报错直接改变字段利用mybatis-plus特性处理 总结 一、sql执行正常,mybatis报错 Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "ur" <K_ISOLATION>a…...

在ros2 jazzy和gazebo harmonic下的建图导航(cartographer和navigation)实现(基本)
我的github分支!!! 你可以在这里找到相对应的源码。 DWDROME的MOGI分支 来源于!! MOGI-ROS/Week-3-4-Gazebo-basics 学习分支整理日志 分支概述 这是一个用于个人学习的新分支,目的是扩展基本模型并添加…...

《Rust权威指南》学习笔记(五)
高级特性 1.在Rust中,unsafe是一种允许绕过Rust的安全性保证的机制,用于执行一些Rust默认情况下不允许的操作。unsafe存在的原因是:unsafe 允许执行某些可能被 Rust 的安全性检查阻止的操作,从而可以进行性能优化,如手…...

GitHub的简单操作
引言 今天开始就要开始做项目了,上午是要把git搭好。搭的过程中遇到好多好多的问题。下面就说一下git的简单操作流程。我们是使用的GitHub,下面也就以这个为例了 一、GitHub账号的登录注册 https://github.com/ 通过这个网址可以来到GitHub首页 点击中间绿色的S…...

「Mac畅玩鸿蒙与硬件54」UI互动应用篇31 - 滑动解锁屏幕功能
本篇教程将实现滑动解锁屏幕功能,通过 Slider 组件实现滑动操作,学习事件监听、状态更新和交互逻辑的实现方法。 关键词 滑动解锁UI交互状态管理动态更新事件监听 一、功能说明 滑动解锁屏幕功能包含以下功能: 滑动解锁区域:用…...

SMMU软件指南之系统架构考虑
安全之安全(security)博客目录导读 目录 5.1 I/O 一致性 5.2 客户端设备 5.2.1 地址大小 5.2.2 缓存 5.3 PCIe 注意事项 5.3.1 点对点通信 5.3.2 No_snoop 5.3.3 ATS 5.4 StreamID 分配 5.5 MSI 本博客介绍与 SMMU 相关的一些系统架构注意事项。 5.1 I/O 一致性 如…...

使用高云小蜜蜂GW1N-2实现MIPI到LVDS(DVP)转换案例分享
作者:Hello,Panda 大家晚上好,熊猫君又来了。 今天要分享的是一个简单的MIPI到LVDS(DVP)接口转换的案例。目的就是要把低成本FPGA的应用潜力充分利用起来。 一、应用背景 这个案例的应用背景是:现在还在…...

「C++笔记」unordered_map:哈希化的无序映射函数(键值对)
unordered_map 是 C 中一个经过哈希函数(Hash)处理的映射(map)容器。 本文中的map和set是差不多的,unordered_map与unordered_set也是对应的。所以不再单独写一篇了。 这里的内容建议看完本文之后再回过头来看 二者虽然…...

Linux 安装jdk
1、官网下载jdk https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html2、以tar包为例,在window或者Linux解压都可以,这里直接在win解压了,上传到服务器 3、在/usr/local/ 创建jdk目录,将jdk上传到…...

asp.net core 发布到iis后,一直500.19,IIS设置没问题,安装了sdk,文件夹权限都有,还是报错
原因就是没有安装ASP.NET Core 9.0 Runtime (v9.0.0) - Windows Hosting Bundle,我是只安装了.net core的sdk,下面介绍下sdk和hosting bundle的关系 在 .NET Core 和 ASP.NET Core 的开发中,SDK(Software Development Kit&#x…...

【Go】运行自己的第一个Go程序
运行自己的第一个Go程序 一、Go语言的安装Go环境安装查看是否安装成功配置GOPROXY(代理) 二、Goland安装三、Goland破解四、新建项目 开一篇专栏记录学习Go的过程,一门新语言从hello world开始,这篇文章详细讲解Go语言环境搭建及hello world实现 一、Go语…...

qt qss文件的使用
qt样式的修改方式 一 通过ui界面的改变样式表来直接修改显示效果。 不推荐,其他人不好修改,不够直观,不易维护。 二 通过setStyleSheet接口修改。 一般,界面很少的时候可以使用。一旦界面多起来,代码部分就显得杂乱…...

【管道——二分+区间合并】
题目 思路 区间合并 1、按照左端点排序2、遍历窗口,若窗口非法,继续遍历;否则执行33、若是第一个窗口,设定合并结果初值,判断结果左端点是否造成“起点过大”,是,FALSE退出;否则执行…...

宽带、光猫、路由器、WiFi、光纤之间的关系
1、宽带(Broadband) 1.1 宽带的定义宽带指的是一种高速互联网接入技术,通常包括ADSL、光纤、4G/5G等不同类型的接入方式。宽带的关键特点是能够提供较高的数据传输速率,使得用户可以享受到稳定的上网体验。 1.2 宽带的作用宽带是…...

如何排查 Apache Doris 中 “Failed to commit txn“ 导入失败问题?
今天来聊聊 Doris 数据导入那些事儿。你是不是在数据导入的时候遇到各种状况,让人头疼不已?别担心,这篇文章给你答案! 在 Doris 的版本里,< 2.0.3 的时候,数据迁移存在一些已知的问题,比如可…...

回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测
回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测 数据准备&#x…...

HCIA-Access V2.5_7_3_XG(S)原理_关键技术
为什么需要测距 因为上行链路只有一根纤,而且每一个ONU到OLT的距离是不一样的,虽然上行通过TDMA技术,让每一个ONU在不同的时间段发送数据,但是仍然有可能在同一时刻到达分光器,产生数据冲突。 有测距的信元传输 所以为了避免碰撞冲突,通过ONU在注册的时候就会启动测距…...

leetcode hot 100 不同路径
62. 不同路径 已解答 中等 相关标签 相关企业 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )…...

智慧工地解决方案 1
建设背景与挑战 工地施工现场环境复杂,人员管理难度大,多工种交叉作业导致管理混乱,事故频发。传统管理方式难以实现科学、有效、集中式的管理,特别是在环境复杂、地点分散的情况下,监管困难,取证复杂。施…...

LeetCode -Hot100 - 53. 最大子数组和
前言 本专栏主要通过“LeetCode 热题100”,来捡起自己本科阶段的算法知识与技巧。语言主要使用c/java。如果同样正在练习LeetCode 热题100的朋友欢迎关注或订阅本专栏。有疑问欢迎留言交流~ 题目描述 题目链接 示例 1: 输入:nums [-2,1…...

php 多进程那点事,用 swoole 如何解决呢 ?
在 PHP 中,多进程的处理通常会遇到一些挑战,比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架,旨在为 PHP 提供异步、并发、协程等功能,解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…...

探索AI在地质科研绘图中的应用:ChatGPT与Midjourney绘图流程与效果对比
文章目录 个人感受一、AI绘图流程1.1 Midjourney(1)环境配置(2)生成prompt(3)完善prompt(4)开始绘图(5)后处理 1.2 ChatGPT不合理的出图结果解决方案 二、主题…...

【竞技宝】CS2:HLTV 2024 TOP11-w0nderful
北京时间2025年1月4日,HLTV年度选手排名正在持续公布中,今日凌晨正式公布了今年的TOP11为NAVI战队的w0nderful。 选手简介 w0nderful是一名来自于乌克兰的CS选手,现年20岁,目前在比赛中司职狙击手。w0nderful于2020年开启了自己的…...