---恢復內容開始---html
現有的互聯網通信方式,是服務器端的進程與客戶端進程的通訊。Python中進行網絡編程,就是在Python程序自己這個進程內,鏈接別的服務器進程的通訊端口進行通訊。編程
互聯網協議上包含了上百種協議標準,可是,最重要的是兩個協議:TCP和IP協議。因此,互聯網協議簡稱TCP/IP協議。安全
通訊時必須知道雙方的標識,而一臺計算機可能同時接入多個網絡,就會有兩個或多個IP地址。因此,IP地址實際上對應的是計算機的網絡接口,一般是指網卡。服務器
IP協議將數據分割成一小塊一小塊,而後經過IP包發送出去。特色:按塊發送,不保證能到達,也不保證能順序到達。網絡
TCP協議,創建在IP協議上,負責將兩臺計算機之間創建可靠鏈接,保證數據包按順序到達。app
特色:握手鍊接,對IP包編號,確保順序收到,若掉包,則自動重發。socket
一個IP包,包含傳輸數據,源IP地址和目標IP地址,源端口和目標端口。tcp
IP是標識在網絡中的電腦的位置,而一臺電腦上會有多個網絡通訊進程,因此端口則是標識目標進程在該電腦上的位置。學習
首先要理解socket表示「打開一個網絡連接」,而建立一個socket須要知道目標計算機中的ip,端口號,而後再指定所用的協議。測試
客戶端:主動發起連接的叫客戶端。
# 導入socket庫: import socket # 建立一個socket: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創建鏈接: s.connect(('www.sina.com.cn', 80))
客戶端導入socket庫後首先建立socket網絡連接,socket.AF_INET指定使用IPv4協議,socket.AF_INET6指定使用IPv6協議。SOCK_STREAM指定使用面向流的TCP協議。而後輸入所需的ip和端口號,進行連接,注:其中域名會自動轉換成ip。
這裏指的一提的是:網絡服務小於1024的端口號都是Internet標準服務的端口,有固定的用處,大於1024的則能夠任意使用。其中,80端口爲網頁服務端口,25爲SMTP服務端口,FTP服務爲21端口。
# 發送數據: s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
而後以HTTP格式發送數據,發起請求。
TCP協議只是創建了大體的雙向通道,而至於怎麼協調,得依據具體的協議來決定,如HTTP協議規定客戶端必須先發起請求,服務端才能返回數據。
# 接收數據: buffer = [] while True: # 每次最多接收1k字節: d = s.recv(1024) if d: buffer.append(d) else: break data = ''.join(buffer)
接受數據時,調用recv(max)方法,指定一次最多接受的字節數,所以,在一個while循環中反覆接受,知道recv()返回空數據,表示接收完畢,退出循環。
# 關閉鏈接: s.close()
最後,調用close()方法關閉Socket,一次完整的網絡通訊就結束了。
網絡通信結束後,就能夠對接受到的數據進行相應的處理了:
header, html = data.split('\r\n\r\n', 1) print header # 把接收的數據寫入文件: with open('sina.html', 'wb') as f: f.write(html)
將HTTP頭和網頁分離一下,把HTTP頭打印出來,網頁內容保存到文件,就獲得了新浪的首頁。
服務器:服務器端是提供服務的一方。首先,和客戶端同樣,創建一個socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
客戶端接下來是設置要鏈接的端口和ip,因此,此時,服務器端要綁定端口和ip:
# 監聽端口: s.bind(('127.0.0.1', 9999))
服務器肯能有多塊網卡,也就是多個ip。能夠只綁定到一塊網卡的ip地址上;也可使用0.0.0.0綁定到全部的網絡地址;還能夠用127.0.0.1綁定本機地址,此時,就有本機的客戶端才能鏈接。
接着,調用listen()方法開始監聽端口,傳入的參數指定最大的連接數量:
s.listen(5) print 'Waiting for connection...'
而後,服務器經過一個永久循環來接受客戶端的鏈接,accept()會等待並返回一個客戶端的鏈接:
while True: # 接受一個新鏈接: sock, addr = s.accept() # 建立新線程來處理TCP鏈接: t = threading.Thread(target=tcplink, args=(sock, addr)) t.start()
每一個鏈接必須建立一個新的線程(或進程)來處理,否則沒法同時應付多個客戶端。
而對於鏈接以後的處理是,服務器先發送一條歡迎消息,而後等待客戶端數據,並加上Hello再發送給客戶端。若是發送了exit字符串,就直接關閉鏈接。
相應的測試客戶端程序:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創建鏈接: s.connect(('127.0.0.1', 9999)) # 接收歡迎消息: print s.recv(1024) for data in ['Michael', 'Tracy', 'Sarah']: # 發送數據: s.send(data) print s.recv(1024) s.send('exit') s.close()
客戶端和服務端同時運行就能夠看到效果:
TCP是創建可靠的鏈接,UDP則是面向無鏈接的協議。使用UDP時,只須要知道對方的IP地址和端口號,不須要創建穩定鏈接,就能夠直接發數據包。可是,能不能到達就不知道了。
它相比於TCP的特色是,速度快,可是不夠穩定可靠,適用於不過重要的鏈接。
與TCP相似,UDP分爲客戶端和服務端。服務器首先須要創建socket,而後綁定端口:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 綁定端口: s.bind(('127.0.0.1', 9999))
其中,SOCK_DGRAM指定這個socket的類型是UDP。綁定端口與TCP同樣,可是不須要調用listen()方法,而是直接接收來自任何客戶端的數據:
print 'Bind UDP on 9999...' while True: # 接收數據: data, addr = s.recvfrom(1024) print 'Received from %s:%s.' % addr s.sendto('Hello, %s!' % data, addr)
recvfrom()方法返回數據和客戶端的地址與端口。這樣,服務器接收到數據後,就能夠直接調用sendto()就能夠把數據用UDP發給客戶端。
而客戶端上,首先也是創建socket鏈接,可是不用創建穩定的鏈接,因此不要調用connet(),直接經過sendto()將數據傳給服務器。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for data in ['Michael', 'Tracy', 'Sarah']: # 發送數據: s.sendto(data, ('127.0.0.1', 9999)) # 接收數據: print s.recv(1024) s.close()
從服務器接收數據還是調用recv()方法。
效果如圖:
總的來講,UDP不須要創建穩定的鏈接,因此數據可能不能安全到達,可是速度快。另外,服務器中綁定的UDP端口與TCP端口互不衝突。
注:本文爲學習廖雪峯Python入門整理後的筆記