您的位置:首页 > 理论基础 > 计算机网络

Python程序设计8——网络编程

2013-08-09 21:36 148 查看
  Python是一个很强大的网络编程工具,python内有很多针对场景网络协议的库,在库顶部可以获得抽象层,这样就可以集中精力在程序的逻辑处理上,而不是停留在网络实现的细节中。

1 少数几个网络设计模块

  在标准库中有很多网络设计模块,在其他地方还有更多,下面是常用的模块。

1.1 socket 模块

  在网络编程中一个基本组件是套接字(socket,又是哪个人才翻译的),套接字是两个程序之间的信息通道,python因此了socket模块的基本细节,并不直接和套接字交互。套接字包括两个:服务器套接字和客户机套接字,创建一个服务器套接字,让它等待连接,这样它就在某个网络地址处(IP+端口号)监听。处理客户端套接字通常比处理服务器端套接字容易,因为服务器端随时处理客户端的连接,还有处理多个连接,而客户机只是简单地连接,完成事务,断开连接。
  socket模块中的socket方法用于创建套接字,socket方法语法格式如下:
socket(socket_family, socket_type, protocol = 0)
其中:
socket_family:该参数的值可以为AF_UNIX或AF_INET
socket_type:该参数的值可以为SOCK_STREAM或SOCK_DGRAM
protocol:该参数一般不赋值,默认值为0
套接字相当于应用程序访问下层网络服务的接口,使用套接字,使得用户可以在不同的主机之间进行通信,从而实现数据交换。

View Code
  上面的listen方法有一个参数,用来设置连接队列的长度

2.3.1 使用SocketServer模块

  如前所述,用Socket生成一个服务进程相对比较复杂,为了简化,python专门提供了SocketServer模块,此模块有5个服务类。如下所示:

  一般情况下,BaseServer类不会被实际使用,因此SocketServer模块中只包含了4个基本的类:针对TCP套接字流的TCPServer,针对UDP数据报套接字的UDPServer,以及UNIXStreamServer和UNIXDatagramServer,其中最重要的类是TCPServer,此类中有简单TCP协议实现的服务器接口,为应用程序提供了可靠的流数据传输,除此之外,还有更多模块中的类派生于TCPServer类,包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer等等,通过对SocketServer模块中类的继承,可以实现更多功能的服务端应用程序,接着是UDPServer类,此类中有数据报服务的接口。此类同样提供数据传输服务,但是并不保证数据的可靠性和有序性。另外两个使用较少的是UNIXStreamServer和UNIXDatagramServer类,这两个类都是使用UNIX域套接字地址,他们几乎不能使用在非UNIX平台上。
  为了写一个使用SocketServer框架的服务器,大部分代码会包含在一个请求处理程序中,每当服务器收到一个请求时,就会实例化一个请求处理程序,并且它的各种处理方法会在 处理请求时被调用。基本的BaseRequestHander类把所有的操作都放到了handle方法中,这个方法会被服务器调用,然后访问属性self.request中的客户端套接字。如果使用的是流,那么可以使用StreamRequestHander类来创建两个新属性self.rfile和self.wfile,然后使用这些类文件对象和客户机通信。
  SocketServer框架中的其他类实现了对HTTP服务器的基本支出,其中包括允许CGI脚本,也包括对XML RPC的支持。
  下面用SocketServer模块制作一个小型服务器:

View Code
客户端的构建

import socket;
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
host = socket.gethostname();
port = 1234;
s.connect((host, port));
print s.recv(1024);
s.close;


  对于一个客户端而言,首先用Socket模块导入,然后调用其socket方法生成一个Socket对象,并使用gethostname方法获取服务器地址,之后再通过connect方法连接服务程序,当客户端连接服务器后,一直等待的服务进程被唤醒,并处理此连接,这里的客户端处理直接调用recv方法获取服务端发送过来的数据,最后调用close方法将连接关闭。运行:
Link come from ('192.168.1.103', 4642)

2.3.2 使用urlparse包也可以创建客户端

