python--8、socket網路編程

來源:互聯網
上載者:User

標籤:head   數位   rom   啟動服務   描述   ast   tool   協議棧   cli   

socket

socket可以完成C/S架構軟體的開發。
須知一個完整的電腦系統是由硬體、作業系統、應用軟體三者組成,具備了這三個條件,一台電腦就可以工作了。但是要跟別人一起玩,就要上互連網(互連網的本質就是一系列的網路通訊協定)。

互連網的核心就是由一堆協議組成,協議就是標準,比如全世界人通訊的標準是英語。所有的電腦都學會了互連網協議,那所有的電腦都就可以按照統一的標準去收發資訊從而完成通訊了。
互連網協議的功能:定義電腦如何接入internet,以及接入internet的電腦通訊的標準。

OSI七層模型:

  • 應用程式層
  • 展示層
  • 會話層
  • 傳輸層
  • 網路層
  • 資料連結層
  • 物理層

物理層功能:

主要是基於電器特性發送高低電壓(電訊號),高電壓對應數字1,低電壓對應數字0

資料連結層的功能:

定義了電訊號的分組方式

乙太網路協議:

早期的時候各個公司都有自己的分組方式,後來形成了統一的標準,即乙太網路協議ethernet

ethernet規定

一組電訊號構成一個資料包,叫做‘幀’ 每一資料幀分成:前序head和資料data兩部分 head? data

head包含:(固定18個位元組)

寄件者/源地址,6個位元組 接收者/目標地址,6個位元組 資料類型,6個位元組 data包含:(最短46位元組,最長1500位元組)

資料包的具體內容 head長度+data長度=最短64位元組,最長1518位元組,超過最大限制就分區發送

mac地址:

head中包含的源和目標地址由來:ethernet規定接入internet的裝置都必須具備網卡,發送端和接收端的地址便是指網卡的地址,即mac地址

mac地址:每塊網卡出廠時都被燒制上一個世界唯一的mac地址,長度為48位2進位,通常由12位16進位數表示(前六位是廠商編號,後六位是流水線號)

廣播:

有了mac地址,同一網路內的兩台主機就可以通訊了(一台主機通過arp協議擷取另外一台主機的mac地址)

ethernet採用最原始的方式,廣播的方式進行通訊,即電腦通訊基本靠吼 

網路層

世界範圍的互連網是由一個個彼此隔離的小的區域網路組成的(一個區域網路=一個廣播域,跨廣播域要通過路由轉寄)
網路層功能:引入一套新的地址用來區分不同的廣播域/子網,這套地址即網路地址

IP協議:

規定網路地址的協議叫ip協議,它定義的地址稱之為ip地址,廣泛採用的v4版本即ipv4,它規定網路地址由32位2進位表示
範圍0.0.0.0-255.255.255.255
一個ip地址通常寫成四段十進位數,例:172.16.10.1 ip地址分成兩部分

  • 網路部分:標識子網
  • 主機部分:標識主機
    注意:單純的ip位址區段只是標識了ip地址的種類,從網路部分或主機部分都無法辨識一個ip所處的子網

例:172.16.10.1與172.16.10.2並不能確定二者處於同一子網

子網路遮罩

所謂”子網路遮罩”,就是表示子網路特徵的一個參數。它在形式上等同於IP地址,也是一個32位位元字,它的網路部分全部為1,主機部分全部為0。比如,IP地址172.16.10.1,如果已知網路部分是前24位,主機部分是後8位,那麼子網路遮罩就是11111111.11111111.11111111.00000000,寫成十進位就是255.255.255.0。

知道”子網路遮罩”,我們就能判斷,任意兩個IP地址是否處在同一個子網路。方法是將兩個IP地址與子網路遮罩分別進行AND運算(兩個數位都為1,運算結果為1,否則為0),然後比較結果是否相同,如果是的話,就表明它們在同一個子網路中,否則就不是。
比如,已知IP地址172.16.10.1和172.16.10.2的子網路遮罩都是255.255.255.0,請問它們是否在同一個子網路?兩者與子網路遮罩分別進行AND運算,

172.16.10.1:10101100.00010000.00001010.000000001

255255.255.255.0:11111111.11111111.11111111.00000000

AND運算得網路地址結果:10101100.00010000.00001010.000000001->172.16.10.0

172.16.10.2:10101100.00010000.00001010.000000010

