《Python网络编程基础》笔记

发布时间:2019-08-26 07:20:45编辑:auto阅读(2010)

                              python网络编程基础
                              ==================

    Author: lujun9972 <lujun9972@X41>
    Date: 2013-03-08 22:29:20 CST


    Table of Contents
    =================
    1 客户端与服务器端
        1.1 使用inetd或xinetd
        1.2 在python中使用syslog
    2 域名系统
        2.1 正向查找
        2.2 反向查找
        2.3 获得运行程序机器的域名信息
        2.4 使用pyDNS
    3 高级网络操作
        3.1 半开发socket
        3.2 超时
        3.3 广播数据
        3.4 使用poll()或select()实现事件通知
        3.5 urllib2
    4 解析HTML和XHTML
        4.1 使用HTMLParser模块解析HTML
        4.2 XML和XML-RPC
            4.2.1 DOM模型
            4.2.2 xmlrpclib库
    5 E-mail服务
        5.1 E-mail的编写和编码
        5.2 SMTP
        5.3 POP协议
        5.4 IMAP协议
    6 FTP
    7 数据库
    8 SSL
    9 SocketServer
    10 SimpleXMLRPCServer


    1 客户端与服务器端
    ~~~~~~~~~~~~~~~~~~~
      1. socket().makefile(操作文件模式,是否开启缓存模式)方法能够使得socket变得像file一样读写
         缓存一般用在磁盘文件中,在socket环境中,一般不开启缓存,将该值设为0
      2. socket.getservbyname(协议名,udp/tcp)   查询服务端口
      3. socket().getsockname()   /socket().getpeername()    #获取地址与端口信息
      4. socket异常:
           异常              说明                                            
          -----------------+------------------------------------------------
           socket.error      与一般IO和通讯问题有关                          
          -----------------+------------------------------------------------
           socket.gaierror   与查询地址信息有关的                            
          -----------------+------------------------------------------------
           socket.herror     与其他地址错误有关                              
          -----------------+------------------------------------------------
           socket.timeout    与在一个socket上调用settimeout后,处理超时有关  
      5. 对于很多操作系统来说,有时候在网络上发送数据的调用会在远程服务器确保已经收到信息之前返回。因此很有可能一个来自对sendall成功调用的数据,事实上并没有被成功收到
         为了解决这个问题,一旦结束写操作,你就应该立刻调用shutdown函数,这样就会强制清除缓存里面的内容内容,同时如果有任何问题就会产生一个异常
         请牢记,数据只有在调用了shutdown函数后才能确保被发送
         需要注意的是,makefile()返回的对象并不提供一个对shutdown()的调用,股必须保持原始的socket对象并使用它
      6. setsockopt(level,optname,value)
         getsockopt(level,optname[,buflen])
         level定义了哪个选项将被使用。通常情况下是SOL_SOCKET
           选项              意义                                                                                                                 期望值                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_BINDTODEVICE   可以使socket只在某个特殊网卡有效                                                                                     一个字符串给出设备的名称或者一个空字符返回默认值  
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_BROADCAST      允许广播地址发送和接受信息,只对UDP有效                                                                              布尔型                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_DONTROUTE      禁止通过路由器和网关往外发送数据包                                                                                   布尔型                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_KEEPALIVE      可以使TCP通信的信息包保持连续性,这些信息包可以在没有信息传输时,使通信的双方确定连接时保持的                        布尔型                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_OOBINLINE      可以把收到的不正常数据看成是正常的数据;也就是说,会通过一个标准的对recv的调用来接受这些数据                         布尔型                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_REUSEADDR      当socket关闭后,本地端用于该socket的端口号立刻就可以被系统重用,通常来说,只有经过系统定义的一段时间后,才能被重用   布尔型                                            
          -----------------+--------------------------------------------------------------------------------------------------------------------+--------------------------------------------------
           SO_TYPE           重新得到socket类型(例如SOCK_STREAM或SOCK_DGRAM).只用于getsockopt()                                                   整数                                              

    1.1 使用inetd或xinetd
    ======================
       inetd和xinetd使用stdin和stdout传统socket给服务进程
      1. xinetd的服务选项说明
           选项名称      描述                                                                                                                                    
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           flags         各种xinetd特有的flags控制着服务器运转                                                                                                   
                         如果你只指定NAMEINARGS,那么它就使参数和inetd一样传递                                                                                   
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           type          如果你正定义已给不在/etc/services列表上的服务,你就应该使用UNLISTED,否则你可以省略type这一行                                            
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           port          如果你设置了type=UNLISTED,则必须在这指定端口号                                                                                          
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           socket_type   如果是TCP,则为stream。如果是UDP,则是dgram                                                                                             
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           protocol      tcp/udp                                                                                                                                 
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           wait          对应所有的TCP服务器,设为no。                                                                                                           
                         对于UDP,如果服务器连接远程机器并未不同机器的信息包请求一个新的进程来处理,那么也应该使用no                                             
                         如果UDP在它的端口上处理所有的信息包,知道它被终止,那么应该使用yes                                                                      
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           user          指定了程序应该在哪个用户下运行                                                                                                          
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           server        实现服务器实际程序的完整路径                                                                                                            
          -------------+----------------------------------------------------------------------------------------------------------------------------------------
           server_args   传递给服务器的一系列参数。如果为了和inetd兼容,使用了NAMEINARGS flag,则必须指定至少一个参数(服务器名),其他参数可以在服务器名后指定  
      2. 通过inetd使用socket对象
         通常socket对象由socket.socket()调用来建立。但如果你的服务器程序由inetd启动,那么你需要根据inetd传给程序的文件描述符,通过调用socket.fromfd()来建立socket对象
         socket.fromfd()与socket.socket()相比,多了一个文件句柄的参数
      s=socket.fromfd(sys.stdin.fileno(),socket.AF_INET,socket.SOCK_STREAM)

      3. 与inetd相关的错误处理
         由于每个inetd服务器进程只处理一个客户端,所以服务器进程由于一个错误而终止就不是一个严重问题
         但也不能说一定没有问题,有些inetd实现,会把stderr传给客户端,这时候异常会通过网络传给客户端,这样会严重迷惑客户端,而且服务器永远不知道发生了这个问题,这时就需要捕获错误并保持在日志中

    1.2 在python中使用syslog
    =========================
       1. 在开始记录信息之前,必须调用openlog函数来初始化syslog接口:
          openlog(ident[,logopt[,facility]])
          1. ident:是一个标识字符从,它会自动加入到每一条日志信息中。通常它是程序名或PID
          2. logopt日志选项,可以用Python位运算符或操作符结合
               选项名词     描述                                                                                                          
              ------------+--------------------------------------------------------------------------------------------------------------
               LOG_CONS     当访问不到机器的syslog进程或记录信息发生错误时,在系统的首选物理Consol上直接显示该信息                        
              ------------+--------------------------------------------------------------------------------------------------------------
               LOG_NDELAY   不进行任何延时就打开syslog程序的连接,一般情况是当有第一条日志信息时打开                                      
              ------------+--------------------------------------------------------------------------------------------------------------
               LOG_NOWAIT   在系统上建立一个新的进程来记录信息,不用wait()等待集成。有些系统不建立新进程,在这些系统上,这个选项不起作用  
              ------------+--------------------------------------------------------------------------------------------------------------
               LOG_PID      自动在每条日志信息中包括进程ID                                                                                
              ------------+--------------------------------------------------------------------------------------------------------------
               LOG_PERROR   错误除了记录到syslog中,还会在stderr上打印出来                                                                
          3. facility工具参数,用来识别产生信息的程序类型
               工具名称     描述                                                                                
              ------------+------------------------------------------------------------------------------------
               LOG_AUTH     认证信息:登录、退出                                                                
              ------------+------------------------------------------------------------------------------------
               LOG_CRON     来自自动命令日程安排程序的信息                                                      
              ------------+------------------------------------------------------------------------------------
               LOG_DAEMON   任何不能被归入日志种类的系统服务器信息                                              
              ------------+------------------------------------------------------------------------------------
               LOG_KERN     操作系统的核心信息,python程序中应该尽量少用                                        
              ------------+------------------------------------------------------------------------------------
               LOG_LOCALx   从LOG_LOCAL0到LOG_LOCAL7,是为了本地使用,由每一个系统管理员自己定制的。             
                            如果你的应用程序只是在内部用,才能使用这些工具,因为在其他地方,LOG_LOCALx是不同的  
              ------------+------------------------------------------------------------------------------------
               LOG_LPR      打印服务器信息                                                                      
              ------------+------------------------------------------------------------------------------------
               LOG_MAIL     和邮件有关的信息                                                                    
              ------------+------------------------------------------------------------------------------------
               LOG_NEW      Usernet新闻信息                                                                     
              ------------+------------------------------------------------------------------------------------
               LOG_USER     用户定义的普通信息,该选项默认                                                      
              ------------+------------------------------------------------------------------------------------
               LOG_UUCP     UUCP信息                                                                         
       2. 调用syslog函数记录信息
          syslog([priority,]message)
          1. priority被syslog配置文件用来确定对一个给定的信息该如何处理,默认为LOG_INFO
          2. syslog优先权说明
               优先权名词    描述                                  
              -------------+--------------------------------------
               LOG_EMERG     紧急情况,整个系统非正常关机或不能用  
              -------------+--------------------------------------
               LOG_ALERT     给管理员发出警报;需要立即采取措施    
              -------------+--------------------------------------
               LOG_CRIT      一个致命错误                          
              -------------+--------------------------------------
               LOG_ERR       一个普通错误                          
              -------------+--------------------------------------
               LOG_WARNING   一个警告                              
              -------------+--------------------------------------
               LOG_NOTICE    对于一个重要的正常情况的通知          
              -------------+--------------------------------------
               LOG_INFO      普通信息                              
              -------------+--------------------------------------
               LOG_DEBUG     调试信息;通常丢弃                             

    2 域名系统
    ~~~~~~~~~~~

    2.1 正向查找
    =============
       1. [(family,socktype,proto,canonname,sockaddr),...]=getaddrinfo(host,port[,family[,socktype[,proto[,flags]]]])
          1. host就是要寻找的域名,其他参数只有当想把结果直接传递给socke.socket()或socket.connect的时候采用到。
        可以设置port为None,然后省略其他参数来进行一个基本的查询
          2. sockaddr就是远程机器的地址
          3. sockaddr是一个tuple,格式为(host,port),主要是为了方便传入connect()函数
       2. socket.gethostbyname()目前只支持IPV4

    2.2 反向查找
    =============
       1. 由于对于一个IP地址,完全有可能不存在反向映射,故对每一个反向查找行为都需要捕获socket.herror
       2. socket.gethostbyaddr(ip)
          gethostbyaddr支持IPV6
          返回的是一个元组,元素0为hostname,元素2为address

    2.3 获得运行程序机器的域名信息
    ===============================
       1. socket.geethostname()获取主机名
       2. socket.getfqdn(主机名)获取完整的域名
       3. socket.getaddrinfo(完整域名,None)获取IP
       

    2.4 使用pyDNS
    ==============
       1. 调用DNS.DiscovernameServers()或者直接设置DNS.dausetlf['server']=['ip1','ip2'...]
       2. 通过调用DNS.Request()来建立一个请求对象
       3. 请求对象的req(name,qtype)执行实际查询
          name为实际查询的名称
          qtype为record类型
       4. 请求对象发出查询后,返回应答对象,应答对象的answers属性包含了应答列表
       5.

    3 高级网络操作
    ~~~~~~~~~~~~~~~

    3.1 半开发socket
    =================
       通常socket是双向的,但如果你想建立一个单向socket,及数据只能在一个方向上传送(也被称为半开放socket),需要调用shutdown()函数
       对shutdown的调用需要一个单独参数,来说明你想怎么光比socket。
         0   禁止将来读  
        ---+------------
         1   禁止将来写  
        ---+------------
         2   禁止将来读和写  
       一旦给出了关闭的方向,socket就不能在该方向上在重新打开了。
       对shutdown的调用时累计的,即调用shutdown(0)后再调用shutdown(1),效果和调用shutdown(2)一样

    3.2 超时
    =========
       1. 调用socket.settimeout(超时秒数)函数,如果经过超时时间后,什么都没有发生,则会产生一个socket.timeout异常

    3.3 广播数据
    =============
       广播数据不能用TCP实现,它多数使用UDP来实现的
       1. 调用socket().setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)来使socket支持广播
       2. 发送的主机使用特殊的地址"<broadcast>",而不是标准的IP地址和主机名

    3.4 使用poll()或select()实现事件通知
    =====================================
       windows不支持poll(),必须使用select()
       select()接口早起使用普遍,但比较笨重,而且慢
       1. select.poll()调用返回poll对象p
       2. p.register(s.fileno(),poll选项集合)注册希望观察的socket和poll事件
            POLLIN                                   
           ----------+------------------------------
            POLLERR                                  
           ----------+------------------------------
            POLLHUP                                  
           ----------+------------------------------
            POLLOUT    至少一个数据包可以被立即发送  
           ----------+------------------------------
            POLLPRI    准备读取重要数据              
           ----------+------------------------------
            POLLNVAL   不正确的请求                  
       3. result=p.poll(等待的毫秒数)  如果什么都没发生,则返回空列表,否则返回含有poll事件的列表
       4. select(iwtd,owtd,ewtd[,timeout])
          iwtd:观察输入的文件对象列表
          owtd:观察输出的文件对象列表
          ewtd:观察错误的文件对象列表
          timeout:浮点类型,用来指明超时秒数
       5. 对select()的调用返回3个tuple,每个tuple都是一个准备好的对象列表,顺序与参数一样

    3.5 urllib2
    ============
       1. urllib2.urlopen(url[,post-data])
       2. 任何在连接过程中产生的异常要么都是urllib2.URLError的实例,要么是它的一个子类
          urllib2.HTTPError的实例很特别,它本身是一种文件类对象,可以迎来读!读取的内容就是浏览器用来显示出错页面(例如404页面等)的内容
       3. 在读取数据的时候,会有两种情况发生:一是通信错误,会使socket模块在调用read()函数时产生socket.error;二是当程序发送文档时,服务器出现问题或由于文档被删除使得发送的文档被截断
          要检查第二种情况,一个方法时首先在服务器的回答中找到包头中记录的内容长度,然后跟收到的报文长度比较
          若内容长度的包头不被提供,则服务检查出这种错误。
       4. urlib2.urlopen().info()函数返回报文头字典

    4 解析HTML和XHTML
    ~~~~~~~~~~~~~~~~~~

    4.1 使用HTMLParser模块解析HTML
    ===============================
       1) 你需要定义一个HTMLParser.HTMLParser的子类,并实现处理不同标签的函数
       2) 需要定义的函数有
          handle_starttag(self,tag,attrs)
          handle_data(self,data)
          handle_endtag(self,tag)
          handle_entityref(self,name)                 #当出现实体的时候调用,在htmlentitydefs类中提供了HTML实体的映射
          handle_charref(self,name)                   #当出现字符参考时调用++-
       3) HTMLParser的feed()方法会适当地调用handle_starttag(),handle_data(),handle_endtag(),handle_entityref()方法
     

    4.2 XML和XML-RPC
    =================
       1. SAX是基于事件模型的,DOM是基于树的

    4.2.1 DOM模型
    --------------

    4.2.2 xmlrpclib库
    ------------------

    5 E-mail服务
    ~~~~~~~~~~~~~

    5.1 E-mail的编写和编码
    =======================
       1. 每一个传统的Email都包含两个不同部分:header和body。header包含控制数据,例如寄件人、目的地、信息的标题。body保安信息本身。最开始总是header,然后是body.header和body之间由一个空行区分
       2. 发送邮件给谁,由SMTP携带这些信息。跟header其实无关
       3. 产生邮件的模块安装在email模块中,一般用email.MIMEText模块中的MIMEText类或email.Message.Message
          使用email.Utils.make_msgid()来得到Message-ID header
          使用email.Utils.formatdate()来得到Date header
          from email.MIMEText import MIMEText
          from email import Utils
          message="hello"
          msg=MIMEText(message)
          msg['To']='reciver@example.com'
          msg['From']='sender@example.com'
          msg['Subject']='Test message'
          msg['Date']=Utils.formatdate(localtime=1)
          msg['Message-ID']=Utils.make_msgid()
          print msg.as_string()
       4. 使用email.message_from_file(fd)来解析邮件
       5. 使用email.Utils.mktime_tz与email.Utils.parsedate_tz()函数联合起来解析邮件的date header
          parsedate_tz()载入一个日期字符串,返回一个10个元素的tuple,如果输入有误则得不到
          tuple的前9个元素可以传递给time.mktime(),
          还有一个mktime_tz()函数可以理解全部的10个元素,把它转换成一个标准的,从新纪元开始至今的秒数
       6. MIME
          1. 一般约定,最基本的内容(纯文本邮件)会出现在最前面,这样没有识别MIME的邮件程序也可用阅读纯文件
          2. 添加MIME附件的方法
         1. 建立一个MIMEMultipart()对象,设置邮件的header
         2. 为邮件内容部分建立一个MIMEText()对象,把它放到MIMEMultipart对象中
         3. 为每一个附件建立一个合适的MIME对象,也把它放到MIMEMultipart()对象中
         4. 调用MIMEMultipart()对象中的as_string()函数来得到作为结果的邮件
            def p_w_upload(filename):
                fd=open(filename,'rb')
            mimetype,mimeencoding=mimetypes.guess_type(filename)
            if mimeencoding or (mimetype is None):
                mimetype='application/octet-stream'
            maintype,subtype=mimetype.split('/')
            if maintype == 'text':
                retval=MIMEText(fd.read(),_subtype=subtype)
            else:
                retval=MIMEBase(maintype,subtype)
                retval.set_payload(fd.read())
                Encoders.encode_base64(retval)
            retval.add_header('Content-Disposition','p_w_upload',filename=filename)
            fd.close()
            return retval
          3. MIME替换方法
         MIME替换方法可以产生一个单独文件的多个版本,用户的邮件程序会自动决定显示哪个
         替换方法和添加附件的方法的区别在于不需要Content-Disposition header:
         def p_w_upload(filename):
             fd=open(filename,'rb')
             mimetype,mimeencoding=mimetypes.guess_type(filename)
             if mimeencoding or (mimetype is None):
             mimetype='application/octet-stream'
             maintype,subtype=mimetype.split('/')
             if maintype == 'text':
             retval=MIMEText(fd.read(),_subtype=subtype)
             else:
             retval=MIMEBase(maintype,subtype)
             retval.set_payload(fd.read())
             Encoders.encode_base64(retval)
             #替换方法和添加附件的方法的区别在于不需要Content-Disposition header
             fd.close()
             return retval
          4. 构建非英语的header
         email.Header模块中的Header类,可以实现用指定字符集编码头数据
         from email.Header import Header
         fromhdr=Header("卢俊蔚",'utf-8')
         fromhdr.append('<lujw@drcbank.com>','ascii')
         msg['From']=fromhdr
         Email地址使用另外的字符集单独添加,否则,编码车功能需就会对Email地址编码,不含MIME功能的程序就不能回复该邮件
          5. 解码header
         使用email.Header.decode_header(x)可以返回一个header内容的列表。
         列表中列表项的格式为(header的独立编码部分,编码文件的字符集)
         若header没有编码,即为ascii,则返回的字符集为None
          6.

    5.2 SMTP
    =========
       1. 使用smtplib发送邮件
          s=smtplib.SMTP(server)
          s.sendmail(fromaddr,toaddrList,message)
          这里message包含header信息,可以用email模块来构建
          另外,只有sendmail()的收件人才能决定谁收到邮件,跟header的To和Cc无关
       2. 使用smtplib时可能产生的错误
            socket.gaierror         寻找地址时出现的错误  
           -----------------------+----------------------
            socket.error            普通IO和通讯错误      
           -----------------------+----------------------
            socket.herror           其他地址错误          
           -----------------------+----------------------
            smtplib.SMTPException   SMTP会话问题          
       3. 通过smtp().set_debuglevel(1)可以开启smtplib的调试模式,它提供了基本的错误处理和调试
          开启了调试模式之后,就能看出smtplib和SMTP服务器
       4. HELO和EHLO
          SMTP的初始版本中,客户端会向服务器发送一个HELO指令作为初始问候。
          SMTP的一系列扩展,称为ESMTP,具有ESMTP功能的服务端会开始EHLO会话,它提示具有ESMTP功能的服务器发送扩展信息。这个信息除了正常信息外,还包括邮件最大容量
          多数现代邮件服务器支持EHLO,服务器会返回它支持的可选SMTP特征的信息
          在一些不支持ESMTP的服务器上,EHLO会返回一个错误,这是你必须发送HELO命令
          如果手工调用了EHLO或HELO指令,sendmail不会再试图自动发送这些指令了
          s=smtplib.SMTP(server)
          code=s.ehlo()[1]
          if not (200<=code<=299):
              code=s.helo()[1]
          if not (200<=code<=299):
              raise SMTPHeloError(code,resp)
       5. SMTP使用TLS层加密
          1. 像通常那样建立SMTP对象
          2. 发送EHLO指令。如果远程主机不支持EHLO,它不支持TLS
          3. 检查啊s.has_ext(),看它是否提供starttls。如果不提供,远程主机不支持TLS,邮件需要以正规方法发送
          4. 调用starttls()来初始化通道
          5. 再次调用ehlo(),这次它是加密的了
          6. 左后,像往常一样发送邮件
       6. SMTP实现认证
          SMTP().login(username,password)
          如果你使用的服务器不支持认证,你会收到一个'Authentication failed"错误提示。可以在调用s.ehlo()后使用s.has_extn('auth')来避免这个错误
         

    5.3 POP协议
    ============
       1. 连接和认证
          1. 建立一个pop3对象,传给它远程服务器的主机名和端口号
          2. 调用user()和pass_()函数来发送用户名和密码
          3. 如果产生poplib.error_proto异常,登录就失败,服务器会发送和异常有关的字符串和解释文字
          4. 若使用APOP认证,则调用POP3().apop(user,passwd)来认证
          5. 若认证失败,则抛出poplib.error_proto异常
       2. 取得邮箱信息
          1. pop3().stat()返回一个tuple,其中包含了服务器邮件中邮件数量和邮件总大小
          2. pop3().list()会返回每一封邮件更详细的信息。list函数返回一个包含两个条目的tuple,第一个是应答代码,另一个是字符串的List。
         列表的每一个字符也包含两个条目,中间有一个空格:邮件的数字和邮件的字节数
       3. 下载邮件
          1. pop3().retr(邮件数字),每次下载一封邮件
          2. retr()函数返回一个tuple,其中包含了结果代码和邮件。但邮件不是字符串格式的,而是一个字符串的列表,每个元素代表该邮件的一行
       4. 删除邮件
          pop().dele(邮件编号),每次只删除一个文件。
          大多数POP服务器只有在你调用了quit()之后,才会真正删除这些邮件

    5.4 TODO IMAP协议
    ==================
        1. Twisted中的IMAP
          

    6 FTP
    ~~~~~~
      1. ftplib
         1. 如果只是想下载文件的话,用urllib2模块比ftplib更简单
         2. ftplib.FTP实例的函数
          f=FTP(ftp服务器地址)                    新建一个FTP对象                                                                                                              
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.getwelcome()                          获取欢迎信息                                                                                                                 
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.login([username,password])            登录ftp                                                                                                                      
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.pwd()                                 获取当前工作目录                                                                                                             
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.cwd(远程目录)                         在远程系统上转换目录                                                                                                         
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.retrlines(运行的指令,回调函数)        以ASC模式下载文件。retrlines函数的第一个参数指定一个在远程系统运行的指令,这个参数一般是RETR,后面是文件名。                 
                                                  它的第二个参数是一个函数,客户端没收到一行数据后都会运行一次这个函数;                                                       
                                                  如果第二个参数被省略,数据会输出到标准输出设备上来。                                                                         
                                                  数据在传输的时候,每一行的行尾都会去掉                                                                                       
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.retrbinary(运行的指令,回调函数        retrbinary函数可以向指定的函数传输整块的数据                                                                                 
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.voidcmd(FTP指令)                      直接向FTP服务器传输一条指令,检查有没有错误,但该函数不返回任何结果                                                          
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.ntransfercmd(FTP下载/上传指令) 例如   以高级二进制模式下载/上传文件。                                                                                              
          f.ntransfercmd('RETR linux.tar')        该函数返回一个tuple:(数据的socket,数据大小的估计值)。注意,该估计值不是精确的,如果FTP服务器上得不到估计值,则估计值为None  
          f.ntransfercmd('STOR linux.tar')        在接受完数据后,要关闭数据socket并调用voidresp()函数                                                                         
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.voidresp()                            获得FTP服务器的响应,如果发现任何错误就报错                                                                                  
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.storbinary(STOR命令,文件类型fd)       实现二进制上传,注意与下载函数不同,第二个参数为文件类型对象,而下载函数的第二个参数为函数                                   
                                                  storbinary()调用的是传入文件类型对象的read()方法                                                                             
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.storlines(STOR命令,文件类型fd)        实现文本格式上传。                                                                                                           
                                                  storlines()函数调用的是文件类型对象的readline()方法                                                                          
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.nlst()                                返回服务器上当前目录下的一系列条目,但仅能获得文件和目录列表,而无其他信息                                                   
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.dir(回调函数)                         从远方服务器上返回一个目录列表,这个列表格式根据FTP服务器操作系统的不同而定,包括详细信息,返回的每一行都会送入回调函数处理  
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.delete(文件名)                        删除文件                                                                                                                     
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.rmd(目录名)                           删除目录                                                                                                                     
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          f.mkd(目录名)                           新建一个目录                                                                                                                 
         ---------------------------------------+-----------------------------------------------------------------------------------------------------------------------------
          rename(旧文件名,新文件名)               跟UNIX的mv指令一样                                                                                                              

    7 数据库
    ~~~~~~~~~
      1. 参数风格说明
         通过查看数据库模块的paramstyle变量来查看
         1. qmark:
        表示question-mark风格。指令字符串中的数据的每一位都用一个问号替换,参数以list或tuple的形式给出。
        Insert into ch14 value(?,?)
         2. format:
        使用和printf一样的类型格式,不支持对于指定参数python的扩展名。它带一个list或tuple来转换
        Insert into ch14 values(%d,%s)
         3. nueric:
        指令字符串中的数据的每一位都被一个后面是数字的冒号代替,数字从1开始,参数以list或tuple给出
        insert into ch14 values(:1,:2)
         4. named:
        和numeric类似,但是冒号后面用名称取代数字,用一个dictionary转换
        Insert into ch14 values(:number,:text)
         5. pyformat:
        至此和Python风格的参数,带dictionary转换
        insert into ch14 values(%(number)d,%(text)s)
      2. 使用executemany()一次执行多个SQL语句
         executemany()函数带一个指令和一列该指令运行的记录。记录上的每条记录为一个list或dictionary,着取决于数据库模块的参数风格
         import sqlite3
         values=([1,'one'],[2,'two'],[3,'three'])
         cur=sqlite3.connect().cursor()
         cur.executemany("insert into ch14 values(?,?)",values)
         cur.execute("insert into ch14 values(?,?)",[5,'five'])
      3. 获取数据
           cursor.fetchall()            获取所有记录                                         
          ----------------------------+-----------------------------------------------------
           cursor.fetchmany([number])   通过设置cursor的arraysize属性来决定每次返回的结果数  
                                        或者传递给fetchmany一个指定的大小                    
          ----------------------------+-----------------------------------------------------
           cursor.fetchone()            返回单独行,若没有数据了返回None                     
      4. 元数据
         除了通过查询的数据外,数据库服务器还可用返回metadata,这个metadata包含一些诸如结果名称和类型的信息
         可以通过cursor.description来获得metadata
      5. Python与SQL类型转换
         1. Binary():
        带一个字符串,并产生一个二进制对象,该对象主要设计城保存大的二进制数据
         2. Date():
        带一个整数的年月日,产生一个日期对象,其中年能使用2位的简化表示方法
         3. DateFromTicks():
        带一个整数或者浮点数,表示UNIX新纪元到现在的秒数,产生一个日期对象。参数和time.time()格式一样
         4. Time():
        带小时(24小时格式),分,秒,都是整数,产生时间对象
         5. TimeFromTicks():
        类似DateFromTicks
         6. Timestamp():
        带一个年(不能2位表示),月,日,时,分,秒,产生一个timestamp类
         7. TimestampFromTicks()
        类似DateFromTicks

    8 SSL
    ~~~~~~
      1. 内置SSL
         1. 建立一个SSL类型
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect(("www.openssl.org",443))
        ssl=socket.ssl(s)
         2. ssl对象只提供两个方法:read()和write(0
      2. pyOpenSSL
         1. 建立一个SSL类型
        from OpenSSL import SSL
        ctx=SSL.Context(SSL.SSLv23_METHOD)
        s=socket.socket(socket.AF_INET,socket_SOCK_STREAM)
        ssl=SSL.Connection(ctx,s)
        ssl.connect(('www.openssl.org',443))
        ssl.sendall("xxx")
        buf=ssl.recv(4098)
        

    9 SocketServer
    ~~~~~~~~~~~~~~~
      SocketServer是Python的框架,用来在服务器上处理来自客户端的请求。
      SocketServer非常适合编写那种接受一个请求并返回一个应答的服务器程序。
      SocketServer相关类一样,它定义了两个类,一个Server对象类,一个request处理类
      1. BaseHTTPServer
         Server类:HTTPServer
         request处理类:BaseHTTPRequestHandler
         1. 建立一个BaseHTTPServer
        class Requesthandler(BaseHTTPRequestHandler):
            pass
        serveraddr=('',8765)
        srvr=HTTPServer(serveraddr,RequestHandler)
        srvr.serve_forever()
        为了实现自己的HTTP服务器,需要定义一个BaseHTTPRequesthandler的子类。
         2. BaseHTPRequestHandl类提供了一些方便的方法
        send_response()
        send_header()
        end_headers()
        还提供了rfile和wfile变量提供直接存取数据流,并发送回文档
         3. 需要自定义的方法有
        do_GET(self)
        do_HEAD(self)
        do_POST(self)
         4. 可以通过同时继承自定义的BaseHTTPRequesthandler的子类和ThreadingMixIn的方式,使得HttpServer实现多线程服务
      2. SimpleHTTPServer
         Server类:simpleHTTPServer
         request处理类:SimpleHTTPRequestHandler
      3. CGIHTTPServer
         Server类:CGIHTTPServer
         request处理类:CGIHTTPRequestHandler
      4. 使用SocketServer模块中的TCPServer/UDPServer和StreamRequestHandler/DatagramRequestHandler等类来实现自己的协议
         TCPServer/UDPServer收到请求后会调用和StreamRequestHandler/DatagramRequestHandler子类中的handler()方法来处理请求
         BaseRequestHandler(及其子类如StreamRequestHandler类等)会初始化一些变量,这些变量包含了客户端和环境变量的信息。如request,client_address

    10 SimpleXMLRPCServer
    ~~~~~~~~~~~~~~~~~~~~~~
      1. 建立一个服务器
         srvr=SimpleXMLRPCServer(serverAddr,SimpleXMLRPCRequestHandler)
      2. 注册一个实例/函数
         srvr.register_instance(Math())
         srvr.register_introspection_functions()
         /
         srvr.register_function(函数名词)
         srvr.register_function(list.sort)          #这个返回值为None,调用会报错
         需要注意的是,注册的函数必须是不能为None的,因为默认通常表示出问题了。
         如果真要接受None,可以爱建立Server实例时,设置allow_none为true
         另外,XML-RPC不能发送Python自定义的类对象,所以参数和返回值必须是简单数据类型,list和dictionary
      3. 启动服务
         srvr.serve_forever()
      4. DocXMLRPCServer模块使客户端可以用Web浏览器查看XML-RPC产生的每个方法描述
      5. CGIXMLRequestHandler类(SimpleXMLRPCServer模块的一部分)可以把CGI脚本转换成XML-RPC服务器
         需要注意的是,使用是这个类需要用用在CGI脚本中,供Web服务器调用的
         handler=CGIXMLRPCRequestHandler()
         handler.register_instance(Math())
         handler.register_introspection_functions()
         handler.handle_request()
      6. 启用Multicall
         Multicall是对标准XML-RPC的一个非正式补充,它使客户端一次向XML-RPC服务器提交多个请求
         srvr.register_multicall_functions()
         srvr.serve_forever()

关键字