天天看點

python學習筆記_19(網絡程式設計)

網絡程式設計

1. 網絡程式設計基礎子產品

  • socket子產品
    • 網絡程式設計中基本元件就是嵌套字(socket),嵌套字基本上就是兩個端點的程式之間的“資訊通道”。python中嵌套字主要使用socket子產品。
    • 嵌套字包括服務端嵌套字和用戶端嵌套字,服務端在建立嵌套字等待連接配接,用戶端通過ip和端口連接配接服務端
    • 一個嵌套字就是socket子產品中socket類的一個執行個體,執行個體化時需要3個參數,對于普通嵌套字,不需要提供任何參數
      參數 預設值 可選值
      第一個參數位址族 socket.AF_INET AF_UNIX
      第二個參數流 socket.SOCK_STREAM socket.SOCK_DGRAM - 資料報
      第三個參數協定
    • 執行個體化一個socket後,服務端使用bind綁定主機位址和端口,在調用listen方法開始監聽,用戶端則使用connect方法連接配接到伺服器,connect方法中使用的位址與伺服器bind方法中的位址相同。
    • listen方法隻有一個參數,伺服器未處理的連接配接的長度(即允許排隊等待的連接配接數目)。
    • 一個位址就是格式為(host, port)的元組,服務端可以使用socket.gethostname得到目前的主機名
    • 服務端開始監聽後接收用戶端的連接配接,使用accept方法來完成,accept方式是阻塞式的,連接配接建立後傳回一個(client, address)的元組,client是用戶端嵌套字,address是位址元組。
    • 嵌套字傳輸資料是使用send和recv方法,recv使用所需的最大位元組數參數來接收資料,send使用字元串參數發送資料
  • urllib和urlib2
    • urllib有基礎的功能,urllib2包括了http驗證或cookie等功能
      # urllib使用
      >>> from urllib import urlopen
      >>> webpage = urlopen('http://www.python.org')
      
      #urlopen傳回的是一個類檔案對象,支援close,read,readline和readlines
      >>> import re
      >>> text = webpage.read()
      >>> m = re.search('<a href="([^" target="_blank" rel="external nofollow" ]+)" .*?>about</a>', text, re.IGNORECASE)
      >>> m.group(1)
      '/about'
      
      #擷取遠端檔案urlretrieve, 傳回一個元組(filename, headers)
      urlretrieve('http://www.python.org', r'c:\python_webpage.html')
                 
  • 其他子產品
    子產品 描述
    asynchat asyncore的增強版
    asyncore 異步嵌套字處理程式
    cgi 基本的CGI支援
    Cookie Cookie對象操作,主要用于伺服器
    Cookielib 用戶端Cookie支援
    email E-mail消息支援(包括MIME)
    ftplib Ftp用戶端子產品
    gopherlib gopher用戶端子產品
    httplib HTTP用戶端子產品
    imaplib IMAP4用戶端
    mailbox 讀取幾種郵箱的格式
    mailcap 通過mailcap檔案通路MIME配置
  • SocketServer子產品
    • SocketServer子產品是标準庫中很多伺服器架構的幾次,增加了服務端socket的特定功能
    • SocketServer封裝了4個類,針對TCP的TCPServer,針對UDP的UDPServer,以及UnixStreamServer和unixDatagramServer
      # SocketServer使用方法
      # 編寫使用SocketServer架構的服務,大部分代碼放在一個請求處理程式中(request handler)
      # 每當伺服器收到一個請求,就會執行個體化一個請求處理程式,并各種處理方法(handler method)會在處理請求時被調用
      
      #socket_server_demo.py
      class Handler(StreamRequestHandler):
      
          def handle(self):
              addr = self.request.getpeername()
              print 'Got connection from', addr
              self.wfile.write('Thank you for connecting')
      
          
      def socket_server_demo():
          #host = socket.gethostname()
          server = TCPServer(('', 1234), Handler)
          server.serve_forever()
          
      
      
      if __name__ == "__main__":
          #main()
          socket_server_demo()
          
      #socket_client.py
      import socket
      
      
      def main():
          s = socket.socket()
          host = socket.gethostname()
          port = 1234
          s.connect((host, port))
          print s.recv(1024)
      
      
      if __name__ == "__main__":
          main()
      
                 

2. 同時多個連接配接

  • 分叉(forking)
  • 線程(threading)
  • 異步I/O(asynchronous)
# forking為linux/unix上的技術,fork一個程序時,基本上是負載它,屬于多程序技術,windows上不支援

#! /bin/sh
# -*- coding: utf-8 -*-

# file name: socket_server_mixin


import time
from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
from SocketServer import ThreadingMixIn

