網絡程式設計
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()