255255.255.255.0:11111111.11111111.11111111.00000000

AND運算得網路地址結果:10101100.00010000.00001010.000000001->172.16.10.0

結果都是172.16.10.0,因此它們在同一個子網路。

總結一下,IP協議的作用主要有兩個,一個是為每一台電腦分配IP地址,另一個是確定哪些地址在同一個子網路。

ip資料包

ip資料包也分為head和data部分,無須為ip包定義單獨的欄位,直接放入乙太網路包的data部分

head:長度為20到60位元組

data:最長為65,515位元組。

而乙太網路資料包的”資料”部分,最長只有1500位元組。因此,如果IP資料包超過了1500位元組,它就需要分割成幾個乙太網路資料包,分開發送了。

乙太網路頭 ? ip 頭 ? ip資料

ARP協議

arp協議由來:電腦通訊基本靠吼,即廣播的方式,所有上層的包到最後都要封裝上乙太網路頭,然後通過乙太網路協議發送,在談及乙太網路協議時候,我門瞭解到

通訊是基於mac的廣播方式實現,電腦在發包時,擷取自身的mac是容易的,如何擷取目標主機的mac,就需要通過arp協議

arp協議功能:廣播的方式發送資料包,擷取目標主機的mac地址

協議工作方式:每台主機ip都是已知的

例如:主機172.16.10.10/24訪問172.16.10.11/24

一:首先通過ip地址和子網路遮罩區分出自己所處的子網

情境? 資料包地址
同一子網\ 目標主機mac,目標主機ip
不同子網網關mac,目標主機ip

二:分析172.16.10.10/24與172.16.10.11/24處於同一網路(如果不是同一網路,那麼下表中目標ip為172.16.10.1,通過arp擷取的是網關的mac)

源mac目標mac源ip目標ip資料部分

發送端主機 發送端mac FF:FF:FF:FF:FF:FF 172.16.10.10/24 172.16.10.11/24 資料

三:這個包會以廣播的方式在發送端所處的自網內傳輸,所有主機接收後拆開包,發現目標ip為自己的,就響應,返回自己的mac

 

socket()模組函數用法

 1 import socket 2 socket.socket(socket_family,socket_type,protocal=0) 3 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,預設值為 0。 4  5 擷取tcp/ip通訊端 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7  8 擷取udp/ip通訊端 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)10 11 由於 socket 模組中有太多的屬性。我們在這裡破例使用了‘from module import *‘語句。使用 ‘from socket import *‘,我們就把 socket 模組裡的所有屬性都帶到我們的命名空間裡了,這樣能 大幅減短我們的代碼。12 例如tcpSock = socket(AF_INET, SOCK_STREAM)

 

服務端通訊端函數
s.bind() 綁定(主機,連接埠號碼)到通訊端
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的串連,(阻塞式)等待串連的到來

用戶端通訊端函數
s.connect() 主動初始化TCP伺服器串連
s.connect_ex() connect()函數的擴充版本,出錯時返回出錯碼,而不是拋出異常

公用用途的通訊端函數
s.recv() 接收TCP資料
s.send() 發送TCP資料(send在待發送資料量大於己端緩衝區剩餘空間時,資料丟失,不會發完)
s.sendall() 發送完整的TCP資料(本質就是迴圈調用send,sendall在待發送資料量大於己端緩衝區剩餘空間時,資料不丟失,迴圈調用send直到發完)
s.recvfrom() 接收UDP資料
s.sendto() 發送UDP資料
s.getpeername() 串連到當前通訊端的遠端的地址
s.getsockname() 當前通訊端的地址
s.getsockopt() 返回指定通訊端的參數
s.setsockopt() 設定指定通訊端的參數
s.close() 關閉通訊端

面向鎖的通訊端方法
s.setblocking() 設定通訊端的阻塞與非阻塞模式
s.settimeout() 設定阻塞通訊端操作的逾時時間
s.gettimeout() 得到阻塞通訊端操作的逾時時間

面向檔案的通訊端的函數
s.fileno() 通訊端的檔案描述符
s.makefile() 建立一個與該通訊端相關的檔案
服務端:

#建立一個串連 socket.socket(AF_INET,SOCK_STREAM)

 

基於TCP的socket

tcp是基於連結的,必須先啟動服務端,然後再啟動用戶端去連結服務端

tcp服務端