class ServerForking(ForkingMixIn, TCPServer):
    pass

class ServerThreading(ThreadingMixIn, TCPServer):
    pass

class Handler(StreamRequestHandler):

    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection form', addr
        print 'waiting for 10s'
        time.sleep(10)
        self.wfile.write('Thank you for connecting')


if __name__ == "__main__":
    #forking方式
    s = ServerForking(('', 1234), Handler)
    # threading方式
    #s = ServerThreading(('', 1234), Handler)
    s.serve_forever()
  
# 3. 異步I/O,異步I/O的基礎是select/poll函數,來自select子產品,poll伸縮性很好,但隻能用于unix系統
# select函數需要3個序列參數,分别是輸入,輸出,異常,序列是檔案描述符整數或者有傳回這樣整數的fileno參數,第4個參數是機關為秒的逾時時間,如果沒有給seelect會阻塞,如果為0那麼就給出一個連續的poll,傳回長度為3的元組
#! /bin/sh
# -*- coding: utf-8 -*-

# file name: socket_server_select.py


import socket
import select

s = socket.socket()
host = socket.gethostname()
port = 1234

s.bind((host, port))
s.listen(5)

inputs = [s]

while True:
    rs, ws, es = select.select(inputs, [], [], 1)
    for r in rs:
        if r is s:
            c, addr = s.accept()
            print 'Got connection from', addr
            inputs.append(c)
        else:
            try:
                data = r.recv(1024)
                disconnected = not data
            except socket.error:
                disconnected = True

            if disconnected:
                print r.getpeername(), 'disconnected'
                inputs.remove(r)
            else:
                print data
                
# 1. pool函數,調用是傳回一個poll對象,使用poll對象register方法注冊一個檔案描述符或帶有fileno的對象,注冊後可以使用unregister方法移除,注冊後調用poll方法(帶有可選的的逾時時間參數)并得到(fd, event)元組,fd是檔案描述符,event事件,event為位掩碼,不同的事件是select子產品的常量
def socket_poll():
    s = socket.socket()
    host = socket.gethostname()
    port = 1234
    s.bind((host, port))

    fdmap = {s.fileno(): s}

    s.listen(5)

    p = select.poll()
    p.register(s)

    while True:
        events = p.poll()
        for fd, event in events:
            if fd == s.fileno():
                c, addr = s.accept()
                print 'Got connection from', addr
                p.register(c)
                fdmap[c.fileno()] = c
            elif event & select.POLLIN:
                data = fdmap[fd].recv(1024)
                if not data:
                    print fdmap[fd].getpeername, 'disconnected'
                    p.unregister(fd)
                    del fdmap[fd]
            else:
                print data
           
事件名 描述
POLLIN 讀取來自檔案描述符的資料
POLLPRI 讀取來自檔案描述符的緊急資料
POLLOUT 檔案描述符已經準備好資料,寫入時不會發生阻塞
POLLERR 與檔案描述符有關的錯誤情況
POLLHUP 挂起,連接配接丢失
POLLNVAL 無效請求,連接配接沒有打開

3. Twisted

#! /usr/bin/python
# -*- coding: utf-8 -*-

#file name: socket_server_twisted.py

import socket
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
from twisted.protocols.basic import LineReceiver


class SimpleLogger(Protocol):

    def connectionMade(self):
        print 'get connection from', self.transport.client

    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'

    def dataReceived(self, data):
        print data


class SimpleLineLogger(LineReceiver):

    def connectionMade(self):
        print '[Line recevier]', 'get connection from', self.transport.client
        self.sendLine('welcome to %s' % socket.gethostname())
        self.setLineMode()

    def connectionLost(self, reason):
        print '[Line recevier]', self.transport.client, 'disconnected'

    def lineReceived(self, line):
        print '[Line recevier]', line

   #def dataReceived(self, data):
   #     print '[Data recevier]', data

factory = Factory()
#factory.protocol = SimpleLogger
factory.protocol = SimpleLineLogger

reactor.listenTCP(1234, factory)
reactor.run()
           
#! /user/bin/python
# -*- coding: utf-8 -*-

# file name: socket_client.py


import socket


def main():
    s = socket.socket()
    #s.setblocking(False)
    #print 'default timeout', s.getdefaulttimeout()
    #s.setdefaulttimeout(1)
    #host = socket.gethostname()
    host = "192.168.1.198"
    port = 1234
    rlt = s.connect((host, port))
    #print rlt
    print 'Hello %s, %s\n' % s.getsockname()
    s.send('Hello %s, %s\r\n' % s.getsockname())
    print s.recv(1024),


if __name__ == "__main__":
    main()