python网络编程
- 格式:pdf
- 大小:196.24 KB
- 文档页数:9
python网络编程:Python 网络编程介绍说明疯狂代码 / ĵ:http://Python/Article69991.html、网络知识些介绍是网络连接端点例如当你Web浏览器请求上主页时你Web浏览器创建个并命令它去连接Web服务器主机Web服务器也对来自请求在个上进行监听两端使用各自来发送和 接收信息在使用时候每个都被绑定到个特定IP地址和端口IP地址是个由4个成序列这4个数均是范围 0~255中值(例如220,176,36,76);端口数值取值范围是0~65535端口数小于1024都是为众所周知网络服务所保留 (例如Web服务使用80端口);最大保留数被存储在模块IPPORT_RESERVED变量中你也可以为你使用另外端口数 值不是所有IP地址都对世界其它地方可见实际上些是专门为那些非公共地址所保留(比如形如192.168.y.z或10.x.y.z)地址127.0.0.1是本机地址;它始终指向当前计算机可以使用这个地址来连接运行在同计算机上其它 IP地址不好记你可以花点钱为特定IP地址注册个主机名或域名(比如使用代替222.76.216.16)域名服务器(DNS)处理名字到IP地址映射每个计算机都可以有个主机名即使它没有在官方注册多少信息通过个网络被传送基于许多原因其中的就是使用协议许多协议是基于简单、低级协议以形成个协议栈例如HTTP协议它是用在Web浏览器和Web服务器的间通信协议它是基于TCP协议而TCP协议又基于IP协议当 在你自己两个间传送信息时候你通常选择TCP或UDP协议TCP协议在两端间建立个持续连接并且你所发送信息有保证按顺序到达它们 目地UDP不建立连接它速度快但不可靠你发送信息也可能到不了另端;或它们没有按顺序到达有时候个信息多个复制到达接收端即使你 只发送了次2、使用地址和主机名模块提供了几个用于使用主机名和地址来工作gethostname返回运行所在计算机主机名:>>> import>>> .gethostname'lenovo'gethostbyname(name) 尝试将给定主机名解释为个IP地址首先将检查当前计算机是否能够解释如果不能个解释请求将发送给个远程DNS服务器(远程DNS服务器 还可能将解释请求转发给另个DNS服务器直到该请求可以被处理)gethostbyname返回这个IP地址或在查找失败后引发个异常>>> .gethostbyname('lenovo')'192.168.1.4'>>> .gethostbyname('')'222.76.216.16'个扩展形式是gethostbyname_ex(name)它返回个包含 3个元素元组分别是给定地址主要主机名、同IP地址可选主机名个列表、有关同主机同接口其它IP地址个列表(列表可能都是空)>>> .gethostbyname('')'60.191.81.49'>>> .gethostbyname_ex('')('', [''], ['60.191.81.48', '60.191.81.49, '60.191.81.50', '60.191.81.51', '60.191.81.52', '60.191.81.53', '60.191.81.54, '220.181.28.50', '220.181.28.51', '220.181.28.52', '220.181.28.53', '220.181.8.54', '220.181.31.182', '220.181.31.183', '220.181.31.184'])gethostbyaddr(address)作用和gethostbyname_ex相同只是你提供给它参数是个IP地址串:>>> .gethostbyaddr('202.165.102.205')('', [''], ['202.165.102.205'])getservbyname(service,protocol)要求个服务名(如'telnet'或'ftp')和个协议(如'tcp'或'udp')返回服务所使用端口号:>>>.getservbyname('http','tcp')80>>>.getservbyname('telnet','tcp)23通常非Python以32位字节包形式存储和使用IP地址inet_aton(ip_addr)和inet_ntoa(packed)在这个形式和IP地址间作转换:>>> .inet_aton('222.76.216.16')'\xdeL\xd8\x10'>>> .inet_ntoa('\xdeL\xd8\x10')'222.76.216.16'也定义了些变量来代表保留IP地址INADDR_ANY和INADDR_BROADCAST是被保留IP地址分别代表任意IP地址和广播地 址;INADDR_LOOPBACK代表loopback设备总是地址127.0.0.1这些变量是32位字节数字形式 getfqdn([name])返回有关给定主机名全域名(如果省略则返回本机全域名)3、使用低级通信尽管Python提供了些封装使得使用更容易但是你也可以直接使用来工作1、创建和销毁模块中(family,type[,proto])创建个新对象family取值通常是AF_INETtype 取值通常是SOCK_STREAM(用于定向连接可靠TCP连接)或SOCK_DGRAM(用于UDP):>>> from import *>>> s=(AF_INET,SOCK_STREAM)family和type参数暗指了个协议但是你可以使用第 3个可选参数(proto取值如IPPROTO_TCP或IPPROTO_RAW)来指定所使用协议代替使用IPPROTO_XX变量你可以使用getprotobyname:>>> getprotobyname('tcp')6>>> IPPROTO_TCP6fromfd(fd,type[,proto]) 是个很少被使用它用来从打开个文件描述符创建个对象(文件描述符由文件fileno思路方法返回)文件描述符和个真实 连接而非个文件对象fileno思路方法返回有关这个文件描述符当你使用完工 对象时你应close思路方法显式关闭以尽快释放资源(尽管被垃圾回收器回收时将自动被关闭)另外你也 可以使用shutdown(how)思路方法来关闭连接边或两边参数0阻止接收数据1阻止发送2阻止接收和发送 2、连接当 两个连接时(例如使用TCP)端监听和接收进来连接而另端发起连接临听端创建个bind(address) 去绑定个特定地址和端口listen(backlog)来临听进来连接最后accept来接收这个新进来连接,下面是在 服务器端代码: >>> s=(AF_INET,SOCK_STREAM)>>> s.bind(('127.0.0.1',44444))>>> s.listen(1)>>> q,v=s.accept #返回 q和地址v注意:上面代码将直处于等待直到连接被建立下面我们再打开另个Python解释器用作客户端;然后键入如下代码:>>> from import *>>> s=(AF_INET,SOCK_STREAM)>>> s.connect(('127.0.0.1',44444) #发起连接好了我们验证下连接是否建立了我们在服务器端键入以下代码来发送条信息:>>> q.send('hello,i come from ') 注:有时可能出现send argument 1 must be orbuffer,not str 原因可能是您机器不支持UTF-8集临时解决方案是q.send(b' hello...')31 #发送字节数在客户端键入以下代码来接收信息:>>> s.recv(1024)'hello,i come from '你 传递给bind和connect地址是个有关AF_INET元组(ipAddress,port)代替connect你也可以调 用connect_ex(address)思路方法如果背后对Cconnect返回个那么connect_ex也将返回个(否则返回 0代表成功)代替引发个异常当你listen时你给了它个参数这个数值表示在等待队列中允许放置进来连接总数当等待队列已满时如果有更多连接到达那么远程端将被告知连接被拒绝在模块中SOMAXCONN变量表明了等待队列所能容纳最大量accept思路方法返回形如bind和connect个地址代表远程地址下面显示变量v值:>>> v('127.0.0.1', 1334)UDP是不定向连接但是你仍然可以使用给定目地址和端口来connect去关联个3、发送和接收数据函 数send([,flags])发送给定串到远程sendto([,flags],address)发送给 定串到个特定地址通常send思路方法用于可靠连接sendto思路方法用于不可靠连接但是如果你在个 UDP 上connect来使它和个特定目标建立联系那么这时你也可以使用send思路方法来代替sendtosend和sendto都返回实际发送字节数当你快速发送大量数据时候你可能想去确保全部信息已被发送那么你可以使用如下个:def safeSend(sock,msg):sent=0while msg:i=sock.send(msg)i-1: #发生了-1sentimsg=msg[i:]time.sleep(25)sentrecv(bufsize[,flags]) 思路方法接收个进来消息如果有大量数据在等待它只返回前面bufsize字节数数据recvfrom(bufsize[,flags])做同 样事除了它使用AF_INET 返回值是(data,(ipAddress,port)),这便于你知道消息来自哪儿(这对于非连接 是有用)send,sendto,recv和recvfrom思路方法都有个可选参数flags默认值为0你可以通过对.MSG_*变量进行组合(按位或)来建立flags值这些值因平台而有所区别但是最通用值如下所示:MSG_OOB:处理带外数据(既TCP紧急数据)MSG_DONTROUTE:不使用路由表;直接发送到接口MSG_PEEK:返回等待数据且不把它们从队列中删除例如如果你有个打开它有个消息等待被接收你可以接收这个消息后并不把它从进来数据队列中删除:>>> q.recv(1024,MSG_PEEK)'hello'>>> q.recv(1024,MSG_PEEK) #没有删除所以你可以再得到它'hello'makefile([mode[,bufsize]]) 思路方法返回个文件类对象其中封装了以便于你以后将它传递给要求参数为个文件代码(或许你喜欢使用文件思路方法来代替send和 recv)这个可选mode和bufsize参数取值和内建open样 4、使用选项对象getpeername和 getsockname思路方法都返回包含个IP地址和端口 2元组(这个 2元组形式就像你传递给connect和bind) getpeername返回所连接远程地址和端口getsockname返回有关本地相同信息在默认 情况下是阻塞式意思就是思路方法在任务完成的前是不会返回例如如果存储向外发送数据缓存Cache已满你又企图发送 更多数据那么你对send将被阻塞直到它能够将更多数据放入缓存Cache你可以通过blocking(flag)思路方法(其中flag取值 是0blocking(0))来改变这个默认行为以使为非阻塞式当为非阻塞式时候如果所做动作将导致阻塞将 会引起error异常下面段代码将试图不断地接受新连接并使用processRequest来处理如果个新连接无效它将间隔半秒再试另 思路方法是在你监听上select或poll来检测个新连接到达别选项可以使用 sockopt(level,name,value)和getsockopt(level,name[,buflen])思路方法来设置和获取 代表了个协议栈区别层level参数指定了选项应用于哪层level取值以SOL_开头(SOL_SOCKET,SOL_TCP 等等)name表明你涉及是哪个选项对于value如果该选项要求数值值value只能传入数字值你也可以传递入个缓存Cache(个串) 但你必须使用正确格式对getsockopt不指定buflen参数意味你要求个数字值并返回这个值如果你提供了 buflengetsockopt返回代表个缓存Cache串它最大长度是buflen字节数下面例子设置了个用于发送缓存Cache 尺寸为64KB:>>> s=(AF_INET,SOCK_STREAM)>>> s.sockopt(SOL_SOCKET,SO_SNDBUF,65535)要得到个包在被路由丢弃前所能有生命周期(TTL)和跳数你可以使用如下代码:>>> s.getsockopt(SOL_IP,IP_TTL)325、数值转换由于区别平台字节顺序不样所以当在网络中传输数据时我们使用标准网络字节顺序nthol(x)和ntohs(x)要求个网络字节顺序数值并把它转换为当前主机字节顺序相同数值而htonl(x)和htons(x)则相反:>>> import.>>> .htons(20000) #转换为个16位值8270>>> .htonl(20000) #转换为个32位值541982720>>> .ntohl(541982720)20000使用SocketServersSocketServers模块为组服务类定义了个基类这组类压缩和隐藏了监听、接受和处理进入连接细节1、SocketServers家族TCPServer和UDPServer都是SocketServer子类它们分别处理TCP和UDP信息注意:SocketServer也提供UnixStreamServer(TCPServer子类)和UNIXdatagramServer(UDPServer子类)它们都如同其父类样除了在创建监听时使用AF_UNIX代替了AF_INET默 认情况下服务次处理个连接但是你可以使用ThreadingMixIN和ForkingMixIn类来创建任 SocketServer线程和子进程实际上SocketServer模块提供了些对些有用类来解决你麻烦它们 是:ForkingUDPServer、ForkingTCPServer、ThreadingUDPServer、 ThreadingTCPServer、ThreadingUnixStreamServer和ThreadingUnixDatagramServerSocketServer以通常思路方法处理进入连接;要使它更有用你应该 提供你自己请求处理器类给它以便它传递个去处理SocketServer模块中BaseRequestHandler类是所有请求处 理器父类假设例如你需要写个多线程电子邮件服务器首先你要创建个MailRequestHandler它是 BaseRequestHandler子类然后把它传递给个新创建SocketServer:import SocketServer...#创建你MailRequestHandleraddr=('220.172.20.6',25) #监听地址和端口server=SocketServer.ThreadingTCPServer(addr,MailRequestHandler)server.serve_forever每 次个新连接到来时这个server创建个新MailRequestHandler例子并它handle思路方法来处理这个新请求server继承自ThreadingTCPServer对于每个新请求它都启动个单独线程来处理这个请求以便于多个请求能够被同时处理如果 用handle_request代替server_forever它将个个处理连接请求server_forever 只是反复handle_request而已般来说你只需使用服务的但是如果你需要创建你自己子类话你可以覆盖我们下面提到思路方法来定制它当 服务被第次创建时候__init__server_bind思路方法来绑定监听(self.)到正确地址 (self.server_address)然后server_activate来激活这个服务(默认情况下listen 思路方法)这个服务不做任何事情直到了handle_request或serve_forever思路方法 handle_requestget_request去等待和接收个新连接然后 very_request(request,client_address)去看服务是否会处理这个连接(你可以在访问控制中使用这个默认情况下 very_request总是返回true)如果会处理这个请求handle_request然后process_request(request,client_address)如果 process_request(request,client_address)导致个异常话将handle_error(request,client_address)默认情况下process_request简单地finish_request(request,client_address);子进程和线程类覆盖了这个行为去开始新进程或线程然后finish_requestfinish_request例子化个新请求处理器请求处理器轮流它们handle思路方法当SocketServer创建个新请求处理器时它传递给这个处理器__init__self变量以便于这个处理器能够访问有关这个服务信息SocketServer fileno思路方法返回监听文件描述符address_family成员变量指定了监听族(如AF_INET)server_address包含了监听被绑定到地址变量包含监听自身2、请求处理器请 求处理器有up、handle和finish思路方法你可以覆盖它们来定制你自己行为般情况下你只需要覆盖handle思路方法 BaseRequestHandler__init__up思路方法来做化工作handle服务于请求finish用 于执行清理工作如果handle或up导致个异常finish不会被记住你请求处理器会为每个请求创建个新例子request 成员变量有有关流(TCP)服务最近接受;对于数据报服务它是个包含进入消息和监听元组client_address包含发送者地址server有对SocketServer个引用(通过这你可以访问它成员如 server_address) 下面例子实现了个EchoRequestHandler这作为个服务端它将客户端所发送数据再发送回客户端:>>> import SocketServer>>> EchoRequestHandler(SocketServer.BaseRequestHandler):... def handle(self):... pr 'Got connection!'... while 1:... mesg=self.request.recv(1024)... not msg:...... pr 'Received:',msg... self.request.send(msg)... pr 'Done with connection'>>> server=SocketServer.ThreadingTCPServer(('127.0.0.1',12321),EchoReuestHandler)>>> server.handle_request #执行后将等待连接Got connection!Received: Hello!Received: I like Tuesdays!Done with connection打开另个Python解释器作为客户端然后执行如下代码:>>> from import *>>> s=(AF_INET,SOCK_STREAM)>>> s.connect(('120.0.0.1',12321))>>> s.send('Hello!')6>>> pr s.recv(1024)Hello!>>> s.send('I like Tuesdays!')16>>> pr s.recv(1024)I like Tuesdays!>>> s.closeSocketServer 模块也定义了BaseRequestHandler两个子类:StreamRequestHandler和DatagramRequestHandler它们覆盖了up和finish思路方法并创建了两个文件对象rfile和wfile你可以用这两个文 件对象来向客户端读写数据从而代替使用思路方法阻塞或同步编程、使用网 络编程中最基本部分就是(套接字)有两种:服务端和客户端 在你创建了个服务端的 后你告诉它去等待连接然后它将监听某个网络地址(形如:xxx.xxx.xxx.xxx:xxx) 直到客户端连接然后这两端就可以通信了处理客户端通常比处理服务端要容易点服务端必须时刻准备处理来自客户端连接并且它必须处理多个连接而客户端只需要简单连接然后做点什么然后断开连接实 例化个时可以指定 3个参数:地址系列(默认为.AF_INET)、流(这是个默认 值: .SOCK_STREAM)或数据报(.SOCK_DGRAM)、协议(默认值是0)对于简单 你可以不指定任何参数而全部使用默认值服务端在使用bind思路方法的后listen思路方法去监听个给定 地址然后客户端就可以通过使用connect思路方法(connect思路方法所使用地址参数和bind相同)去连接服务端listen思路方法 要求个参数这个参数就是等待连接队列中所能包含连接数旦服务端了listen思路方法就进入了临听状态然后通 常使用个无限循环:1、开始接受客房端连接这通过accept思路方法来实现了这个思路方法后将处于阻塞状态(等待客户端发起连接)直到个客 户端连接连接后accept返回形如(client,address)个元组其中client是个用于和客户端通信 address是客户端形如xxx.xxx.xxx.xxx:xxx地址;2、然后服务端处理客户端请求;3、处理完成的后又 1有关传输数据有两个思路方法:send和recvsend使用串参数发送数据;recv参数是字节数表示次接受数据量如果你不确定次该接受数据量话最好使用1024下面给出个最小服务器/客户机例子:服务端:imports = .host = .gethostnameport = 1234s.bind((host, port))s.listen(5)while True:c, addr = s.acceptpr 'Got connection from', addrc.send('Thank you for connecting')c.close客户端:imports = .host = .gethostnameport = 1234s.connect((host, port))pr s.recv(1024)注意:如果你使用Ctrl-C来停止服务端话如果再次使用相同端口可能需要等待会儿2、使用SocketServerSocketServer模块简单化了编写网络服务器工作它提供了 4个基本服务类:TCPServer(使用TCP协议)、UDPServer(使用数据报)、UnixStreamServer、 UnixDatagramServerUnixStreamServer和UnixDatagramServer用于类Unix平台这 4个类处理请求都使用同步思路方法也就是说在下个请求处理开始的前当前请求处理必须已完成用SocketServer创建个服务器需要 4步:1、通过子类化BaseRequestHandler类和覆盖它handle思路方法来创建个请求处理器类用于处理进来请求;2、例子化服务类如TCPServer并传递给它参数:服务器地址和请求处理器类;3、服务例子对象handle_request或serve_forever思路方法去处理请求下面使用SocketServer用同步思路方法写个最简单服务器:from SocketServer import TCPServer, StreamRequestHandler#第步其中StreamRequestHandler类是BaseRequestHandler类子类它为流定义了 #rfile和wfile思路方法Handler(StreamRequestHandler):def handle(self):addr = self.request.getpeernamepr 'Got connection from', addrself.wfile.write('Thank you for connecting')#第 2步其中''代表运行服务器主机server = TCPServer(('', 1234), Handler)#第 3步serve_forever导致进入循环状态server.serve_forever注意:使用阻塞或同步思路方法次只能连接个客户端处理完成后才能连接下个客户端 非阻塞或异步编程2009-9-7 0:38:13疯狂代码 /。