《Python 编程》笔记(十三)

基础知识

  • 大体上,互联网可以有如下几个层次构成:

    • 底层的网络层:类似 TCP/IP 机制,处理字节间传送,不关心内容;
    • 套接字:连接到网络的编程接口,类似 TCP/IP 运行在物理网络层上,支持灵活的客户端/服务器模型;
    • 更高层的协议:结构化互联网通信架构,如 FTP 等协议,运行在套接字上,并定义了消息格式和标准地址;
    • 服务器端网络脚本:应用模型,如 CGI,定义了网页浏览器和网络服务器之间的通信协议;
    • 高层的框架和工具:如 Django, Google App Engine等,使用套接字和通信协议,但可以处理特定技术或较大的问题领域。
  • Grail 是由 Guido Van Rossum编写的互联网浏览器。

  • 一些流行的工具:

    • 网络工具:socket, select, socket server
    • 客户端协议工具:FTP, Email, HTTP, TELNET等;
    • 服务器端 CGI 脚本:可以满足简单的网站开发任务;
    • 网络框架和云:Django,用于快速开发;Google App Engine,“云计算”框架,提供了企业级别工具应用,允许网站利用谷歌网络基础设施;Turbo Gears,工具综合包,包括一个JavaScript库,一个模板系统,网络互动的CherryPy等;Zope,开源的网络应用服务器和工具包;
    • Web 服务(XML-RPC, SOAP):XML-RPC是是一种通过网络向部件提供远程程序请求的技术,对于客户端而言,网络服务器似乎是简单的函数,当函数调用发出,把传输的数据编码成XML格式,使用 Web 的HTTP传输机制传输到远程服务器。最终效果就是在客户端上简化连接到网络服务器的界面。Python 的xmlrpc.clientxmlrpc.server分别用于客户端和服务器。SOAP机制也是类似。
    • 其他工具:
      • mod_python:一个在 Apache 网络服务器上优化 Python 的服务器端脚本执行的系统;
      • Twisted:一个用 Python 编写的支持异步、事件驱动的网络架构,支持大量的网络协议和通用网络服务器部署的与编码;
      • HTML gen:一个轻量级工具,可以从 Python 对象树直接生成HTML代码来描述网页。
  • 套接字层

    • 机器标识符:机器名称,可以使用域名或者IP; 端口号,用于会话时的协商数字标识符;
    • 机器名和端口号组合可以唯一标志网络上的任何会话。
  • 协议层

    • 标准的因特网协议规定了一个结构化方式来探讨套接字,它们一般规范了消息格式和套接字端口号:消息格式提供了会话期间在套接字上交换的字节结构;端口号是保留的数字标识符,用于底层套接字上的信息交换。
    • 端口号规则:

      • 原则上,套接字端口可以在0~65535之间的任何16位二进制码整数值。但是0到1023之间的端口号被保留下来分配给了高层的标准协议。
      协议常见功能端口号Python 模块
      HTTPWeb 网站80http.client, http.server
      NNTPUsenet 新闻组119nntplib
      FTP 数据(默认)文件传送20ftplib
      FTP 控制文件传送21ftplib
      SMTP发送邮件25smtplib
      POP3获取邮件110poplib
      IMAP4获取邮件143imaplib
      Finger信息79
      SSH命令行22
      Telnet命令行23telnetlib
      • 客户端和服务器:通常称永久运行的监听程序为服务器,连接程序为客户端。
      • 协议结构:有些协议可以在套接字上定义发送信息的内容;其它的协议也许可以在会话期间指定控制信息交换的顺序。
  • Python 网络库模块

    模块功能
    socket, ssl网络和IPC通信支持(TCP/IP,UDP等)
    cgi服务器端CGI脚本支持:解析输入流,转义HTML文本等
    urllib.request从给定地址获取网页
    urllib.parse解析URL字符串不同的部分,可以转义URL
    http.client, ftplib, nntplib网络、文件传送、新闻组
    http.cookies, http.cookiejar根据网站要求将数据存储在客户端,便于利用cookies实现自动登录等
    poplib, imaplib, smtplib邮件相关协议
    telnetlibtelnet协议模块
    html.parser, xml.*解析网页
    xdrlib, socket对二进制数据进行编码,使其具有可移植性,便于传输
    struct, pickle对Python对象进行编码,使其称为打包二进制数据或序列化的字节字符串,便于传输
    email.*通过标题附件和编码解析和撰写电子邮件
    mimetypes推测类型
    uu, binhex, base64, binascii, quopri, email.*编码和解码以文本传输的二进制数据(在电子邮件包中自动进行)
    socketserver针对一般网络服务器的框架
    http.server基本的HTTP服务器部署

    套接字编程

  • socket模块中包含的高级工具:

    • 转换字节为标准的网络序列(ntohl, htonl);
    • 查询及其名和地址(gethostname, gethostbyname);
    • 在文件对象上封装套接字对象(sockobj.makefile);
    • 使用套接字调用处于非阻塞状态(sockobj.setblocking);
    • 设置套接字超时(sockobj.settimeout)。
  • ssl.wrap_socket 调用可以加密传输。

  • 服务器端程序编写的一般步骤:

    • server = socket(AF_INET, SOCK_STREAM):默认创建TCP套接字对象。其中AF_INET指定的是IP地址协议(还有另外一种是支持IPv6的),SOCK_STREAM则意味着TCP传输协议。一般都使用这种方式创建。
    • server.bind(host, port):可以把套接字对象和某一个地址绑定起来。bind需要一个适用于已经创建的套接字类型的元组。
    • server.listen(N):开始监听来自客户端的请求,并且允许保留N个挂起的请求。通常来说,处理器足够快,所以设置为5对于一般应用就可以了。
    • conn, addr = server.accept():调用会返回一个全新的套接字对象,在这个套接字对象上,数据可以在连接的客户端上相互转移;
    • data = conn.recv(1024):用于读取发送来的消息,当客户端关闭套接字的末尾时,会触发EOF标志;
    • conn.send(b'msg'):发送字节字符串到客户端;某些程序可能需要重新发送未发送的部分或者使用conn.sendall强制发送所有字节;
    • conn.close():关闭特定的客户端连接。
  • 可以使用picklestruct模块序列化对象,然后进行传输处理,这样也比较方便。但针对普通的字符串,使用encodedecode方法也可以满足了。

  • 客户端程序编写的一般步骤:

    • client = socket(AF_INET, SOCK_STREAM)
    • client.connect
    • client.send
    • client.recv(1024):获取来自服务器的消息
    • client.close():关闭与服务器的连接,发送EOF信号
  • 完整的示例程序参见 echo/server.pyecho/client.py

