当前位置: 首页 > article >正文

练习题: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 socket

server_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 socket

client_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 socket

server_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 socket

client_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 socket

server_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 socket

client_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 socket

server_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 socket

client_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.kler.cn/a/467669.html

相关文章:

  • Zotero7(64位)更新:旧版本的卸载与文献同步问题
  • 【数据结构-堆】力扣3066. 超过阈值的最少操作数 II
  • Day 23:接口文档与 Swagger
  • 机器学习深度学习快速上手
  • 智能工厂的设计软件 应用场景的一个例子:为AI聊天工具添加一个知识系统 之11 方案再探之2 项目文件(修改稿1)
  • 什么情况会导致JVM退出?
  • 每日一学——日志管理工具(ELK Stack)
  • java基础学习(接口和抽象类的区别)
  • Gitea代码仓服务搭建
  • node.js内置模块之---http 和 https 模块
  • 在macOS上安装MySQL
  • vscode代码AI插件Continue 安装与使用
  • 七大设计原则之开闭原则
  • 嵌入式linux中数据结构详解与分析
  • k8s基础(4)—Kubernetes-Service
  • 【socketioxide和axum集成-实现websocket实时通信-Rust点滴】
  • VR线上展厅如何重塑展览展示新生态,引领科技潮流?
  • CSS——2.书写格式一
  • UniApp 性能优化策略
  • Java基于SSM框架的影院选座系统小程序【附源码、文档】