View Code
  Socket套接字不仅可以连接服务器,还可以处理客户端请求,从而达到服务端与客户端的通信。下面用Socket对象分别创建一个服务端和客户端。
服务端socket流程是:
  首先生成Socket对象,然后使用bind方法绑定主机和端口号,并使用listen方法监听,之后进行while循环,使服务器一直处于监听状态,使用accept方法可以接受客户端的一个连接,接着使用send方法来向客户端发送一个字符串的数据,最后使用close连接,释放资源。
  总的来说:先创建socket连接并监听,然后阻塞等待,如果有消息过来,就发送消息,然后关闭连接,总共4步
服务端:

View Code
客户端:
客户端的流程是:使用Socket模块中的socket方法生成socket对象,然后使用connect连接服务器,并输出服务端发送过来的消息,最后关闭Socket对象。

import socket;
import time;
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
sock.connect(("localhost", 8001));
time.sleep(2);
sock.send("1");
print sock.recv(1024);
sock.close();


2.4 异步通信方式

  在上面的示例中,所有的服务器端实现都是同步的,也就是说,服务程序只有处理完一个连接后,才能处理另外一个连接。如果要使服务器端应用程序能够同时处理多个连接,则需要使用异步通信方式。python标准库提供了三种处理方式:分叉方式、线程方式和异步I/O方式。

2.4.1 使用SocketServer进行分叉处理

  当有多个连接同时到达服务器端时,可以通过分叉方式进行处理,对于接收到的每个连接,主进程都会生成相应的一个子进程专门用来处理此连接,而主进程则依旧保持在监听状态,从而使每个连接都由一个对应的子进程来处理。由于生成的子进程和主进程是同时运行的,所以不会阻塞新的连接。这种方式好处是比较简单、有效,但是由于生成进程消耗的资源比较大,所以当连接很多时,会带来性能问题。
  分叉是相当于复制了主进程,但是只是子进程,相当于在时间线上创建了分支,最后得到两个独立存在的进程,进程可以判断哪个是主进程,哪个是子进程(使用fork,就行叉子一样)
在一个使用分叉的服务器中,每一个客户端连接都利用分叉创造一个子进程,主进程继续监听新的连接,同时子进程处理客户端,当客户端请求结束后,子进程就退出了,因为分叉的进程是并行运行的,客户端之间不必相互等待。

View Code
  在该例子中定义了Server类,该类继承自ForkingMixIn和TCPServer类,使此类具有两个类的特点,也就是提供流数据传输服务和多连接处理。

2.4.2 使用线程方式

  由于线程是一种轻量级的进程,具有进程所没有的优势,当存在大量连接而消耗资源太多的情况下,则可以使用线程方式进行处理。线程实现的方式和分叉的处理方式类似。当有连接到来的时候,主线程将生成一个子线程来处理连接,而在子线程处理连接的时候,主线程依然处于监听状态,并不会阻塞连接。
  由于生成的子线程和主线程都存在于相同的进程中,共享内存,因此这种处理方式的效率非常高,从而使得大量地使用线程会造成线程之间的数据同步,如果处理不好,则可能使得服务程序时区效应,这就必须确保主线程和子线程的变量不冲突,在现代操作系统中一般都使用分叉方式来处理多连接,但是windows不支持。

  下面是利用分叉创建线程的方式:

View Code
  这段代码与使用分叉处理的示例相同,唯一区别在于在生成的Server类时采用了ThreadingMixIn类,这样生成的Server类在处理多连接时采用线程方式处理

2.4.3 异步IO方式

  当服务器与客户端通信时,来自客户端的数据持续的时间较长且数据突发的多连接情况下,如果使用分叉或线程处理,占用资源太多,一种改进的方式就是采用专门的异步IO通信方式,即在一定的时间段查看已有的连接并处理。处理的过程包括读取数据和发送数据。
  在python标准库中,由asyncore和asynchat模块来实现异步IO处理,这种功能依赖于select()和poll方法,这两个方法定义在select模块中。
