Python-網絡編程之socket

軟件開發架構

開發軟件,必需要開發一套客戶端與服務端web

客戶端:使用服務編程

服務端:提供服務瀏覽器

C/S 架構

c即client:客戶端服務器

S即Server:服務端網絡

優勢:軟件使用穩定,節省網絡資源架構

缺點:一、下載客戶端 二、每次更新須要更新客戶端socket

C/S 架構的軟件:例如qqtcp

B/S 架構

B即Browser:瀏覽器(瀏覽器也是軟件)操作系統

S即Server:服務端

優勢:一、以瀏覽器充當客戶端,無需下載客戶端 二、無需更新

缺點:網絡資源消耗大

B/S 架構例如:在瀏覽器輸入 xxx域名

網絡編程

網絡編程的發展史

全部先進數都源自於軍事,但願經過遠程獲取數據,因此出現了網絡編程

要實現遠程通訊必須具有:

一、 物理鏈接介 --> 網卡

二、 互聯網協議 --> OSI七層協議

互聯網協議

互聯網協議又稱爲網絡七層協議,OSI七層協議,OSI是一個世界標準組織

OSI 七層協議:

  • 應用層
  • 表示層
  • 會話層
  • 傳輸層
  • 網絡層
  • 數據鏈路層
  • 物理鏈路層

物理鏈路層

基於電流信號發送二進制數據

數據鏈路層

以太網協議,專門處理基於電流信號發送的二進制的數據

以太網協議規定好電流信號數據的分組方式,每一臺鏈接網線的電腦都必須有一塊網卡,網卡由不一樣廠商生產,每塊網卡都有世界上惟一的12位的編號

交換機

數據鏈路層的一個應用,它能夠將多態電腦鏈接到一塊兒

基於以太網協議發送廣播、單播 數據,但有一點很差會產生廣播風暴,不能跨局域網通訊

互聯網

互聯網的出現 可讓局域網之間通訊

網絡層

基於IP工做

IP地址

用於標識惟一的一臺計算機的地址

傳輸層

TCP/UDP 協議,基於端口工做

端口號 標識了電腦上某一個軟件,端口號範圍:0-65535

咱們在開發中經常使用軟件的默認端口號:

MySql:3306

Mongodb:27017

Django:8000

Tomcat:8080

Flask:5000

Redis:6379

若 客戶端與服務端想要通訊,必需要創建鏈接,產生雙向通道

一條通道是客戶端往服務端發送消息

另外一條是服務端往客戶端發送消息

TCP工做原理

TCP三次握手

張三首先向李四招手(syn),李四看到張三向本身招手後,向對方點了點頭擠出了一個微笑(ack)。張三看到李四微笑後確認了李四成功辨認出了本身(進入estalished狀態)。

可是李四還有點狐疑,向四周看了一看,有沒有可能張三是在看別人呢,他也須要確認一下。

因此李四也向張三招了招手(syn),張三看到李四向本身招手後知道對方是在尋求本身的確認,因而也點了點頭擠出了微笑(ack),李四看到對方的微笑後確認了張三就是在向本身打招呼(進入established狀態)。

因而兩人加快步伐,走到了一塊兒,相互擁抱

咱們看到這個過程當中一共是四個動做,張三招手--李四點頭微笑--李四招手--張三點頭微笑。

其中李四連續進行了2個動做,先是點頭微笑(回覆對方),而後再次招手(尋求確認),實際上能夠將這兩個動做合一,招手的同時點頭和微笑(syn+ack)。

因而四個動做就簡化成了三個動做,張三招手--李四點頭微笑並招手--張三點頭微笑。這就是三次握手的本質,中間的一次動做是兩個動做的合併。

咱們看到有兩個中間狀態,syn_sent和syn_rcvd,這兩個狀態叫着「半打開」狀態,就是向對方招手了,可是還沒來得及看到對方的點頭微笑。

syn_sent是主動打開方的「半打開」狀態,syn_rcvd是被動打開方的「半打開」狀態。客戶端是主動打開方,服務器是被動打開方。

TCP 數據傳輸就是兩我的隔空對話,差了一點距離,因此須要對方反覆確認聽見了本身的話。

張三喊了一句話(data),李四聽見了以後要向張三回覆本身聽見了(ack)。

若是張三喊了一句,半天沒聽到李四回覆,張三就認爲本身的話被大風吹走了,李四沒聽見,因此須要從新喊話,這就是tcp重傳。

也有多是李四聽到了張三的話,可是李四向張三的回覆被大風吹走了,以致於張三沒聽見李四的回覆。

張三並不能判斷到底是本身的話被大風吹走了仍是李四的回覆被大風吹走了,張三也不用管,重傳一下就是。

既然會重傳,李四就有可能同一句話聽見了兩次,這就是「去重」。「重傳」和「去重」工做操做系統的網絡內核模塊都已經幫咱們處理好了,用戶層是不用關心的。