1 ss = socket() #建立伺服器通訊端2 ss.bind()      #把地址綁定到通訊端3 ss.listen()      #監聽連結4 inf_loop:      #伺服器無限迴圈5     cs = ss.accept() #接受用戶端連結6     comm_loop:         #通訊迴圈7         cs.recv()/cs.send() #對話(接收與發送)8     cs.close()    #關閉用戶端通訊端9 ss.close()        #關閉伺服器通訊端(可選)

tcp用戶端

1 cs = socket()    # 建立客戶通訊端2 cs.connect()    # 嘗試串連伺服器3 comm_loop:        # 通訊迴圈4     cs.send()/cs.recv()    # 對話(發送/接收)5 cs.close()            # 關閉客戶通訊端
  粘包

須知:只有TCP有粘包現象,UDP永遠不會粘包,為何,且聽我娓娓道來

首先需要掌握一個socket收發訊息的原理

 

發送端可以是一K一K地發送資料,而接收端的應用程式可以兩K兩K地提走資料,當然也有可能一次提走3K或6K資料,或者一次只提走幾個位元組的資料,也就是說,應用程式所看到的資料是一個整體,或說是一個流(stream),一條訊息有多少位元組對應用程式是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因。而UDP是面向訊息的協議,每個UDP段都是一條訊息,應用程式必須以訊息為單位提取資料,不能一次提取任意位元組的資料,這一點和TCP是很不同的。怎樣定義訊息呢?可以認為對方一次性write/send的資料為一個訊息,需要明白的是當對方send一條資訊的時候,無論底層怎樣分段分區,TCP協議層會把構成整條訊息的資料區段排序完成後才呈現在核心緩衝區。

例如基於tcp的通訊端用戶端往服務端上傳檔案,發送時檔案內容是按照一段一段的位元組流發送的,在接收方看了,根本不知道該檔案的位元組流從何處開始,在何處結束

所謂粘包問題主要還是因為接收方不知道訊息之間的界限,不知道一次性提取多少位元組的資料所造成的。

此外,發送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的資料後才發送一個TCP段。若連續幾次需要send的資料都很少,通常TCP會根據最佳化演算法把這些資料合成一個TCP段後一次發送出去,這樣接收方就收到了粘包資料。

  1. TCP(transport control protocol,傳輸控制通訊協定)是連線導向的,面向流的,提供高可靠性服務。收發兩端(用戶端和伺服器端)都要有一一成對的socket,因此,發送端為了將多個發往接收端的包,更有效發到對方,使用了最佳化方法(Nagle演算法),將多次間隔較小且資料量小的資料,合并成一個大的資料區塊,然後進行封包。這樣,接收端,就難於分辨出來了,必須提供科學的拆包機制。 即面向流的通訊是無訊息保護邊界的。
  2. UDP(user datagram protocol,使用者資料包通訊協定)是不需連線的,面向訊息的,提供高效率服務。不會使用塊的合并最佳化演算法,, 由於UDP支援的是一對多的模式,所以接收端的skbuff(通訊端緩衝區)採用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了訊息頭(訊息來源地址,連接埠等資訊),這樣,對於接收端來說,就容易進行區分處理了。 即面向訊息的通訊是有訊息保護邊界的。
  3. tcp是基於資料流的,於是收發的訊息不可為空,這就需要在用戶端和服務端都添加空訊息的處理機制,防止程式卡住,而udp是基於資料報的,即便是你輸入的是空內容(直接斷行符號),那也不是空訊息,udp協議會幫你封裝上訊息頭,實驗略

udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個位元組的資料就算完成,若是y>x資料就丟失,這意味著udp根本不會粘包,但是會丟資料,不可靠

tcp的協議資料不會丟,沒有收完包,下次接收,會繼續上次繼續接收,己端總是在收到ack時才會清除緩衝區內容。資料是可靠的,但是會粘包。

兩種情況下會發生粘包。

發送端需要等緩衝區滿才發送出去,造成粘包(發送資料時間間隔很短,資料了很小,會合到一起,產生粘包)

接收方不及時接收緩衝區的包,造成多個包接收(用戶端發送了一段資料,服務端只收了一小部分,服務端下次再收的時候還是從緩衝區拿上次遺留的資料,產生粘包) 

 

拆包的發生情況

當發送端緩衝區的長度大於網卡的MTU時,tcp會將這次發送的資料拆成幾個資料包發送出去。

