Day-17: 網絡編程

---恢復內容開始---html

  現有的互聯網通信方式,是服務器端的進程與客戶端進程的通訊。Python中進行網絡編程,就是在Python程序自己這個進程內,鏈接別的服務器進程的通訊端口進行通訊。編程

  互聯網協議上包含了上百種協議標準,可是,最重要的是兩個協議:TCP和IP協議。因此,互聯網協議簡稱TCP/IP協議。安全

  通訊時必須知道雙方的標識,而一臺計算機可能同時接入多個網絡,就會有兩個或多個IP地址。因此,IP地址實際上對應的是計算機的網絡接口,一般是指網卡。服務器

  IP協議將數據分割成一小塊一小塊,而後經過IP包發送出去。特色:按塊發送,不保證能到達,也不保證能順序到達。網絡

  TCP協議,創建在IP協議上,負責將兩臺計算機之間創建可靠鏈接,保證數據包按順序到達。app

特色:握手鍊接,對IP包編號,確保順序收到,若掉包,則自動重發。socket

  一個IP包,包含傳輸數據,源IP地址和目標IP地址,源端口和目標端口。tcp

  IP是標識在網絡中的電腦的位置,而一臺電腦上會有多個網絡通訊進程,因此端口則是標識目標進程在該電腦上的位置。學習

  • TCP編程

  首先要理解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()

  客戶端和服務端同時運行就能夠看到效果:

  • UDP編程

  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入門整理後的筆記

相關文章
相關標籤/搜索