1. 广播(Broadcast)
广播的定义
广播是一种数据传输方式,数据包从一个源发送到网络中所有节点。广播是网络中的一对多通信方式,所有处于同一网络段的设备都能接收到广播数据包。
广播的特点
- 范围限制:广播数据包只在本地网络(LAN)传播,不会穿越路由器到达其他网络段。
- 地址格式:IPv4中,广播地址通常为子网的最高地址。例如,192.168.1.0/24网络的广播地址为192.168.1.255。
广播的Python实现
注:在下列示例代码中,先执行接收端代码再执行发送端代码。
发送端(broadcast_sender.py)
import socket
import struct
def get_local_ip():
"""
获取本地计算机的 IP 地址。
:return: 本地 IP 地址
"""
# 创建一个 UDP 套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# 尝试连接一个外部的地址(不会实际发送数据)
s.connect(('8.8.8.8', 80))
local_ip = s.getsockname()[0]
except Exception:
local_ip = '127.0.0.1'
finally:
s.close()
return local_ip
def calculate_broadcast_address(ip, subnet_mask):
"""
计算给定 IP 地址和子网掩码的广播地址。
:param ip: IP 地址
:param subnet_mask: 子网掩码
:return: 广播地址
"""
ip_addr = struct.unpack('!I', socket.inet_aton(ip))[0]
mask = struct.unpack('!I', socket.inet_aton(subnet_mask))[0]
broadcast = ip_addr | ~mask
return socket.inet_ntoa(struct.pack('!I', broadcast & 0xFFFFFFFF))
def send_broadcast(message, port):
local_ip = get_local_ip()
print(f'本机IP地址为:{local_ip}')
subnet_mask = '255.255.255.0' # 假设子网掩码为 255.255.255.0
# 计算广播地址
broadcast_address = calculate_broadcast_address(local_ip, subnet_mask)
print(f"广播地址为: {broadcast_address}")
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 创建 UDP 套接字用于广播
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
try:
# 发送广播消息
sock.sendto(message, (broadcast_address, port))
finally:
sock.close()
print(f'已将广播消息 [{message}] 发送到 [{broadcast_address}:{port}]')
if __name__ == "__main__":
send_broadcast(b'Hello, World!', 5000)
在这个代码示例中,创建了一个UDP套接字,并设置了SO_BROADCAST选项,以允许套接字发送广播消息。然后,将消息发送到广播地址。
接收端(broadcast_receiver.py)
import socket
def receive_broadcast(port):
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 绑定到任意地址和指定端口
sock.bind(('', port))
print("接收广播消息中...")
while True:
data, addr = sock.recvfrom(1024)
print(f'收到来自{addr}的广播消息: {data.decode()}')
if __name__ == "__main__":
receive_broadcast(5000)
在这个代码示例中,创建了一个UDP套接字,并将其绑定到本地地址和端口5000,以接收广播消息。
2. 组播(Multicast)
组播的定义
组播是一种数据传输方式,数据包从一个源发送到网络中一组特定的节点(即一个组),而不是所有节点。组播是一对多的通信方式,但仅限于加入该组播组的接收者。
组播的特点
- 范围灵活:组播数据包可以在局域网(LAN)或广域网(WAN)中传播,取决于网络配置。
- 地址格式:IPv4组播地址范围是224.0.0.0到239.255.255.255。
组播的Python实现
注:在下列示例代码中,先执行接收端代码再执行发送端代码。
发送端(multicast_sender.py)
import socket
import struct
def multicast_message(message, group, port):
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 设置TTL,控制数据包在网络中可以经过的最大跳数
ttl = struct.pack('b', 1)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
try:
# 发送数据到组播地址
sock.sendto(message, (group, port))
finally:
sock.close()
print(f'已将组播消息 [{message}] 发送到 [{group}:{port}]')
if __name__ == "__main__":
multicast_message(b'Hello, World!', '224.0.0.2', 5000)
在这个代码示例中,创建了一个UDP套接字,并设置了IP_MULTICAST_TTL选项,以控制组播数据包的生存时间(TTL)。然后,将消息发送到指定组播地址及端口号。
接收端(multicast_receiver.py)
import socket
import struct
def receive_multicast(group, port):
# 创建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 绑定到组播地址和端口
sock.bind(('', port))
# 加入组播组
mreq = struct.pack('4sl', socket.inet_aton(group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print('接收组播消息中...')
while True:
data, addr = sock.recvfrom(1024)
print(f'接收到来自{addr}的组播消息: {data.decode()}')
if __name__ == "__main__":
receive_multicast('224.0.0.2', 5000)
在这个代码示例中,创建了一个UDP套接字,并将其绑定到本地地址和端口5000。然后,通过设置IP_ADD_MEMBERSHIP选项来加入组播组,以接收组播消息。
3. setsockopt
方法详解
sock.setsockopt
方法在 Python 的 socket
模块中用于设置套接字的选项。这个方法非常灵活,可以控制许多与套接字相关的行为。下面是 sock.setsockopt
可以设置的常见参数及其含义的详细解释:
level
参数
level
参数指定了套接字选项的协议层级。常见的层级有:
socket.SOL_SOCKET
:套接字级别的选项。socket.IPPROTO_IP
:IPv4 协议级别的选项。socket.IPPROTO_IPV6
:IPv6 协议级别的选项。socket.IPPROTO_TCP
:TCP 协议级别的选项。socket.IPPROTO_UDP
:UDP 协议级别的选项。
optname
参数
optname
参数指定了具体的选项名称。以下是一些常见选项及其含义:
套接字级别选项 (socket.SOL_SOCKET
)
socket.SO_BROADCAST
:允许套接字发送广播消息。socket.SO_REUSEADDR
:允许重用本地地址和端口。socket.SO_KEEPALIVE
:启用TCP保持连接机制。socket.SO_LINGER
:控制套接字关闭时的行为。socket.SO_RCVBUF
:设置接收缓冲区大小。socket.SO_SNDBUF
:设置发送缓冲区大小。socket.SO_RCVTIMEO
:设置接收超时时间。socket.SO_SNDTIMEO
:设置发送超时时间。
IPv4 选项 (socket.IPPROTO_IP
)
socket.IP_MULTICAST_TTL
:设置组播数据包的生存时间(TTL)。socket.IP_MULTICAST_LOOP
:控制组播数据包是否回送到本地。socket.IP_ADD_MEMBERSHIP
:加入组播组。socket.IP_DROP_MEMBERSHIP
:退出组播组。
TCP 选项 (socket.IPPROTO_TCP
)
socket.TCP_NODELAY
:禁用Nagle算法,降低延迟。
value
参数
value
参数指定了选项的值,可以是整数、布尔值或特定的结构体。以下是一些常见的值及其格式:
布尔值选项
一些选项可以使用布尔值(0或1)来开启或关闭。例如:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
socket.SO_BROADCAST选项被设置为1,启用了广播功能,使套接字可以发送广播消息。
整数选项
一些选项需要整数值,例如设置缓冲区大小。整数值通常用于调整套接字的性能参数。
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096)
socket.SO_RCVBUF选项被设置为4096,指定接收缓冲区的大小为4096字节。这有助于控制数据接收时的缓冲能力。
结构体选项
某些选项需要特定的结构体,这通常涉及复杂的配置,如组播组的加入。这类选项通常通过struct模块来构造所需的结构体。
import struct
# 构造组播请求结构体
mreq = struct.pack('4sl', socket.inet_aton('224.0.0.2'), socket.INADDR_ANY)
# 加入组播组
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
使用struct.pack方法将组播地址和本地接口(socket.INADDR_ANY表示本地所有接口)打包成一个结构体。然后,通过socket.IP_ADD_MEMBERSHIP选项将套接字加入到指定的组播组。
常见示例
以下是一些常见的setsockopt用法示例,展示如何使用不同类型的值来配置套接字。
设置广播
# 启用广播
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
设置地址重用
# 启用地址重用
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
加入组播组
import struct
# 构造组播请求结构体
mreq = struct.pack('4sl', socket.inet_aton('224.0.0.2'), socket.INADDR_ANY)
# 加入组播组
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
设置组播TTL
# 设置组播TTL为2
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
设置发送和接收超时
import struct
# 设置接收超时为5秒
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, struct.pack('ll', 5, 0))
# 设置发送超时为5秒
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, struct.pack('ll', 5, 0))
在设置发送和接收超时的示例中,使用struct.pack方法将超时时间(秒和微秒)打包成一个结构体,然后通过socket.SO_RCVTIMEO和socket.SO_SNDTIMEO选项分别设置接收和发送的超时时间。