轉載請註明:@小五義 http://www.cnblogs.com/xiaowuyi
學慣用書:《python 網路編程基礎》作者John Goerzen
第一部分底層網路學習
Python提供了訪問底層作業系統Socket介面的全部方法,需要的時候這些介面可以提供靈活而強有力的功能。
(1)基本用戶端操作
在《python 網路編程基礎》一書中,作者列出了一個簡單的Python用戶端程式,具體如下:
import socket,sysport =70host=sys.argv[1]filename=sys.argv[2]s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect((host,port))s.sendall(filename+"\r\n")while 1: buf=s.recv(2048) if not len(buf): break sys.stdout.write(buf)
該程式實現的是Gopher協議,實現從主機上請求相關文檔的功能。(Gopher是Internet上一個非常有名的資訊尋找系統,它將Internet上的檔案組織成某種索引,很方便地將使用者從Internet的一處帶到另一處。在WWW出現之前,Gopher是Internet上最主要的資訊檢索工具,Gopher網站也是最主要的網站。但在WWW出現後,Gopher失去了昔日的輝煌。現在它基本很少被使用。)
於是,我按照書上的語句進行了一下測試,在dos下運行python gopherclient.py quux.org。但是系統提示為
Traceback (most recent call last):
File "gopherclient.py", line 5, i
filename=sys.argv[2]
IndexError: list index out of range
看了一下,sys.argv只有兩個元素['gopherclient.py', 'quux.org/']所以filename=sys.argv[2]就超出下界了。可是為什麼會出現這個原因呢?是書裡面寫錯了嗎,因為我也是初學socket,不是很瞭解,所以我也是沒有找到原因,如果哪位大牛知道是什麼原因,希望能給講解一下。
(2)基本伺服器操作
《python 網路編程基礎》一書中同樣給出了一個簡單的伺服器程式,具體如下:
import sockethost=''port=51423s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)s.bind((host,port))s.listen(1)print "Server is running on port %d;press Ctrl-C to terminate." %portwhile 1: clientsock,clientaddr=s.accept() clientfile=clientsock.makefile('rw',0) clientfile.write("welcome,"+str(clientaddr)+'\n') clientfile.write("Please enter a string:") line=clientfile.readline().strip() clientfile.write("You entered %d characters.\n" %len(line)) clientfile.close() clientsock.close()
該程式運行後,提示“Server is running on port 51423:press Ctrl-C to terminate”。此時,通過另一台機器telnet本機器的51423連接埠,如telnet 127.0.0.1:51423,此時會提示welcome 127.0.0.1 ****,please enter a string:。 然後輸入幾個字元後,會返回你輸入字元的個數。
這裡就該程式進行一下分析:
1、首先匯入socket模組,給host和port賦值。
2、調用socket.socket()來建立一個socket賦值給s。socket.socket(domain, type, protocol).domain參數的值有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET。這幾個值中AF_UNIX=AF_LOCAL, PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL, AF_INET=PF_INET。一般來說,AF 表示ADDRESS FAMILY 地址族,PF 表示PROTOCOL FAMILY 協議族,但這兩個宏定義是一樣的,所以使用哪個都沒有關係。參數type指定socket的類型:SOCK_STREAM提供有序、可靠、雙向及基於串連的位元組流。SOCK_DGRAM支援資料報。SOCK_SEQPACKET提供有序、可靠、雙向及基於串連的資料報通訊。SOCK_RAW提供對原始網路通訊協定的訪問。SOCK_RDM提供可靠的資料報層,但是不保證有序性。protocol一般取0(為什麼取0我也沒搞清楚,放在以後明白了再寫上吧)。
3、s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)。setsockopt()函數用於任意類型、任意狀態套介面的設定選項值。儘管在不同協議層上存在選項,但本函數僅定義了最高的“套介面”層次上的選項。選項影響套介面的操作,諸如加急資料是否在普通資料流中接收,廣播資料是否可以從套介面發送等等。 這個函數中,第一個參數為協議層參數,指明了希望訪問一個選項所在的協議棧。通常我們需要使用下面中的一個:
SOL_SOCKET來訪問套介面層選項
SOL_TCP來訪問TCP層選項
第二個參數是與第一個參數相對應的。第一個參數決定了協議層level,第二個參數決定了該協議層下選項組合。SOL_SOCKET的選項組合如下:
協議層 選項名字
SOL_SOCKET SO_REUSEADDR
SOL_SOCKET SO_KKEPALIVE
SOL_SOCKET SO_LINGER
SOL_SOCKET SO_BROADCAST
SOL_SOCKET SO_OOBINLINE
SOL_SOCKET SO_SNDBUF
SOL_SOCKET SO_RCVBUF
SOL_SOCKET SO_TYPE
SOL_SOCKET SO_ERROR
具體的一些組合用法可見:http://wenku.baidu.com/view/23013b7101f69e3143329402.html
第三個參數設為1,這裡我也沒很明白其中的意思,我試著把1換成50,結果是一樣的。換成0也是可以的,沒發現什麼區別。希望大牛們給指點一下。
4、s.bind((host,port))綁定主機連接埠。
5、s.listen(1):listen函數使用主動串連套介面變為被串連套介面,使得一個進程可以接受其它進程的請求,從而成為一個伺服器處理序。在TCP伺服器編程中listen函數把進程變為一個伺服器,並指定相應的通訊端變為被動串連。這裡的參數涉及到一些網路的細節。在進程正理一個一個串連請求的時候,可能還存在其它的串連請求。因為TCP串連是一個過程,所以可能存在一種半串連的狀態,有時由於同時嘗試串連的使用者過多,使得伺服器處理序無法快速地完成串連請求。如果這個情況出現了,伺服器處理序希望核心如何處理呢?核心會在自己的進程空間裡維護一個隊列以跟蹤這些完成的串連但伺服器處理序還沒有接手處理或進行中的串連,這樣的一個隊列核心不可能讓其任意大,所以必須有一個大小的上限。這個backlog告訴核心使用這個數值作為上限。毫無疑問,伺服器處理序不能隨便指定一個數值,核心有一個許可的範圍。這個範圍是實現相關的。很難有某種統一,一般這個值會小30以內。這裡設定為1表示每次最多隻有一個等候處理的串連。
6、while迴圈從accept()函數開始。程式會在串連了一個用戶端後關閉socket。當某個用戶端串連的時,accept返回兩個資訊,一個新的串連用戶端socket和用戶端的ip地址、連接埠號碼。如在上面的例子中添加print語句輸出clientsock和clientaddr,你會發現clientsock為socket.socketobject,clientaddr=('用戶端Ip',連接埠)。後面的迴圈中使用了檔案類對象,伺服器接著顯示出一些介紹性資訊,從用戶端讀一個字串,顯示一個應答,最後關閉用戶端socket。