理解socket
socket 是操作系统中I/O 系统的延伸部分,它可以使进程和机器之间通信成为可能。
把对网络支持加入操作系统,是以一种扩展现有文件描述符结构的方法来实现的。
Python 通过socket 模块提供访问操作系统socket 库的接口,建立socket 时,你只需要调用这个模块的函数和常量。
2.2 建立socket
对于一个客户端来说,建立一个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 对象
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 种可能的异常:
Ø 与一般I/O 和通信问题有关的socket.error;
Ø 与查询地址有关的socket.gaierror;
Ø 与其它地址错误有关的socket.herror;
Ø 与在一个socket 上调用settimeout() 后,处理超时有关的socket.timeout;
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 客户端区别:
Ø 建立socket 时,UDP 采用SOCK_DGRAM ,TCP 用SOCK_STREAM 。
Ø 程序没有办法探测到服务器什么时候发送完数据,connect() 的调用只是初始化一些内在参数。
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 通信的文件类接口。