1、引入:
網絡編程 是如何在程序中實現兩台計算機的通信
eg:當你使用瀏覽器訪問新浪時,你的計算機就和新浪的某台服務器通過互聯網連接起來了,然后新浪的服務器把網頁內容作為數據通過互聯網傳輸到你的電腦上。。
電腦不只瀏覽器,還有QQ、郵件客戶端等其他的,,不同的程序連接的別的計算機也會不同;;確切的說,網絡通信 是兩台計算機的兩個進程之間的通信
eg:瀏覽器進程和新浪服務器上的某個Web服務進程在通信。。。而QQ進程呢是和騰訊的某個服務器上的某個進程在通信
用python進行網絡編程,就是在python程序本身這個進程內,連接別的服務器進程的通信端口進行通信
2、TCP/IP簡介
①為了把全世界的所有不同類型的計算機都連接起來,就必須規定一套全球通用的協議;只要支持這個協議,任何私有網絡就可以聯入互聯網
②通信的時候,雙方必須知道對方的標識,好比發郵件必須知道對方的郵件地址。互聯網上每個計算機的唯一標識就是IP地址,類似123.123.123.123。如果一台計算機同時接入到兩個或更多的網絡,比如路由器,它就會有兩個或多個IP地址,所以,IP地址對應的實際上是計算機的網絡接口,通常是網卡。
③IP協議負責把數據從一台計算機通過網絡發送到另一台計算機。數據被分割成一小塊一小塊,然后通過IP包發送出去。由於互聯網鏈路復雜,兩台計算機之間經常有多條線路,因此,路由器就負責決定如何把一個IP包轉發出去。IP包的特點是按塊發送,途徑多個路由,但不保證能到達,也不保證順序到達。
④TCP協議則是建立在IP協議之上的。TCP協議負責在兩台計算機之間建立可靠連接,保證數據包按順序到達。TCP協議會通過握手建立連接,然后,對每個IP包編號,確保對方按順序收到,如果包丟掉了,就自動重發
⑤一個IP包除了包含要傳輸的數據外,還包含源IP地址和目標IP地址,源端口和目標端口。
⑥端口有什么作用?在兩台計算機通信時,只發IP地址是不夠的,因為同一台計算機上跑着多個網絡程序。一個IP包來了之后,到底是交給瀏覽器還是QQ,就需 要端口號來區分。每個網絡程序都向操作系統申請唯一的端口號,這樣,兩個進程在兩台計算機之間建立網絡連接就需要各自的IP地址和各自的端口號。
3、客戶端
# !/user/bin/env python
# -*- coding: utf-8 -*-
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET指定使用IPv4協議;SOCK_STREAM指定使用面向流的TCP協議
s.connect(('www.sina.com.cn',80)) # 參數是一個tuple,包含地址和端口號
s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n') # 向新浪服務器發送請求,要求返回首頁的內容
# 接收數據:
buffer = []
while True:
# 每次最多接收1k字節:
d = s.recv(1024) # recv(max)方法,一次最多接收指定的字節數
if d:
buffer.append(d)
else:
break
data = ''.join(buffer)
# 關閉連接:
s.close()
# 接收到的數據包括HTTP頭和網頁本身,我們只需要把HTTP頭和網頁分離一下,把HTTP頭打印出來,網頁內容保存到文件
header, html = data.split('\r\n\r\n', 1)
print header
# 把接收的數據寫入文件:
with open('sina.html', 'wb') as f:
f.write(html)
4、服務器
服務器進程首先要綁定一個端口並監聽來自其他客戶端的連接。如果某個客戶端連接過來了,服務器就與該客戶端建立Socket連接,隨后的通信就靠這個Socket連接了。
服務器還需要同時響應多個客戶端的請求,所以,每個連接都需要一個新的進程或者新的線程來處理,否則,服務器一次就只能服務一個客戶端了
Eg:編寫一個簡單的服務器程序,它接收客戶端連接,把客戶端發過來的字符串加上Hello
再發回去
服務器端:
# !/user/bin/env python
# -*- coding: utf-8 -*-
import socket,time,threading
def tcplink(sock,addr):
print 'Accept new connect from %s:%s...' % addr
sock.send('Welcome!') # 發送給客戶端
# 連接建立后,服務器首先發一條歡迎消息,然后等待客戶端數據,並加上Hello再發送給客戶端。如果客戶端發送了exit字符串,就直接關閉連接
while True:
data = sock.recv(1024) # 每次最多接收1024k字節
time.sleep(1)
if data == 'exit' or not data:
break
sock.send('hello,%s!' % data)
sock.close()
print 'Connect from %s:%s closed' % addr
if __name__ == '__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999)) # 監聽端口,綁定到本機地址
s.listen(5) # listen()方法開始監聽端口,參數指定等待連接的最大數量
print 'Waiting for connection,,,'
# 服務器程序通過一個永久循環來接收來自客戶端的連接,accept會等待並返回一個客戶端的連接;;每個連接都必須創建新線程
while True:
sock,addr = s.accept()
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
客戶端:
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協議進行Socket編程在Python中十分簡單,對於客戶端,要主動連接服務器的IP和指定端口,對於服務器,要首先監聽指定端口,然后,對每一個新的連接,創建一個線程或進程來處理。通常,服務器程序會無限運行下去。
同一個端口,被一個Socket綁定了以后,就不能被別的Socket綁定了。一個Socket依賴4項:服務器地址、服務器端口、客戶端地址、客戶端端口來唯一確定一個Socket