python应用-socket网络编程(2)

     socket 是 Python 标准库中的一个模块,它提供了低级别的网络通信接口。使用 socket 模块,你可以创建客户端和服务器应用程序,以便在网络上进行数据交换。

接着上文我们介绍下socket模块其他的一些函数。

目录

gettimeout() 和settimeout()

socket.error套接字错误

inet_aton() 和inet_ntoa()

inet_pton()和inet_ntop()

socket.getsockopt()

socket.setsockopt()

socket.setblocking()

gethostname() 

gethostbyname(hostname) 

getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

getnameinfo(sockaddr, flags) 

gettimeout() 和settimeout()

调用gettimeout()方法获取默认的套接字超时时间,调用settimeout()方法设定一个套接字超时时间。默认无超时时间,当设置超时时间后,当服务端启动socket套接字时,超过了超时时间没有客户端建立连接,会报错。

import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Waiting for a connection...')
print("Default socket timeout: %s" % server_socket.gettimeout())
server_socket.settimeout(10)
print("Current socket timeout: %s" % server_socket.gettimeout())

举例:设置超时时间10s,执行程序后,10s后程序会报错

Waiting for a connection...
Default socket timeout: None
Current socket timeout: 10.0
Traceback (most recent call last):
    File "/Users/htsc/Desktop/test.py", line 43, in <module>
    connection, client_address = server_socket.accept()
    File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/socket.py", line 293, in accept
    fd, addr = self._accept()
    TimeoutError: timed out

socket.error套接字错误

在网络应用中,经常会遇到一方尝试连接,但另一方由于网络媒介失效或者其他

原因无法响应。Python的socket库提供了一个方法,能通过socket.error异常优雅地处理套接

字错误。

# 创建一个 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接到服务器
server_address = ('localhost', 8080)
try:
  client_socket.connect(server_address)
except socket.error as e:
  print(f'客户端连接报错 {e}')
# 发送数据
try:
  message = 'hello world!'
  client_socket.sendall(message.encode())
except socket.error as e:
  print(f'客户端发送错误 {e}')

服务端没有启动时执行程序报错:

客户端连接报错 [Errno 61] Connection refused
客户端发送错误 [Errno 32] Broken pipe

inet_aton() 和inet_ntoa() 

inet_aton() 将十进制格式的 IPv4 地址转换为二进制格式。

inet_ntoa() 将二进制格式的 IPv4 地址转换回点分十进制格式的字符串。

print(socket.inet_aton('192.168.1.1'))
print(socket.inet_ntoa(b'\xc0\xa8\x01\x01'))
b'\xc0\xa8\x01\x01'
192.168.1.1

inet_pton()和inet_ntop()

同时支持IPv4和IPv6地址的转换。

socket.inet_pton(socket.AF_INET6, ip_str) 来转换IPv6地址的字符串为二进制格式,以及 socket.inet_ntop(socket.AF_INET6, packed_ip) 来转换二进制格式为IPv6地址的字符串。

print(socket.inet_pton(socket.AF_INET6,'A2DD:E543::A234:12AE'))
print(socket.inet_ntop(socket.AF_INET6,b'\xa2\xdd\xe5C\x00\x00\x00\x00\x00\x00\x00\x00\xa24\x12\xae'))'
b'\xa2\xdd\xe5C\x00\x00\x00\x00\x00\x00\x00\x00\xa24\x12\xae'
a2dd:e543::a234:12ae

socket.inet_pton(socket.AF_INET, ip_str) 来转换IPv4地址的字符串为二进制格式,以及 socket.inet_ntop(socket.AF_INET, packed_ip) 来转换二进制格式为IPv4地址的字符串。

print(socket.inet_pton(socket.AF_INET,'192.168.1.1'))
print(socket.inet_ntop(socket.AF_INET,b'\xc0\xa8\x01\x01'))
b'\xc0\xa8\x01\x01'
192.168.1.1

