这里将详细讲解一下socket模块中一些常用到的函数/属性。
1. socket模块属性
下面列出了一些常用的socket模块属性。(引用自《python核心编程》)
更详细的函数说明,请看python官方文档中的socket模块:https://docs.python.org/2/library/socket.html
2. 实际应用中的实例分析
2.1 打印设备名和IPV4地址
函数原型: socket.gethostname() 和 socket.gethostbyname(hostname) 。也可以使用socket.getfqdn() 返回完整的域名字。
1
2
3
4
5
6
|
host_name
=
socket.gethostname()
# 获取设备名
ip_addr
=
socket.gethostbyname(host_name)
# 获取本机IP
remote_host
=
'www.python.org'
ip_addr
=
socket.gethostbyname(remote_host)
# 获取远程主机的IP
|
2.2 打印本地网络接口的IPV4地址
1
2
3
4
5
6
7
8
|
import
socket, fcntl, struct
ifname
=
"eth0"
s
=
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
inet
=
fcntl.ioctl(s.fileno(),
0x8915
, struct.pack(
'256s'
, ifname[:
15
]))
ret
=
socket.inet_ntoa(inet[
20
:
24
])
print
ret
|
2.3 IPV4地址格式转换
函数原型:socket.inet_aton(ip_string) 和socket.inet_ntoa(packed_ip) 。 如果需要ipv6支持,就需要使用inet_pton()/inet_ntop()函数。
1
2
3
4
5
6
7
8
|
from
binascii
import
hexlify
# 以十六进制形式表示二进制数据
ip_addr
=
'192.168.1.15'
packed_ip_addr
=
socket.inet_aton(ip_addr)
# 将字符串形式的IP地址转换成32位二进制格式
unpacked_ip_addr
=
socket.inet_ntoa(packed_ip_addr)
# 与上面相反
print
"IP Address: %s"
%
ip_addr
print
"Packed: %s"
%
hexlify(packed_ip_addr)
print
"Unpacked: %s"
%
unpacked_ip_addr
|
2.4 通过指定端口和协议找服务名
函数原型: socket.getservbyname(servicename[, protocolname]) 和 socket.getservbyport(port[, protocolname]) 。协议名可以省略,默认是找所有的协议,如需指定,只能是‘tcp’或者‘udp’。
1
2
|
serverport
=
socket.getservbyname(
'http'
,
'tcp'
)
servername
=
socket.getservbyport(
80
,
'tcp'
)
|
2.5 主机字节序与网络字节序之间的转换
1
2
3
4
5
6
7
|
data
=
1234
# 32-bit
print
"Original: %s => Long host byte: %s => Network byte: %s"
%
(data,socket.ntohl(data),socket.htonl(data)
# 16-bit
print
"Original: %s => Short host byte: %s => Network byte: %s"
%
(data,socket.ntohs(data),socket.htons(data)
## 函数名中的n表示网络; h表示主机; l表示长整型; s表示短整型,即16位。
|
2.6 设置套接字超时
1
2
3
|
s
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(
100
)
print
s.gettimeout()
|
2.7 设置缓冲区大小
setsockopt()方法接收三个参数:level、optname和value。其中,optname是选项名,value是该选项名的值。value所用的符号常量(SO_*等)可以在socket模块中查看。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
socket
s_buf
=
1024
r_buf
=
1024
sock
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
bufsize
=
sock.getsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF)
print
"bufsize [Before]: %d"
%
bufsize
sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY,
1
)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,s_buf)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF,r_buf)
bufsize
=
sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print
"bufsize [After]: %d"
%
bufsize
|
2.8 设置套接字(非)阻塞模式
默认情况下,TCP套接字是处于阻塞模式下的。这种模式会使操作效率低下,如果两个程序最后都在等待对方发送或者接收数据,就有可能导致死锁。调用setblocking()方法可以改变套接字的阻塞标志。默认值为1,表示会阻塞。传入值为0时则关闭阻塞。如果套接字为非阻塞,并且没有为处理操作做好准备,就会产生一个socket.error。解决的方法是设置一个超时值。
1
2
3
4
5
6
7
8
|
s
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setblocking(
1
)
s.settimeout(
0.5
)
s.bind((
'127.0.0.1'
,
0
))
socket_addr
=
s.getsockname()
print
str
(socket_addr)
while
1
:
s.listen(
1
)
|
2.9 地址重用
当连接有意或无意关闭后,如果想再在这个端口上运行套接字服务端就会抛出一个"Address alreadly in use"异常。解决的方法是启用套接字重用选项SO_REUSEADDR。
1
2
3
4
5
|
s
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,
1
)
s.bind((bind_ip, bind_port))
s.listen(
5
)
...
|
2.10 处理套接字错误
在socket套接字使用过程中,很可能会出现很多错误,可以使用try-except语句处理异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
# First try-except block
try
:
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except
socket.error, e:
print
"Error creating socket: %s"
%
e
# Second try-except block
try
:
s.connect((host, port))
except
socket.gaierror, e:
print
"Address-related error connecting to server: %s"
%
e
sys.exit(
1
)
except
socket.error, e:
print
"Connection error: %s"
%
e
sys.exit(
1
)
# Third try-except block
try
:
s.sendall(
"GET %s HTTP/1.0\r\n\r\n"
%
filename)
except
socket.error, e:
print
"Error sending data: %s"
%
e
sys.exit(
1
)
while
1
:
# Fourth try-except block
try
:
buf
=
s.recv(
1024
)
except
socket.error, e:
print
"Error receiving data: %s"
%
e
sys.exit(
1
)
|