張三能夠向李四喊話,一樣李四也能夠向張三喊話,由於tcp連接是「雙工的」,雙方均可以主動發起數據傳輸。不過不管是哪方喊話,都須要收到對方的確認才能認爲對方收到了本身的喊話。

張三多是個高射炮,一說連說了八句話,這時候李四能夠不用一句一句回覆,而是連續聽了這八句話以後,一塊兒向對方回覆說前面你說的八句話我都聽見了,這就是批量ack。

可是張三也不能一次性說了太多話,李四的腦子短期可能沒法消化太多,兩人之間須要有協商好的合適的發送和接受速率,這個就是「TCP窗口大小」。

網絡環境的數據交互同人類之間的對話還要複雜一些,它存在數據包亂序的現象。

同一個來源發出來的不一樣數據包在「網際路由」上可能會走過不一樣的路徑,最終達到同一個地方時,順序就不同了。

操做系統的網絡內核模塊會負責對數據包進行排序,到用戶層時順序就已經徹底一致了。

TCP 四次揮手

TCP斷開連接的過程和創建連接的過程比較相似,只不過中間的兩部並不老是會合成一步走,因此它分紅了4個動做,張三揮手(fin)——李四傷感地微笑(ack)——李四揮手(fin)——張三傷感地微笑(ack)。

之因此中間的兩個動做沒有合併,是由於tcp存在「半關閉」狀態,也就是單向關閉。

張三已經揮了手,但是人尚未走,只是再也不說話,可是耳朵仍是能夠繼續聽,李四呢繼續喊話。等待李四累了,也再也不說話了,超張三揮了揮手,張三傷感地微笑了一下,才完全結束了。

上面有一個很是特殊的狀態time_wait,它是主動關閉的一方在回覆完對方的揮手後進入的一個長期狀態,這個狀態標準的持續時間是4分鐘,4分鐘後纔會進入到closed狀態,釋放套接字資源。不過在具體實現上這個時間是能夠調整的。

它就比如主動分手方要承擔的責任,是你提出的要分手,你得付出代價。這個後果就是持續4分鐘的time_wait狀態,不能釋放套接字資源(端口),就比如守寡期,這段時間內套接字資源(端口)不得回收利用。

它的做用是重傳最後一個ack報文,確保對方能夠收到。由於若是對方沒有收到ack的話,會重傳fin報文,處於time_wait狀態的套接字會當即向對方重發ack報文。

同時在這段時間內,該連接在對話期間於網際路由上產生的殘留報文(由於路徑過於崎嶇,數據報文走的時間太長,重傳的報文都收到了,原始報文還在路上)傳過來時,都會被當即丟棄掉。

4分鐘的時間足以使得這些殘留報文完全消逝。否則當新的端口被重複利用時,這些殘留報文可能會干擾新的連接。

4分鐘就是2個MSL,每一個MSL是2分鐘。MSL就是maximium segment lifetime——最長報文壽命。這個時間是由官方RFC協議規定的。至於爲何是2個MSL而不是1個MSL,我尚未看到一個很是滿意的解釋。

四次揮手也並不老是四次揮手,中間的兩個動做有時候是能夠合併一塊兒進行的,這個時候就成了三次揮手,主動關閉方就會從fin_wait_1狀態直接進入到time_wait狀態,跳過了fin_wait_2狀態。

應用層

應用層包含了表示層和會話層,常見的有http、ftp

Socket

什麼是Socket

Socket是一個模塊,能夠寫一套C/S 架構的套接字

爲何要使用Socket

Socket套接字 封裝好了各層協議的工做,能夠節省開發成本

如何使用

那麼咱們先寫服務端,啓動也是先啓動服務端

# 服務端

# coding=utf-8

import socket
s = socket.socket()     # 導入socket模塊產生一個s對象
s.bind(
    ("127.0.0.1",8888)      # 設置ip+port,綁定套接字
)
s.listen(5)                 # 開始監聽(5) 表示能夠服務 5+1=6 個客戶端,一個正在服務,另外5個在等候服務
while True:
    conn, addr = s.accept() # 接收客戶端鏈接
    while True:
        try:    
            data = conn.recv(1024).decode("utf-8")      # 接收客戶端信息
            print(data)                 # 打印客戶端信息

            if len(data) == 0:          # 若是發送爲0,跳過
                continue

            if data == "q":             # 若是客戶端發送q退出,那麼退出服務
                break
            conn.send(data.encode("utf-8")) # 服務端發行消息
        except Exception as e:
            break
    conn.close()
s.close()        #關閉服務器套接字(可選)
# 客戶端

# coding=utf-8
import socket       # 導入socket模塊

c = socket.socket()     
c.connect(
    ("127.0.0.1",8888)      # 鏈接服務端
)

while True:
    send_msg = input("client:>>>")
    c.send(send_msg.encode("utf-8"))        # 發送消息

    if send_msg == "q":
        break
    data = c.recv(1024).decode("utf-8")     # 接收消息
    print(data)


c.close()
相關文章
相關標籤/搜索