注意事项:

  • inet_aton() 和 inet_ntoa() 是非线程安全的,并且在多线程环境中使用时可能会出现问题。在多线程环境中,建议使用 inet_pton() 和 inet_ntop() 函数,它们提供了更好的错误处理和线程安全性。
  • inet_ntoa() 返回的字符串指针指向一个静态内存区域,因此每次调用都会覆盖上一次的结果。在多次调用 inet_ntoa() 时,需要注意不要依赖之前的结果。

socket.getsockopt()

是一个用于获取套接字选项值的函数。这个函数在 Python 的 socket 模块中定义,用于检索与套接字相关的各种参数和设置。

socket.getsockopt(level, option, buflen=0)

参数说明:

level:表示要获取的选项所在的协议层级。常见的值有:

socket.SOL_SOCKET:通用套接字选项。

socket.IPPROTO_IP:IP选项。

socket.IPPROTO_TCP:TCP选项。

option:表示要获取的选项的名称。例如 socket.SO_REUSEADDR 用于确定套接字地址是否可以在关闭后立即重用,socket.SO_SNDBUF 和 socket.SO_RCVBUF 分别用于获取发送和接收缓冲区的大小等。

buflen:表示获取选项值的缓冲区的长度。在某些情况下,这个参数可能是可选的,并且默认值为0,表示自动分配足够的空间来存储选项值。

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#发送缓冲区大小
print(client_socket.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF))

socket.setsockopt()

用于修改套接字的设置选项值,比上面的setsockopt()方法,多一个value参数,传入要修改的选项值。

举例:修改发送缓冲区大小

# 创建一个 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(client_socket.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF))
#修改发送缓冲区大小 传入value参数=1024102
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,1024102)
print(client_socket.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF))

socket.setblocking()

默认情况下,TCP套接字处于阻塞模式中。

阻塞模式:在阻塞模式下,套接字操作(如发送和接收数据)将等待直到操作完成或发生错误。比如调用recv() 方法并且没有数据可读,程序将会挂起(即暂停执行),直到有数据可读或者连接关闭。

非阻塞模式:参数为 False,则套接字被设置为非阻塞模式。在非阻塞模式下,套接字操作会立即返回,而不会等待操作完成。比如recv() 方法可能会返回一个空字符串,或者引发一个异常(如 socket.error),具体取决于操作系统的行为。

举例:

  • 客户端连接设置为阻塞模式
# 创建一个 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#设置为阻塞模式
client_socket.setblocking(True)
#client_socket.settimeout(0.5)
# 连接到服务器
server_address = ('localhost', 8080)
client_socket.connect(server_address)
print('等待接收数据')
data = client_socket.recv(1024)
print(f'Received {len(data)} bytes {data.decode()}')

执行后程序显示:等待接收数据

  • 客户端设置为非阻塞模式:

以上代码将client_socket.setblocking(True)改为client_socket.setblocking(False)设置为非阻塞模式。重新执行程序后会报错:

BlockingIOError: [Errno 36] Operation now in progress

这个错误是在使用非阻塞套接字(non-blocking socket)时常见的错误之一。当你尝试在一个非阻塞套接字上执行一个需要等待的操作(比如读取或写入数据),而这个操作不能立即完成时,就会抛出这个错误。

在非阻塞模式下,套接字操作不会等待直到数据可用或操作完成。相反,它们会立即返回,并可能抛出 BlockingIOError 异常来表明操作不能立即完成。这意味着你需要检查操作是否成功,并在需要时重试。

gethostname() 

用于获取当前主机名。

import socket
print(socket.gethostname())
192.168.1.8

gethostbyname(hostname) 

用于解析主机名,返回相应的IP地址。这个函数只支持 IPv4 地址。

print(socket.gethostbyname('www.baidu.com'))
print(socket.gethostbyname('localhost'))
180.101.50.242
127.0.0.1

getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

