1.什麼是socket?
socket的意願是“插座”,在計算機通訊領域,socket被翻譯為“套接字”,他是計算機之間進行通訊的一種約定或者一種方式,透過socket這種約定,一臺計算機可以接受其他計算機的資料,也可以向其他計算機傳送資料。
我們把插頭插到插座上就能從電網獲得電力供應,同樣,為了遠端計算機進行資料傳輸,需要連線到因特網,而socket就是用來連線到因特網的工具。
2.Unix/Linux中的socket是什麼?
在Unix/Linux系統中,為了統一對各種硬體的操作,簡化介面,不同的硬體裝置也都被看成一個檔案,對這些檔案的操作,等同於對磁碟上普通檔案的操作。
UNIX/Linux中的一切都是檔案
檔案描述符(file descriptor)
- 通常用 0 來表示標準輸入檔案(stdin),他對應的硬體裝置就是鍵盤。
- 通常用 1 來表示標準輸出檔案(stdout),他對應的硬體裝置就是顯示器。
Unix/Linux程式在執行任何形式的I/O操作時,都是在讀取或者寫入一個檔案描述符,一個檔案描述符只是一個和開啟的檔案相關聯的整數,他的背後可能是一個硬碟上的普通檔案、FIFO、管道、終端、鍵盤、顯示器,甚至是一個網路連線。
我們可以透過socket()來建立一個網路連線,或者說開啟一個網路檔案,socket()的返回值就是檔案描述符,有了檔案描述符,我們就可以使用普通的檔案操作函式來傳輸資料了。
- 用recv()讀取從遠端計算機傳來的資料;
- 用write()向遠端計算機寫入資料;
只要使用socket()建立了連線,剩下的就是檔案操作了;網路程式設計就是如此簡單!
3.socket有哪些型別?
根據資料的傳輸方式,可以將Internet套接字分成兩種型別,透過socket()建立連線時,必須告訴它使用哪種資料傳輸方式。
3.1 流格式套接字(SOCK_STREAM)
流格式套接字(stream sockets)也叫“面向連線的套接字”,在程式碼中使用SOCK_STREAM表示。
SOCK_STREAM是一種可靠的、雙向的通訊資料流,資料可以準確無誤地到達另一臺計算機,如果損壞或者丟失,可以重新傳送。
流格式套接字有自己的糾錯機制
SOCK_STREAM的特徵
- 資料在傳輸過程中不會消失;
- 資料是按照傳輸順傳輸的;
- 資料的傳送和接收不是同步的(不存在資料邊界)
可以將SOCK_STREAM比喻成一條傳送帶,只要傳送帶本身沒有問題(不會斷網),就能保證資料不丟失;同時。較晚傳送的資料不會優先到達,較早傳送的資料不會晚到達,這就保證了資料是按照順序傳遞的。
為什麼流格式套接字可以達到高質量的資料傳輸呢?這是因為它使用了 TCP 協議(The Transmission Control Protocol,傳輸控制協議),TCP 協議會控制你的資料按照順序到達並且沒有錯誤。
你也許見過 TCP,是因為你經常聽說“TCP/IP”。TCP 用來確保資料的正確性,IP(Internet Protocol,網路協議)用來控制資料如何從源頭到達目的地,也就是常說的“路由”。
假設傳送帶傳送的是水果,接收者需要湊齊 100 個後才能裝袋,但是傳送帶可能把這 100 個水果分批傳送,比如第一批傳送 20 個,第二批傳送 50 個,第三批傳送 30 個。接收者不需要和傳送帶保持同步,只要根據自己的節奏來裝袋即可,不用管傳送帶傳送了幾批,也不用每到一批就裝袋一次,可以等到湊夠了 100 個水果再裝袋。
也就是說,不管資料分幾次傳送過來,接收端只需要根據自己的要求讀取,不用非得在資料到達時立即讀取。傳送端有自己的節奏,接收端也有自己的節奏,它們是不一致的。
3.2、資料包格式套接字(SOCK_DGRAM)
資料包格式套接字(Datagram Sockets)也叫“無連線的套接字”,在程式碼中使用 SOCK_DGRAM 表示。
計算機只管傳輸資料,不作資料校驗,如果資料在傳輸中損壞,或者沒有到達另一臺計算機,是沒有辦法補救的。也就是說,資料錯了就錯了,無法重傳。
可以將 SOCK_DGRAM 比喻成高速移動的摩托車快遞,它有以下特徵:
另外,用兩輛摩托車分別傳送兩件包裹,那麼接收者也需要分兩次接收,所以“資料的傳送和接收是同步的”;換句話說,接收次數應該和傳送次數相同。
資料包套接字也使用 IP 協議作路由,但是它不使用 TCP 協議,而是使用 UDP 協議(User Datagram Protocol,使用者資料包協議)。
注意:SOCK_DGRAM 沒有想象中的糟糕,不會頻繁的丟失資料,資料錯誤只是小機率事件。
實現服務端和客戶端交流的程式
server端
import socket
# 1.構建套接字物件
sock = socket.socket()
# 2.繫結:度無端的ip和埠
sock.bind(("127.0.0.1",8899))
# 3.確定最大監聽數
sock.listen(3)
# 4.等待連線
while 1:
print("等待伺服器連線。。。")
#獲取客戶端的套接字物件和地址
conn,addr = sock.accept()
print(conn,addr)
#conn:傳送訊息 send 方法 接收資料 recv方法
while 1:
msg = conn.recv(1024)
print(msg.decode())
if msg.decode() == "quit":
break
res = input("響應>>>")
conn.send(res.encode())
client端
import socket
# 建立客戶端的套接字物件
sock = socket.socket()
# 連線伺服器
sock.connect(("127.0.0.1",8899))
while 1:
data = input(">>>")
sock.send(data.encode())
if data == "quit":
break
res = sock.recv(1024)
print("伺服器響應",res.decode())
互動
服務端
客戶端
超出監聽數
監聽數:只包括等待連線的客戶端,連線成功的客戶端不算數