您好,欢迎访问三七文档
FoundationsofPythonNetworkProgramming-读书笔记系列(1)-Low-LevelNetworking以前,人们热衷于如何将两台机器互相连接,许多连接的方法在今天已经过时,还有很多方法沿用至今。TCP/IP就是之一,可以说,TCP/IP协议是当今使用范围最广的协议,这本书所有的内容都是基于TCP/IP的。TCP/IP的数据传输层是TCP和UDP,我们通过TCP和UDP连接远程机器时,只需要远程机器的IP和端口号,然后建立连接传输数据。其中TCP和UDP又有着许多不同之处。何时使用TCP?1.你需要确保传输的数据准确的到达并且保持完整。2.你需要发送大量的数据,而不是简单的请求和返回。3.你能忍受建立连接时消耗的时间。(效率低)何时使用UDP?1.你不关心你发送的包是否准确的到达,或者你能自己处理这些问题。(不稳定)2.你只是希望得到一个简单的请求和返回。3.你需要快速的建立连接。(效率高)4.你发送的数据量不是很大。UDP限制每个包不能超过64KB,通常人们使用UDP时只使用了低于1KB。在Python中建立一个TCP或UDP连接是一件非常简单的事情,需要使用Socket模块,这是Python的标准模块。客户端(NetworkClients)1.创建一个socket对象s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)第一个参数socket.AF_INET说明我们使用的是IPv4,第二个参数socket.SOCK_STREAM指的是我们使用TCP进行数据传输,如果要使用UDP,则使用socket.SOCK_DGRAM,如:s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)2.connect连接远程服务器s.connect(())连接远程服务器需要远程服务器的IP和端口,注意到上面我们使用了服务器的域名也是可以的,因为Python为我们做了DNS的解析。同时,注意到connect的参数是一个tuple。我们上面连接的是一个http站点,默认端口是80,我们可以通过下面的方法获取到默认的端口号:port=socket.getservbyname('http','tcp')相应的,你可以查询诸如:smtp,ftp等等端口号。3.连接后,从一个socket对象获取信息比如,获取本机的IP地址和端口号,获取远程机器的IP地址和端口号,如:#!/usr/bin/envpython#InformationExample-Chapter2importsocketprintCreatingsocket,s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)printdone.printLookingupportnumber,port=socket.getservbyname('http','tcp')printdone.printConnectingtoremotehostonport%d%port,s.connect(())printdone.printConnectedfrom,s.getsockname()printConnectedto,s.getpeername()输出结果会显示:Creatingsocket...done.Lookingupportnumber...done.Connectingtoremotehostonport80...done.Connectedfrom('192.168.XX.XX',2548)Connectedto('64.233.189.104',80)可以看到,我的本机使用的是一个随机的端口号(2548),每次执行端口号都会不同。4.File-like对象我们可以通过Socket对象来执行一些比如发送(send(),sendto()),接收数据的操作(recv(),recvfrom()),同时,我们还可以把Socket对象转换为一个类似文件的对象(File-likeObject),然后使用其中的write()来发送数据,read(),readline()来接收数据。File-like对象更适合TCP连接,因为TCP连接必须保证数据流能够完整正确的到达,数据流表现的更像是一个文件。而UDP却不是,它是一个基于包的连接,它只管把这些包发送出去,如果使用File-like对象来处理,将很难追踪定位出现的错误。生成一个File-like对象通过下面的语句:fd=s.makefile('rw',0)#s是前面的创建的socket对象,rw表示可读和可写权限然后,就可以调用fd的write(),readines()等方法了。例子如下,同时注意细节的错误处理,这里不详细介绍:#!/usr/bin/envpython#ErrorHandlingExampleWithShutdownandFile-LikeObjects-Chapter2importsocket,sys,timehost=sys.argv[1]textport=sys.argv[2]filename=sys.argv[3]try:s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)exceptsocket.error,e:printStrangeerrorcreatingsocket:%s%esys.exit(1)#Tryparsingitasanumericportnumber.try:port=int(textport)exceptValueError:#Thatdidn'twork.Lookitupinstread.try:port=socket.getservbyname(textport,'tcp')exceptsocket.error,e:printCouldn'tfindyourport:%s%esys.exit(1)try:s.connect((host,port))exceptsocket.gaierror,e:printAddress-relatederrorconnectingtoserver:%s%esys.exit(1)exceptsocket.error,e:printConnectionerror:%s%esys.exit(1)fd=s.makefile('rw',0)printsleepingtime.sleep(10)printContinuing.try:fd.write(GET%sHTTP/1.0\r\n\r\n%filename)exceptsocket.error,e:printErrorsendingdata:%s%esys.exit(1)try:fd.flush()exceptsocket.error,e:printErrorsendingdata(detectedbyflush):%s%esys.exit(1)try:s.shutdown(1)exceptsocket.error,e:printErrorsendingdata(detectedbyshutdown):%s%esys.exit(1)while1:try:buf=fd.read(2048)exceptsocket.error,e:printErrorreceivingdata:%s%esys.exit(1)ifnotlen(buf):breaksys.stdout.write(buf)注意上面在我们发送了数据之后,使用了shutdown方法,是为了保证发送的数据成功到达目标机器。因为shutdown()会等待,直到接收到一个准确的退出代码。服务器端(NetworkServer)通过TCP创建一个服务端可以总结为如下四个步骤:1.创建一个socket对象。(createsocketobject)2.设置socket对象的属性。(setoptions)3.绑定一个端口。(bindtoaport)4.监听来自客户端的连接。(listenforconnection)针对上面的四个步骤,下面是一个最简单的实现:host=''#接受来自任何端口的连接port=51423#第一步,创建一个socket对象s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#第二步,设置socket属性s.setsockopt(socket.SOL_SOCKET,socket.SO_RESUSEADDR,1)#第三步,绑定一个端口s.bind((host,port))#第四步,监听来自客户端的连接s.listen(5)#参数5表示同时监听5个连接通过UDP创建一个服务端步骤也差不多,创建一个socket,设置option,bind端口,然而,UDP不需要listen()和accept(),而是使用recvfrom()就足够了。recvfrom()函数返回两个信息:接受的数据(data)和客户端的地址(address)和端口(port)。下面是UDP服务端的例子:#!/usr/bin/envpython#UDPEchoServer-Chapter3-udpechoserver.pyimportsocket,tracebackhost=''#Bindtoallinterfacesport=51423s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)s.bind((host,port))while1:try:message,address=s.recvfrom(8192)printGotdatafrom,address#Echoitbacks.sendto(message,address)except(KeyboardInterrupt,SystemExit):raiseexcept:traceback.print_exc()DomainNameSystem(DNS)我们能很轻松的记住博客园的域名,却基本上很难说出它的IP地址来,因为DNS为我们解析了域名。socket.getaddrinfo()根据主机名或域名等来获取相应的信息。socket.getaddrinfo(host,port[,family[,socktype[,proto[,flags]]]]))返回值是一个tuple的列表,每个tuple返回如下信息:(family,socktype,proto,canonname,sockaddr)同时,gethostbyaddr()根据IP地址获取相应的信息,同时使用getaddrinfo()和gethostbyaddr()可以实现对域名的双重验证。如下面的例子:importsys,socketdefgetipaddrs(hostname):GetalistofIPaddressesfromagivenhostname.Thisisastandard(forward)lookup.result=socket.getaddrinfo(hostname,None,0,socket.SOCK_STREAM)return[x[4][0]forxinresult]defgethostname(ipaddr):GetthehostnamefromagivenIPaddress.Thisisareverselookup.returnsocket.gethostbyaddr(ipaddr)[0]try:#First,dothereverselookupandgetthehostname.hostname=gethostname(sys.argv[1])#couldraisesocket
本文标题:《Foundations of Python Network Programming》读书笔记
链接地址:https://www.777doc.com/doc-4252042 .html