2.1 理解socket
socket 是操作系统中I/O系统的延伸部分,它可以使进程和机器之间通信成为可能。
把对网络支持加入操作系统,是以一种扩展现有文件描述符结构的方法来实现的。
Python通过socket模块提供访问操作系统socket库的接口,建立socket时,你只需要调用这个模块的函数和常量。
2.2 建立socket
对于一个客户端来说,建立一个socket需要两个步骤:
需要告诉系统两件事,通信类型的协议家族,通信类型指明用什么协议来传输数据,
如IPV4、IPV6、IPX/SPX、 AFP,目前大多数据都是 IPV4,对应AF_INET .
协议家族一般表示TCP通信(SOCK_STREAM)或表示UDP通信(SOCK_DGRAM)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((“www.example.com”,80))
connect.py
#!/usr/bin/python
import socket
print "Creatint socket....."
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "done."
print "Connecting to remote host..."
s.connect(("www.google.com",80))
print "done."
2.2.1 寻找端口号
你可以通过/etc/services知道已知服务器端口号列表。
getservbyname()函数需要两个参数 协议名、端口名。
connect2.py
#!/usr/bin/python
import socket
print "Creating socket...."
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "done."
print "looking up port number..."
port = socket.getservbyname('http','udp')
print "done."
print "Connecting to remote host on port %d..." %port
s.connect(("www.google.com",port))
print "done."
2.2.2 从socket获取信息
connect3.py
#!/usr/bin/python
import socket
print "Creating socket...."
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "done."
print "looking up port number..."
port = socket.getservbyname('http','udp')
print "done."
print "Connecting to remote host on port %d..." %port
s.connect(("www.google.com",port))
print "done."
print "Connecting from",s.getsockname()
print "Connecting to",s.getpeername()
第一条显示你本身的IP地址和端口号(端口号由操作系统分配)
第二条显示远程机器的IP地址和端口号
2.3 利用socket通信
建立socket通信后,就可以利用它进行发送和接收数据,Python提供两种方法:
socket对象提供了操作系统的send() 、sendto()、recv()、recvfrom()调用的接口。
特殊需求时,如读写数据时协议可以详细控制、使用二进制协议传送固定大小数据时、
数据超时需要特别处理时、 编写UDP程序时,都需要用socket对象。
文件对象提供了read()、write()和readline()这些更典型的Python接口。
文件对象很难用于UDP
2.4 处理错识
在Python中,当网络出现错误时,socket代码会产生异常。这些错误是网络程序不能忽略的。
2.4.1 socket异常
socketerror.py
#!/usr/bin/python
import socket,sys
host = sys.argv[1]
textport = sys.argv[2]
try:
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except socket.error,e:
print "Strange error createing socket:%s" %e
sys.exit(1)
try:
port = int(textport)
except ValueError:
try:
port = socket.getservbyname(textport,'tcp')
except socket.error,e:
print "Counldn't find your port:%s" %e
sys.exit(1)
try:
s.connect((host,port))
except socket.gaierror,e:
print "Address -related error connecting to sever:%s" %e
sys.exit(1)
except socket.error,e:
print "Connecting error %s" %e
sys.exit(1)
try:
s.sendall("GET / HTTP/1.0\r\n\r\n")
except socket.error,e:
print "Error sending data:%s" %e
sys.exit(1)
s.shutdown(1)
while 1:
try:
buf = s.recv(2048)
except socket.error,e:
print "Error receiving data:%s" %e
sys.exit(1)
if not len(buf):
break;
sys.stdout.write(buf)
Python的socket模块定义4种可能的异常:
2.4.2 遗漏的错误
对于很多操作系统,有时候在网络上发送的数据调用会在远程服务器确保已经收到信息之前返回,因此很有可能一个来自对sendall()成功调用返回的数据,事实上永远没有被收到。
为了解决这个问题,一旦结束写操作,你应该立即调用shutdown()函数,这样会强制清除缓存里面的内容,同时如果有任何问题会产生一个异常。
请牢记,数据只有在你调用了shutdown()函数才能确保被发送。
shutdown.py
#!/usr/bin/python
import socket,sys,time
host = sys.argv[1]
textport = sys.argv[2]
filename = sys.argv[3]
try:
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except socket.error,e:
print "Strage error creating socket:%s" %e
sys.exit(1)
try:
port = int(textport)
except ValueError:
try:
port = socket.getservbyname(textport,'tcp')
except socket.error,e:
print "Couldn't find your port :%s" %e
sys.exit(1)
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)
print "sleeping......"
time.sleep(10)
print "Continuing."
try:
s.sendall("GET / HTTP/1.0\r\n\r\n")
except socket.error,e:
print "Error sending data:%s" %e
sys.exit(1)
s.shutdown(1)
while 1:
try:
buf = s.recv(2048)
except socket.error,e:
print "Error receiving data:%s" %e
sys.exit(1)
if not len(buf):
break;
sys.stdout.write(buf)
s.close()
2.4.3 文件类对象引起的错误
shutdownfile.py
import socket,sys,time
host = sys.argv[1]
textport = sys.argv[2]
filename = sys.argv[3]
try:
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
except socket.error,e:
print "Strage error creating socket:%s" %e
sys.exit(1)
try:
port = int(textport)
except ValueError:
try:
port = socket.getservbyname(textport,'tcp')
except socket.error,e:
print "Couldn't find your port :%s" %e
sys.exit(1)
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)
fd = s.makefile('w',0)
print "sleeping......"
time.sleep(10)
print "Continuing."
try:
fd.write("GET / HTTP/1.0\r\n\r\n")
except socket.error,e:
print "Error sending data:%s" %e
sys.exit(1)
s.shutdown(1)
while 1:
try:
buf = fd.read(2048)
except socket.error,e:
print "Error receiving data:%s" %e
sys.exit(1)
if not len(buf):
break;
sys.stdout.write(buf)
s.close()
2.5 使用UDP
Udp通信几乎不使用文件类对象,因为它们往往不能为数据如何发送和接收提供足够的控制。
udp.py
#!/usr/bin/python
import socket,sys
host = sys.argv[1]
port = int(sys.argv[2])
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.connect((host,port))
while 1:
print "\nEnter data to transmit:"
data = sys.stdin.readline().strip()
s.sendall(data)
buf = s.recv(2048)
if not len(buf):
break
sys.stdout.write(buf)
该代码连接服务器(第三章 udp服务器例子),输入要发送的一行文字,数据发送后,进入一个无限循环来等待回复。
该程序发送一个UDP信息包,接收一个UDP信息包,并继续等待其他的信息包,最后,它被Ctrl-C 终止。
UDP客户端与TCP客户端区别:
udptime.py
import socket,sys,struct,time
host = '210.72.145.44'
port = 37
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.sendto('',(host,port))
buf = s.recvfrom(2048)[0]
if len(buf) !=4:
print "Wrong-sized reply %d:%s" % (len(buf),buf)
sys.exit(1)
secs = struct.unpack("!I",buf)[0]
secs -= 2208988800
print time.ctime(int(secs))
2.6 总结
网络通信的基本接口是socket,它扩展了操作系统的基本I/O到网络通信,socket可以通过socket函数来建立,通过connect()函数来连接,得到了socket,你可以确定本地和远程端点Ipf 地址和端口号,socket对不同的协议来说过生日 一种通用的接口,它可以处理TCP和UDP通信。
当进行网络通信时,错误检查很重要,使用shutdown()可以确保当有写错误发生时,能得到提醒。
Python提供了两种和socket工作的接口:用于UDP和高级TCP目的标准 socket接口,以及用于简单TCP通信的文件类接口。