網際網路協議包含了上百種協議標準,但是最重要的兩個協議是tcp和ip協議,所以,大家把網際網路的協議簡稱tcp/ip協議。
ip協議負責把資料從一台計算機通過網路傳送到另一台計算機。資料被分割成一小塊一小塊,然後通過ip包傳送出去。由於網際網路鏈路複雜,兩台計算機之間經常有多條線路,因此,路由器就負責決定如何把乙個ip包**出去。ip包的特點是按塊傳送,途徑多個路由,但不保證能到達,也不保證順序到達。
tcp協議則是建立在ip協議之上的。tcp協議負責在兩台計算機之間建立可靠連線,保證資料報按順序到達。tcp協議會通過握手建立連線,然後,對每個ip包編號,確保對方按順序收到,如果包丟掉了,就自動重發。
許多常用的更高階的協議都是建立在tcp協議基礎上的,比如用於瀏覽器的http協議、傳送郵件的smtp協議等。
乙個tcp報文除了包含要傳輸的資料外,還包含源ip位址和目標ip位址,源埠和目標埠。
埠有什麼作用?在兩台計算機通訊時,只發ip位址是不夠的,因為同一臺計算機上跑著多個網路程式。乙個tcp報文來了之後,到底是交給瀏覽器還是qq,就需要埠號來區分。每個網路程式都向作業系統申請唯一的埠號,這樣,兩個程序在兩台計算機之間建立網路連線就需要各自的ip位址和各自的埠號。ip位址決定了交給哪個電腦,而埠號是決定交給哪個程序。
tcp程式設計
大多數連線都是可靠的tcp連線。建立tcp連線時,主動發起連線的叫客戶端,被動響應連線的叫伺服器。在這裡插入**片
所以,我們要建立乙個基於tcp連線的socket,可以這樣做:
# 匯入socket庫:
import socket
# 建立乙個socket:
s = socket.socket(socket.af_inet, socket.sock_stream)
# 建立連線:
s.connect(('www.sina.com.cn', 80))
建立socket,af_inet指定適應ipv4協議,如果想要使用更為先進的ipv6,就指定為af_inet6。
sock_stream指定使用面向流的tcp協議,這樣,乙個socket物件就建立成功,但是還是沒有建立鏈結。
s.connect(('www.sina.com.cn', 80))
注意引數是乙個tuple,包含位址和埠號。
s.send(b'get / http/1.1\r\nhost: www.sina.com.cn\r\nconnection: close\r\n\r\n')
伺服器
伺服器程序首先要繫結乙個埠並監聽來自其他客戶端的連線。如果某個客戶端連線過來了,伺服器就與該客戶端建立socket連線,隨後的通訊就靠這個socket連線了。
所以,伺服器會開啟固定埠(比如80)監聽,每來乙個客戶端連線,就建立該socket連線。由於伺服器會有大量來自客戶端的連線,所以,伺服器要能夠區分乙個socket連線是和哪個客戶端繫結的。乙個socket依賴4項:伺服器位址、伺服器端口、客戶端位址、客戶端埠來唯一確定乙個socket。
但是伺服器還需要同時響應多個客戶端的請求,所以,每個連線都需要乙個新的程序或者新的執行緒來處理,否則,伺服器一次就只能服務乙個客戶端了。
我們來編寫乙個簡單的伺服器程式,它接收客戶端連線,把客戶端發過來的字串加上hello再發回去。
埠號需要預先指定。因為我們寫的這個服務不是標準服務,所以用9999這個埠號。請注意,小於1024的埠號必須要有管理員許可權才能繫結:
# 監聽埠:
s.bind(('127.0.0.1', 9999))
緊接著,呼叫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()
每個連線都必須建立新執行緒(或程序)來處理,否則,單執行緒在處理連線的過程中,無法接受其他客戶端的連線:
ef tcplink(sock, addr):
print('accept new connection from %s:%s...' % addr)
sock.send(b'welcome!')
while true:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
print('connection from %s:%s closed.' % addr)
要測試這個伺服器程式,我們還需要編寫乙個客戶端程式: linux 網路程式設計與 windows 網路程式設計
最近寫了乙個程式,涉及到在windows下執行的乙個程式到執行在linux下的伺服器取出資料。一開始還真沒有適應過來。下面說說我遇見的一些問題。1 windows下connect不成功。最開始也是不清楚 出錯,程式莫名其妙就在這個connect斷下,而且會花費很長的時間在connect這一句停留很長...
資料幀和網路編址
資料幀和網路編址 資料幀 1 ip報文頭部資訊用於指導網路裝置對報文進行路由和分片。2 同乙個網段 內的資料 通過鏈路層即可實現 單播,組播,廣播 而跨網段的資料 需要使用網路裝置的路由功能。3 分片是指資料報超過一定長度時,需要被劃分成不同 的片段使其能夠在網路中傳輸。4 ip報文頭部長度為20到...
TCP IP網路編學習筆記 三
可以設定tcp或udp通訊的輸入緩衝大小和輸出緩衝大小,但是最終大小並不一定是你所設定的大小 如果服務端先close,或者說先發出fin訊息表示分手,則服務端斷開連線後,在短時間內無法再次bind相同的埠,因為上一次繫結此埠的socket處於time wait過程中,還未被銷毀 不過此時客戶端是沒有...