注意:select和poll方法,poll方法的伸缩性更好,但它只能在UNIX系统中使用,在windows系统中不可用。
1.select模块
select方法用于指定的文件描述符进行监视,并在文件描述符集改变的时候做出响应。select模块中的select方法的语法格式如下:
select.select(List rlist, List wlist, List xlist[, long timeout])
select方法有4个参数:
1.rlist、wlist和xlist,这是3个必须参数,分别表示等待输入、输出和错误的文件描述符,这3个参数都为文件描述符列表。空列表也是允许的,但是如果3个参数都是空列表,则表示平台相关,在linux系统平台下允许,在windows平台下不允许。
2.timeout是可选的,参数为一个浮点数,用例指定系统监视文件描述符集改变的超时时间,单位为妙。
select方法返回值是一个包含3个值的元组,元组中的3个值即为在select方法中前3个参数已经准备好的文件描述符。下面介绍select方法

2.使用asyncore模块
  在python中,可以使用asyncore模块来实现异步通信,实际上,该模块提供了用例构建异步通信方式的客户端和服务端的基础架构,特别适用于聊天类的服务器和协议的实现。其基本思想是,创建一个或多个网络信道,实际上网络信道是Socket对象的一个封装,当信道创建后,调用loop方法来激活网络信道服务,直到最后一个网络信道关闭。
  在asyncore模块中,主要用于网络事件循环检测的loop方法是核心,在loop方法中将会通过select方法来检测特定的网络信道。当select方法返回所有事件的socket对象后,loop方法检查检查此事件和套接字状态并创建一个高层次的事件信息,然后针对此高层次的事件信息调用相应的方法,asyncore还提供了底层的API用来创建服务器。
  同时,该模块中还有一个dispatcher类,这是一个Socket对象的轻量级封装,用于处理网络交互事件,其中的方法是在异步loop方法中调用或者直接当作一个普通的非阻塞Socket对象。框架是:

View Code
  在该段代码中,导入了asyncore模块和socket模块,接着定义了一个名称为http_client的类,该类继承自asyncore.dispatcher类,因此可以在http_client类中重载dispatcher类中的处理方法。
  在构造函数中,首先调用了dispatcher类的构造函数,然后用create_socket方法创建socket对象,该方法封装了socket模块的socket方法。在调用socket方法之后,还用了setblocking方法设置其阻塞方式为非阻塞,并获取了套接字的文件描述符最后通过add_channel方法将文件描述符加入。在构造函数中使用connect方法连接特定服务器的80端口,这也是http默认端口,并设置类变量buffer为一个HTTP获取命令,获取内容。
接下来定义了5个事件处理方法,分别在不同的时间发生时候被调用。
1.handle_connect方法:将在HTTP连接时候被调用
2.handle_close方法:关闭连接
3.handle_read方法:调用recv方法来获取http数据,recv方法中的参数为一次读取最大字节数,需要注意的是,缓冲区大小最好为2的幂,如1024或者4086等数据
4.handle_write方法:用来处理发送时的数据,这里首先调用send方法发送数据,其返回值为依据发送成功的数据,然后设置buffer为未发送的数据,这样做的原因是在异步通信过程中,不一定能保证每次发送都能发送成功
5.writable方法:用来判断在什么时候发送数据,在方法体重,只是判断需要发送数据的缓冲区是否为空,如果不为空,则返回True,表示需要发送数据,而当缓冲区为空时,则不需要继续发送数据。
示例:使用asyncore和socket模块将所请求的服务器端信息打印出来
步骤:
1.导入asyncore和socket模块
2.新建一个类,该类继承asyncore.dispatcher类,并重载asyncore.dispatcher类中的__init__、handle_connect、handle_read、writable、handle_write和handle_close方法。在__init__方法中获取与服务端的脸颊,并将指定的页面请求赋值与request对象;在handle_connect方法中输出连接的服务器信息;在handle_write方法中奖请求对象request发送到服务器端;在handle_read方法中奖获取的服务器发送给客户端的信息写入一个记事本中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: