首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

15.4  使用内置的SSL

在多数系统中都包含内置SSL的Python。然而,编译不带SSL支持的Python也是有可能的。如果您得到“缺少SSL支持”的错误信息,您就需要重新编译您的Python或者取得一个更新的版本。

为了启动一个SSL session,首先您需要像平常那样连接一个socket,接着建立一个SSL对象,该对象可以在socket上通信。这时候,所有通信都会使用这个新的SSL对象。下面是一个简单的例子:

#!/usr/bin/env python

# Basic SSL example - Chapter 15 - basic.py

import socket, sys

def sendall(s, buf):

    byteswritten = 0

    while byteswritten < len(buf):

        byteswritten += s.write(buf[byteswritten:])

print "Creating socket...",

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "done."

print "Connecting to remote host...",

s.connect(("www.openssl.org", 443))

print "done."

print "Establishing SSL...",

ssl = socket.ssl(s)

print "done."

print "Requesting document...",

sendall(ssl, "GET / HTTP/1.0\r\n\r\n")

print "done."

s.shutdown(1)

while 1:

    try:

        buf = ssl.read(1024)

    except socket.sslerror, err:

        if (err[0]) in [socket.SSL_ERROR_ZERO_RETURN, socket.SSL_ERROR_EOF]:

            break

    elif (err[0]) in [socket.SSL_ERROR_WANT_READ,

                         socket.SSL_ERROR_WANT_WRITE]:

        continue

    raise

    if len(buf) == 0:

        break

    sys.stdout.write(buf)

s.close()

运行这个程序(它不需要参数),您会发现它连接到www.openssl.org站点。接着它建立起一个SSL连接,并像普通HTTP那样通信。它会打印出该站点的主页。

您会注意到这个程序里面的sendall()函数。SSL对象只提供两个方法:read()和write()。它们大致和socket的recv()和send()方法相对应。和send()方法一样,write()也不能保证会把所有请求的数据都写出。不幸的是,SSL对象不能提供一个和第一章中介绍的socket的sendall()类似的方法,所以您必须自己实现。这一版本的sendall()方法简单地确保整个字符串都得到传输,就像标准的sendall()方法。

请注意在read()周围处理异常的部分。Python内置的SSL可以在文件尾,或者即使是读数据时产生异常。这段代码可以确保当收到合适的文件尾标记时退出主循环,并忽略那些不是错误的异常。

当前很多网络协议都是面向行的。SSL对象并不提供一个readline()方法,这就在使用面向行的协议时比较麻烦。下面是一个包装SSL的对象,它加入了一些缺少的函数:

#!/usr/bin/env python

# Basic SSL example with wrapper - Chapter 15 - basic-wrap.py

import socket, sys

class sslwrapper:

    def __init__(self, sslsock):

        self.sslsock = sslsock

        self.readbuf = ''

        self.eof = 0

    def write(self, buf):

        byteswritten = 0

        while byteswritten < len(buf):

            byteswritten += self.sslsock.write(buf[byteswritten:])

    def _read(self, n):

        retval = ''

        while not self.eof:

            try:

                retval = self.sslsock.read(n)

            except socket.sslerror, err:

                if (err[0]) in [socket.SSL_ERROR_ZERO_RETURN,

                                   socket.SSL_ERROR_EOF]:

                    self.eof = 1

            elif (err[0]) in [socket.SSL_ERROR_WANT_READ,

                                 socket.SSL_ERROR_WANT_WRITE]:

                   continue

            else:

                   raise

            break

        if len(retval) == 0:

            self.eof = 1

        return retval

    def read(self, n):

        if len(self.readbuf):

            # Return the stuff in readbuf, even if less than n.

            # It might contain the rest of the line, and if we try to

            # read more, it might block waiting for data that is not

            # coming to arrive.

            bytesfrombuf = min(n, len(self.readbuf))

            retval = self.readbuf[:bytesfrombuf]

            self.readbuf = self.readbuf[bytesfrombuf:]

            return retval

        retval = self._read(n)

        if len(retval) > n:

            self.readbuf = retval[n:]

            return retval[:n]

        return retval

    def readline(self, newlinestring = "\n"):

        retval = ''

        while 1:

            linebuf = self.read(1024)

            if not len(linebuf):

                return retval

            nlindex = linebuf.find(newlinestring)

            if nlindex != -1:

                retval += linebuf[:nlindex + len(newlinestring)]

                self.readbuf = linebuf[nlindex + len(newlinestring):] \

                       + self.readbuf

                return retval

            else:

                retval += linebuf

print "Creating socket...",

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "done."

print "Connecting to remote host...",

s.connect(("www.openssl.org", 443))

print "done."

print "Establishing SSL...",

ssl = socket.ssl(s)

print "done."

ssl = sslwrapper(ssl)

print "Requesting document...",

ssl.write("HEAD / HTTP/1.0\r\n\r\n")

print "done."

s.shutdown(1)

while 1:

    line = ssl.readline("\r\n")

    if not len(line):

        break

    print "Received line:", line.strip()

s.close()

尽管这个程序仅仅从服务器读取几行数据,您还是可以把sslwrapper类用在您自己的程序中。它可以在很多程序中替换标准socket对象。还请注意的是,您或许根本永远用不上它,有些Python模块,例如在第6章中讨论的urllib2,已经支持Python内置的SSL了。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论