用于解析主机名和服务名,并返回相应的套接字地址信息。它支持 IPv4 和 IPv6 地址,并可以根据需要选择适当的协议。

函数的参数包括:

  • host:主机名或 IP 地址,以字符串形式给出。
  • port:服务名或端口号,以字符串或整数形式给出。为字符串,通常代表一个服务名(如 "http" 或 "ftp");为整数,代表一个具体的端口号(比如80 或者443)。
  • family:地址族,用于指定 IP 地址版本。常见的值包括 socket.AF_INET(IPv4)、socket.AF_INET6(IPv6)和 socket.AF_UNSPEC(不指定,让系统选择)。默认值为 0,表示让系统选择。
  • socktype:套接字类型,指定使用的套接字类型。常见的值有 socket.SOCK_STREAM(流套接字,如 TCP)和 socket.SOCK_DGRAM(数据报套接字,如 UDP)。默认值为 0,表示让系统选择。
  • proto:协议类型。通常可以设置为 0,让系统根据 family 和 socktype 选择合适的协议。
  • flags:标志位,用于控制地址信息的获取方式。例如,socket.AI_PASSIVE 用于获取用于绑定的地址,socket.AI_CANONNAME 用于获取主机名的规范形式等。

举例:分别获取百度域名443端口的一些地址信息

print(socket.getaddrinfo('www.baidu.com',443,family=socket.AF_INET6,type=socket.SOCK_STREAM))
print(socket.getaddrinfo('www.baidu.com',443,family=socket.AF_INET,type=socket.SOCK_DGRAM))
#结果
[(<AddressFamily.AF_INET6: 30>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('240e:e9:6002:15a:0:ff:b05c:1278', 443, 0, 0)), (<AddressFamily.AF_INET6: 30>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('240e:e9:6002:15c:0:ff:b015:146f', 443, 0, 0))]
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('180.101.50.188', 443)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_DGRAM: 2>, 17, '', ('180.101.50.242', 443))]

返回结果list中每个元组解释如下:

这个五元组的意义如下:

第一个切片:2表示 IPv4 协议(socket.AF_INET)

30表示 IPv6 协议(socket.AF_INET6)

第二个切片: 1表示流套接字(socket.SOCK_STREAM),通常用于 TCP 连接。2表示数据报套接字(Socket.SOCK_DGRAM:)

第三个切片:6:这是 TCP 的协议号,17是UDP的协议号

第四个切片: 主机的规范名称,可能为空字符串,

第五个切片:('93.184.216.34', 80):这是实际的 IP 地址和端口号,可以用这些信息来创建套接字并进行连接。

getnameinfo(sockaddr, flags) 

是 getaddrinfo() 的逆操作,它将套接字地址信息转换为主机名和服务名。

参数解释:

sockaddr:套接字地址的元组。对于 IPv4,它通常是一个 (host, port) 的形式,其中 host 是 IP 地址(字符串形式),port 是端口号(整数形式)。

flags:标志位,用于控制解析的方式。例如,socket.NI_NUMERICHOST 和 socket.NI_NUMERICSERV 可以用来强制返回数字形式的主机名和服务名,而不是尝试解析它们。

getnameinfo 函数返回一个包含两个元素的元组:(host, serv),其中 host 是主机名(字符串形式),serv是服务名或端口号(字符串形式)。如果 IP 地址没有与之关联的主机名(即它没有被反向解析),getnameinfo 可能会返回 IP 地址本身作为主机名。

print(socket.getnameinfo(('180.101.50.242', 443),socket.NI_NUMERICSERV))
('180.101.50.242', '443')

共勉: 东汉·班固《汉书·枚乘传》:“泰山之管穿石,单极之绠断干。水非石之钻,索非木之锯,渐靡使之然也。”

-----指水滴不断地滴,可以滴穿石头;

-----比喻坚持不懈,集细微的力量也能成就难能的功劳。

----感谢读者的阅读和学习,谢谢大家。

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值