一直不是很清楚服務器的定義,對於什么是服務器/客戶端架構也只有一個模糊的感覺。最近開始學習,才明白一些什么服務器和客戶端的關系。
所謂的服務器,就是提供服務的東西,它是一個硬件或者軟件,可以向一個或者多個客戶端提供所需要的服務。它存在的目的就是等待客戶的請求,然后給客戶服務,再接着等待請求。
而客戶端,就來連上一個服務器,提出自己的請求,然后等待獲得反饋。
比如說,打印機就是一個服務器的例子,與之相連的計算機就是客戶端,通過網絡連接打印機后,給它提出服務需求(打印)和傳輸數據(傳輸內容),然后打印機開始工作,或者返回造成服務失敗的原因(比如缺紙或者沒有墨)打印機是服務器端,它是一只等待請求的,它在一直工作,計算機這端是客戶端,它不是一只工作的。
打印機是一個硬件服務器,也有一些軟件服務器,比如說Web服務器,數據庫服務器等等。
-----------------------------------------------------------------------------
那么它們又是怎樣通過網絡連接的呢?客戶端/服務器之間的網絡編程是如何做的?
首先,我們要創建一個通訊的斷電,讓服務器可以“監聽”請求,好比公司的電話,客戶通過電話設備向公司發行請求。
當然一個服務器在有電話之后還得將自己的電話給潛在的客戶,才能得到響應,也就是說,必須得將網址發給客戶才能有用。
對於客戶端來說,同樣也是創建一個通信端點,然后建立到與服務器的連接,客戶就可以提出請求了。
-------------------------------------------------------------------------------------
關於通訊端點,就要介紹一個概念,叫套接字。
我們之前談的“通訊端點”的概念,是一個關於通訊的抽象,而套接字就是一種有這樣能力數據結構了。
就好比我們應用整型浮點型布爾型一樣,套接字也是一種數據結構,我們通過它來訪問網絡。
套接字起源於上世紀七十年代,在加州大學伯克利分校版本上的Unix上創立,開始的時候被設計用於在同一台機器上的多個應用程序通訊,也就是進程間通訊,有兩種類型,一種基於文件系統,一種基於網絡。
基於文件系統被用於不同進程通訊是很有意義的,因為文件系統是不同的進程都可以訪問的。而不同電腦間,基於網絡的套接字就是必須的了。
我先只考慮基於網絡的套接字,有兩種地址家族,一種是AF_INET,另一種是AF_NETLINK。在大部分時候,我們討論的都是有鏈接的AF_INET套接字。
套接字的地址是有兩部分組成的,一個是主機,一個是端口。
類似於電話網絡的區號和電話號碼的含義。主機確定了你訪問的機器,也就是一個IP地址,端口是你所訪問的服務器軟件所使用的端口號。一台機器上可以有很多個程序都在使用端口,合法的端口號范圍是0~65535.小於1024的端口號是系統保留的端口號。
-------------------------------------------------------------------------------------
同時,還有一個基礎知識是面向連接和面向無連接。
套接字有兩種類型,一種是面向連接的,通信前先建立一條連接,然后順序的、可靠的、不會重復的數據傳輸,不會有數據邊界。實現這種連接的主要協議是傳輸控制協議(TCP),創建TCP套接字要指定套接字類型是SOCK_STREAM.
另一種是無連接套接字,也就是說傳輸數據前先不連接,這樣數據傳輸的順序、可靠性、不重復性就不可保證。這種傳輸的主要協議是用戶數據報協議(UDP)。
----------------------------------------------------------------------------------------------
至此,網絡的基礎暫且放下,開始講Python的網絡編程。
Python提供了一個socket模塊,來創建和使用套接字。
socket()模塊函數
socket(socket_family, socket_type, protocol=0)
這個函數可以用於創建一個套接字對象,分別寫入套接字家族名和套接字類型。
創建了這個套接字對象后,所有的交互都可以通過該對象的方法調用來進行。
具體的方法可以直接查Python的文檔,我這里舉例說明一下創建一個TCP服務器的過程:
from socket import * from time import ctime HOST = '' # 主機 PORT = 8002 # 端口號,可以隨意選擇 BUFSIZ = 1024 ADDR = (HOST, PORT) #主機端口號組成一個套接字地址 tcpSerSock = socket(AF_INET, SOCK_STREAM) #創建一個套接字對象,是AF_INET族的tcp套接字 tcpSerSock.bind(ADDR) #這個函數用於綁定地址到套接字 tcpSerSock.listen(5) # 服務器開始監聽連接,參數表示最多允許同時有幾個連接進來 while True: print 'waiting for connection...' tcpCliSock, addr = tcpSerSock.accept() #用於等待連接的到來 print '...connected from:',addr while True: data = tcpCliSock.recv(BUFSIZ) #用於接收從客戶端發來的數據 參數代表一次最多接受的數據量,這里為1k if not data: break tcpCliSock.send('[%s] %s' % ( ctime(), data)) # 將時間戳作為內容發送給客戶端 tcpCliSock.close() tcpSerSock.close()
運行時,可以看到服務器開始運行,等待一個客戶端的連接,所以此刻我們需要用客戶端去連接這個服務器。
再寫一個簡單的客戶端的程序:
from socket import * HOST = 'localhost' #由於服務器開設在自己電腦上,所以主機是本地 PORT = 8002 #同一個連接端口 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) #同樣的TCP套接字 tcpCliSock.connect(ADDR) # 連接相應的地址,初始化TCP服務器的連接 while True: data = raw_input('>') if not data: break tcpCliSock.send(data) # 向服務器傳輸數據 data = tcpCliSock.recv(BUFSIZ) # 接受服務器端的數據 if not data: break print data tcpCliSock.close()
至此,你運行客戶端的程序,就可以驗證程序了。
當然,為了學習的樂趣,最好不要在本地連接,還是拿兩台電腦來連接比較好,主要是客戶端要知道服務器端的IP地址。
IP地址在網絡屬性里可以找,如圖:


然后用同樣的程序框架就可以實現兩機間的通訊了,最后可以做到如下圖的一個對講機程序:

當然,要做到一個雙方都可以自由發言的聊天工具,卻又不是這么簡單了。需要有新的嘗試了。