处理多个客户端

派生进程处理

  • 主要思路就是使用一个父进程长期运行,并监听来自客户端的请求;每当请求被接收后,都会派生一个新的子进程负责处理请求,并给客户端发送响应。完整的支持派生进程的服务器端代码参见 这里
  • 使用派生进程的方式处理多个客户端请求时,需要注意僵尸子进程的处理;否则,它们将会浪费系统进程表记录,浪费系统资源。僵尸进程截图如下:
  • 需要注意的是,在完成请求后,子进程需要调用os._exit(N)来退出,出现上面图片显示的僵尸进程的现象,也是由于我调用了exit(N)后导致的。看来,需要特别注意这个问题。os._exitsys.exit调用是一样的,但是前者在会立即退出调用进程,没有清理操作等。通常来说,os._exit用于子进程退出,而sys.exit则可以在每个地方使用。关于它们的区别可以看看 Stack overflow上的提问
  • 在 Linux 上使用信号处理程序防止僵尸进程:当子进程停止或退出时,通过操作系统把SIGCHLD信号传递到父进程来重新设置信号处理器,从而有可能清理僵尸进程。具体来说,如果在 Python 脚本中为SIGCHLD信号处理器分配SIG_IGN(忽略)操作,那么当子进程退出时僵尸进程将会通过操作系统而被自动清除掉;这样,父进程也不需要发送等待调用来清理僵尸进程了。具体的改进代码参见 此处,关键代码signal(signal.SIGCHLD, signal.SIG_IGN)
  • 使用能够信号的好处有两点:

    • 简单:无需追踪并捕获子进程;
    • 准确:客户端请求之间也不暂存僵尸进程。
  • 但上述做法最大的缺点是移植性更差,但在 Linux 工作的很好。子进程退出后就会被立即删除,并不会出现大量”将要消失“的僵尸进程。

  • 最后,来看看使用multiprocessing模块来实现同样的功能吧,事实上也非常简单!完整示例代码详见 此处,关键代码如下:
def dispatcher(server):
    assert isinstance(server, socket)
    while True:
        conn, addr = server.accept()
        print('Request from client:', addr, end=' ')
        print('at', datetime.now())
        multiprocessing.Process(target=handle_request, args=(conn, )).start()
  • 但是使用multiprocessing模块在标准的 Windows 环境下却可能出问题,因为socket无法被pickle传递,所以会出现异常。不过,也没什么大不了的,基本不用 Window 系统,可以忽略这个问题。

多线程服务器

  • 多进程服务器存在着性能、可移植性和复杂性的问题,而线程则是新的替代方案;并且线程服务器更加简单。
  • 采用多线程模型的完整示例代码可以参见 这里,以下为关键代码:
def dispatcher(server):
    assert isinstance(server, socket)
    while True:
        conn, addr = server.accept()
        print('Request from client:', addr, end=' ')
        print('at', datetime.now())
        thread.start_new(handle_request, (conn, ))

使用 socketserver

  • socketserver模块为我们处理了线程或者进程分支的细节部分,我们主要实现的是请求处理的部分,方便使用。
  • 完整的示例代码参见 这里,以下给出一般的服务器编写模板:
class RequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            data = self.request.recv(1024)
            if not data:
                break
            self.request.send(data)
        self.request.close()

def main():
    # 此处可以使用ForkingTCPServer等,socketserver会为你处理进程管理的细节。
    server = socketserver.ThreadingTCPServer(('', 2050), RequestHandler)
    server.serve_forever()

包装套接字

  • 核心函数:makefile,调用后,它会为我们返回下面的封装对象;然后就可以像对一个文件那样进行操作了。
>>> s.makefile()
<_io.TextIOWrapper name=7 mode='r' encoding='UTF-8'>
  • 使用流重定向技术,我们可以方便地将 GUI 程序通过socket与非 GUI 程序通信,并且使用起来更加直观清晰。我们可以使用基本的printinput函数完成发送和接收,所以对于客户端来说,套接字连接在很大程度上都是无形的了。
  • 要注意的是,标准流通常是可以缓冲的,所以在测试时,有些是需要sys.stdout.flush显示刷新的,否则发送的数据会不完整甚至可能导致死锁。
  • 有关makefile的一个应用示例可以参见 简单的文件下载示例,涉及到的关键代码:
sockfile = sock.makefile('r')
name = sockfile.readline()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值