1.1 简介
因为要放上具体的代码,今天就从新浪转战CSDN了
原来对Python有了一定的了解,2和3的差距有时候还是挺折腾人的。原来刷过几个oj,上面都是用Python解决的,对于大部分人来说问题应该不是特别大。
今天开始lz就按照《Python Network Programming Cookbook》这本书来进行追踪,求监督!
ps.第一章还是按照Python3进行代码的,后面的章节,因为有涉及到操作系统的问题,话说我一直不知道为什么Python for Win里面竟然会有os.fork()……后面就直接在Ubuntu 12.04上面实验了。Ubuntu自带Python 2.7,正好和书本当中的版本一样。
1.2 打印设备名和IPv4地址
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_1_local_machine_info.py
import socket
def print_machine_info():
host_name = socket.gethostname()
ip_address = socket.gethostbyname(host_name)
print ("host name is %s " %host_name)
print ("ip address is %s" %ip_address)
if __name__ == '__main__':
print_machine_info()</span></span></span>
__name__注意是两个下划线,表示调用程序的进程名,如果在命令行中运行脚本,__name__的值就是'__main__'
print函数就是前面格式化,用引号包住,后面一个%,然后再把需要的输出用括号括起来。
1.3 获取远程设备的IP地址
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_2_remote_machine_info.py
import socket
def get_remote_machine_info():
remote_host = 'www.python.org'
try:
print("IP address: %s" %socket.gethostbyname(remote_host))
except socket.error as err_msg:
print("%s %s" %(remote_host, err_msg))
if __name__ == '__main__':
get_remote_machine_info()</span></span></span>
主机名改变即可,socket.gethostbyname的参数改变。
1.4 将IPv4地址转换成不同的格式
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_3_ipv4_address_conversion.py
import socket
from binascii import hexlify
def convert_ipv4_address():
for ip_addr in ['127.0.0.1','192.168.0.1']:
packed_ip_addr = socket.inet_aton(ip_addr)
unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)
print(" IP address %s => Packed: %s , UnPacked: %s "
%(ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr))
if __name__ == '__main__':
convert_ipv4_address()</span></span></span>
调用了binascii模块的hexlify函数,以十六进制形式表示二进制数据。
1.5 通过制定的端口和协议找到服务名
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_4_finding_service_name.py
import socket
def find_service_name():
protocolname = 'tcp'
for port in [80,25]:
#for port in range(20,81):
print("Port: %s => service name: %s " %(port, socket.getservbyport(port,protocolname)))
print("Port: %s => service name: %s " %(53, socket.getservbyport(53, 'udp')))
if __name__ == '__main__':
find_service_name()</span></span></span>
1.6 主机字节序和网络字节序之间相互转换
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_5_integer_conversion.py
import socket
def convert_integer():
data = 1234
# 32 bit
print("original: %s => Long host byte order: %s, Network byte order: %s"
% (data, socket.ntohl(data), socket.htonl(data)))
# 16 bit
print("original: %s => Short host byte order: %s, Network byte order: %s"
% (data, socket.ntohs(data), socket.htons(data)))
data1 = socket.htonl(data)
data2 = socket.ntohl(data1)
print("%s %s %s" %(data,data1,data2))
if __name__ == '__main__':
convert_integer()</span></span></span>
socket库中的类函数ntohl()把网络字节序转换成了长整形主机字节序。函数名中的n表示网络,h表示主机,l表示长整形,s表示短整型,即16位。
1.7 设定并获取默认的套接字超时时间
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"># 1_6_socket_timeout.py
import socket
def test_socket_timeout():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Default socket timeout: %s" %s.gettimeout())
s.settimeout(100)
print("Current socket timeout: %s" %s.gettimeout())
if __name__ == '__main__':
test_socket_timeout()
</span></span></span>
需要创建套接字对象。套接字构造方法第一个参数是地址族,第二个参数是套接字类型。
socket=socket.socket(family,type),family的值可以是AF_UNIX(Unix域,用于同一台机器上的进程通讯),也可以是AF_INET(对于IPv4协议的TCP和UDP)。至于type参数,SOCK_STREAM(流套接字)或者SOCK_DGRAM(数据报文套接字),SOCK_RAW(raw套接字)。
gettimeout()方法获取套接字的超时时间,再调用settimeout()方法修改超时时间。传给settimeout()的参数可以是秒数,非负浮点数,也可以是None。如果是None,就禁用了套接字操作的超时检测。
这里提到了socket,那么就说一下socket套接字的用法。
先说一下SNTP服务器,(这样1.14好像没有写的必要了?)这里感谢点击打开链接,写的很详细,大家可以参考。后面还有更加复杂的多线程,后面章节会说明如何编写。
server端:
第一步,创建一个socket对象,参数已经说了,见上。
第二步,socket进行绑定到一个地址上面,进行监听。
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">socket.bind(address)
sock.listen(backlog)</span></span></span>
address是一个(ip,port)的双元素二元组,绑定的时候需要这么操作 socket.bind((ip,port))
backlog表示最多连接数,server收到连接请求之后,这些请求会进行排队,如果队列已经满了,超过backlog数目,那么拒绝请求。
第三步,server通过socket.accept()接收client端来的请求链接。
代码如下,一般会有一个while True的循环,表示服务器一直进行监听。
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">while True:
client_socket, address = socket.accept()
data = client_socket.recv(data_payload).decode()
client_socket.sendall(msg.encode())</span></span></span>
调用accept方法的时候,socket会进入waiting(或阻塞)状态。client端请求链接,建立连接并返回服务器。accept()方法返回一个含有两个元素的双元组,形如client_socket,address。第一个client_socket是新的socket对象,server通过它与client发送信息,address是client的address。
第四步,接收、处理和发送信息。
代码和第三步的代码在一起了。这里需要注意一点,Python3.4有一个捉鸡的问题,上文档
In Python3, bytes strings and unicode strings are now two different types. Since sockets are not aware of string encodings, they are using raw byte strings, that have a slightly different interface from unicode strings.</