補充問題一:為何tcp是可靠傳輸,udp是不可靠傳輸

基於tcp的資料轉送請參考我的另一篇文章http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcp在資料轉送時,發送端先把資料發送到自己的緩衝中,然後協議控制將緩衝中的資料發往對端,對端返回一個ack=1,發送端則清理緩衝中的資料,對端返回ack=0,則重新發送資料,所以tcp是可靠的

而udp發送資料,對端是不會返回確認資訊的,因此不可靠

補充問題二:send(位元組流)和recv(1024)及sendall

recv裡指定的1024意思是從緩衝裡一次拿出1024個位元組的資料

send的位元組流是先放入己端緩衝,然後由協議控制將緩衝內容發往對端,如果待發送的位元組流大小大於緩衝剩餘空間,那麼資料丟失,用sendall就會迴圈調用send,資料不會丟失

 

socket的connect和ACCEPT的實現細節。什麼是socket ?socket是在應用程式層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象為幾個簡單的介面供應用程式層調用,也就是說你不用再管tcp協議細節及send、recv實現細節。簡單的說,Socket就是利用伺服器和用戶端解決處理序間通訊串連的問題。對於socket connect的理解:友好的socket層對程式員屏蔽了很多協議的細節,比如三向交握,那麼TCP三向交握是哪個階段開始的? 下面的流程圖說的很清楚是用戶端端在調用connect()時建立的三向交握。 另外我們還需要注意兩個隊列,一個syn隊列,一個accept隊列。 socket ACCEPT的實現:使用者串連server之後會選擇跟server的哪個socket進行傳送資料,新用戶端又跟哪個socket建立串連?首先要明確,ACCEPT()是從ACCEPT隊列裡面取出用戶端的SYN請求,然後完成三向交握,並且socket server對於每個client都會建立一個新的socketfd,然後通過socketfd來和client完成資料轉送。對於TCP/IP協議棧是維護者一個接受和發送緩衝區的。在接受到來自用戶端的資料包後,伺服器端的TCP/IP協議棧會做如下處理:如果收到的是請求連結的資料包,則傳給監聽者串連請求連接埠的socketfd通訊端,如果是已經建立過連結的用戶端的資料包,則將資料放入接受緩衝區。這樣,當伺服器端需要讀取指定用戶端的資料時,則可以利用socketfd_new通訊端通過recv或者read函數到緩衝區裡去取指定的資料(因為socketfd_new代表的socket對象記錄了用戶端IP和連接埠,以此鑒別)。資料包如何找到相對應的socket,這個方法在Linux kernel代碼裡也是有體現的。static inline struct sock *__inet_lookup(struct net *net,struct inet_hashinfo *hashinfo,const __be32 saddr, const __be16 sport,const __be32 daddr, const __be16 dport,const int dif){u16 hnum = ntohs(dport);/* 先嘗試尋找處於串連成功的socket */struct sock *sk = __inet_lookup_established(net, hashinfo,saddr, sport, daddr, hnum, dif);/* 如果沒有找到串連成功的socket,那麼就去處於listen狀態的socket尋找 */return sk ? : __inet_lookup_listener(net, hashinfo, daddr, hnum, dif);} 總結整個過程:伺服器端在調用listen之後,核心會建立兩個隊列,SYN隊列和ACCEPT隊列,其中ACCEPT隊列的長度是由backlog指定的。伺服器端在調用ACCEPT之後,將阻塞,等待ACCEPT隊列的元素,用戶端在調用connect之後,將開始發起SYN請求,請求與伺服器建立串連,此時成為第一次握手。伺服器端在接受到SYN請求之後,把請求方放入SYN隊列中,並給用戶端回複一個確認幀ACK,此幀還會攜帶一個請求與用戶端簡曆串連的請求標誌,也就是SYN,這成為第二次握手。用戶端收到SYN+ACK幀後,connect返回,並發送確認建立串連幀ACK給伺服器端。這稱為第三向交握。伺服器端收到ACK幀後,把請求方從SYN隊列中移出,防止ACCEPT隊列,而ACCEPT()函數也等到了自己的資源,從阻塞中喚醒,從ACCEPT隊列中取出請求方,重新簡曆一個新的sockfd,並返回。這就是listen,ACCEPT,connect這三個函數的工作流程和原理。從這個過程中可以看到,在connect函數中發生了兩次握手。

python--8、socket網路編程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.