天天看點

python的網絡程式設計

一、系統和網絡

1、系統

作業系統: (Operating System,簡稱OS)是管理和控制計算機硬體與軟體資源的計算機程式,是直接運作在“裸機”上的最基本的系統軟體,任何其他軟體都必須在作業系統的支援下才能運作。

2、osi七層協定

osi七層:

    實體層

    資料鍊路層

    網絡層

    傳輸層

    會話層

    表示層

    應用層

tcp/ip五層:

tcp/ip四層:

    網絡接口層

3、資料鍊路層

以太網協定:

       # 一組電信号構成一個資料包,叫做‘幀’

       # 每一資料幀分成:報頭head和資料data兩部分

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

        發送者/源位址,6個位元組

        接收者/目标位址,6個位元組

        資料類型,6個位元組

data包含:(最短46位元組,最長1500位元組)

資料包的具體内容:

head長度+data長度=最短64位元組,最長1518位元組,超過最大限制就分片發送

4、網絡層

ip資料包也分為head和data部分

  head:長度為20到60位元組

  data:最長為65,515位元組

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

5、傳輸層

二、socket

1、介紹

我們經常把socket翻譯為套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象為幾個簡單的接口供應用層調用已實作程序在網絡中通信。

2、 套接字工作流程

伺服器端先初始化Socket,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待用戶端連接配接。在這時如果有個用戶端初始化一個Socket,然後連接配接伺服器(connect),如果連接配接成功,這時用戶端與伺服器端的連接配接就建立了。用戶端發送資料請求,伺服器端接收請求并處理請求,然後把回應資料發送給用戶端,用戶端讀取資料,最後關閉連接配接,一次互動結束

3、套接字函數

#1、服務端套接字函數

s.bind()    綁定(主機,端口号)到套接字

s.listen()  開始TCP監聽

s.accept()  被動接受TCP客戶的連接配接,(阻塞式)等待連接配接的到來

#2、用戶端套接字函數

s.connect()     主動初始化TCP伺服器連接配接

s.connect_ex()  connect()函數的擴充版本,出錯時傳回出錯碼,而不是抛出異常

#3、公共用途的套接字函數

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()           關閉套接字

#4、面向鎖的套接字方法

s.setblocking()     設定套接字的阻塞與非阻塞模式

s.settimeout()      設定阻塞套接字操作的逾時時間

s.gettimeout()      得到阻塞套接字操作的逾時時間

#5、面向檔案的套接字的函數

s.fileno()          套接字的檔案描述符

s.makefile()        建立一個與該套接字相關的檔案

4、實作基于TCP的套接字(先啟動服務端)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<code>#服務端:</code>

<code>import</code> <code>socket</code>

<code>phone</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM) </code><code>#tcp協定</code>

<code>phone.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))      </code><code>#綁定ip和端口,讓用戶端連接配接</code>

<code>phone.listen(</code><code>5</code><code>)                </code><code>#半連接配接池大小</code>

<code>print</code><code>(</code><code>'starting...'</code><code>)</code>

<code>conn,client_addr</code><code>=</code><code>phone.accept()     </code><code>#等待用戶端連接配接</code>

<code>print</code><code>(conn,client_addr)</code>

<code>data</code><code>=</code><code>conn.recv(</code><code>1024</code><code>)             </code><code>#基于建立好的conn連結對象收發消息</code>

<code>conn.send(data.upper())</code>

<code>conn.close()                   </code><code>#斷開連結</code>

<code>phone.close()                   </code><code>#終止服務</code>

<code>#用戶端:</code>

<code>phone.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))        </code><code>#連接配接伺服器的ip和端口</code>

<code>phone.send(</code><code>'hello'</code><code>.encode(</code><code>'utf-8'</code><code>))</code>

<code>data</code><code>=</code><code>phone.recv(</code><code>1024</code><code>)</code>

<code>print</code><code>(data)</code>

<code>phone.close()</code>

5、最終版基于TCP的套接字

上面的值實作發送一條消息和連接配接一個用戶端,是以要對程式進行修改

21

22

23

24

<code>phone.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))                 </code><code>#綁定ip和端口,讓用戶端連接配接</code>

<code>phone.listen(</code><code>5</code><code>)                          </code><code>#半連接配接池大小</code>

<code>while</code> <code>True</code><code>:</code>

<code>    </code><code>conn,client_addr</code><code>=</code><code>phone.accept()           </code><code>#等待用戶端連接配接</code>

<code>    </code><code>print</code><code>(conn,client_addr)</code>

<code>    </code><code>while</code> <code>True</code><code>:</code>

<code>        </code><code>data</code><code>=</code><code>conn.recv(</code><code>1024</code><code>)              </code><code>#基于建立好的conn連結對象收發消息</code>

<code>        </code><code>conn.send(data.upper()) </code>

<code>    </code><code>conn.close()                       </code><code>#斷開連結</code>

<code>phone.close()                           </code><code>#終止服務</code>

<code>    </code><code>msg</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip()</code>

<code>    </code><code>if</code> <code>len</code><code>(msg) </code><code>=</code><code>=</code> <code>0</code><code>:</code><code>continue</code>

<code>    </code><code>phone.send(msg.encode(</code><code>'utf-8'</code><code>))</code>

<code>    </code><code>data</code><code>=</code><code>phone.recv(</code><code>1024</code><code>)</code>

<code>    </code><code>print</code><code>(data)</code>

6、粘包

應用程式所看到的資料是一個整體,或說是一個流(stream),一條消息有多少位元組對應用程式是不可見的,是以TCP協定是面向流的協定,這也是容易出現粘包問題的原因。

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

此外,發送方引起的粘包是由TCP協定本身造成的,TCP為提高傳輸效率,發送方往往要收集到足夠多的資料後才發送一個TCP段。

若連續幾次需要send的資料都很少,通常TCP會根據優化算法把這些資料合成一個TCP段後一次發送出去,這樣接收方就收到了粘包資料。

7、解決粘包的處理方法

程式流程:用戶端發送指令,服務端在本地執行後,傳回得到的結果給用戶端

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

<code># 服務端:</code>

<code>from</code> <code>socket </code><code>import</code> <code>*</code>

<code>import</code> <code>subprocess</code>

<code>import</code> <code>struct</code>

<code>server</code><code>=</code><code>socket(AF_INET,SOCK_STREAM)</code>

<code>server.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8088</code><code>))</code>

<code>server.listen(</code><code>5</code><code>)</code>

<code>    </code><code>conn,client_addr</code><code>=</code><code>server.accept()</code>

<code>    </code><code>print</code><code>(client_addr)</code>

<code>        </code><code>try</code><code>:</code>

<code>            </code><code>cmd</code><code>=</code><code>conn.recv(</code><code>8096</code><code>)</code>

<code>            </code><code>if</code> <code>not</code> <code>cmd:</code><code>break</code>

<code>            </code><code>obj</code><code>=</code><code>subprocess.Popen(cmd.decode(</code><code>'utf-8'</code><code>),shell</code><code>=</code><code>True</code><code>,</code>

<code>                             </code><code>stdout</code><code>=</code><code>subprocess.PIPE,</code>

<code>                             </code><code>stderr</code><code>=</code><code>subprocess.PIPE</code>

<code>                             </code><code>)</code>

<code>            </code><code>stdout</code><code>=</code><code>obj.stdout.read()</code>

<code>            </code><code>stderr</code><code>=</code><code>obj.stderr.read()</code>

<code>            </code><code>total_size </code><code>=</code> <code>len</code><code>(stdout) </code><code>+</code> <code>len</code><code>(stderr)     </code><code>#制作固定長度的報頭</code>

<code>            </code><code>headers</code><code>=</code><code>struct.pack(</code><code>'i'</code><code>,total_size)</code>

<code>            </code><code>conn.send(headers)</code>

<code>            </code><code>conn.send(stdout)                        </code><code>#發送指令的執行結果</code>

<code>            </code><code>conn.send(stderr)</code>

<code>        </code><code>except</code> <code>ConnectionResetError:</code>

<code>            </code><code>break</code>

<code>    </code><code>conn.close()</code>

<code>server.close()</code>

<code># 用戶端:</code>

<code>client</code><code>=</code><code>socket(AF_INET,SOCK_STREAM)</code>

<code>client.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8088</code><code>))</code>

<code>    </code><code>cmd</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip()</code>

<code>    </code><code>if</code> <code>not</code> <code>cmd:</code><code>continue</code>

<code>    </code><code>client.send(cmd.encode(</code><code>'utf-8'</code><code>))   </code><code>#發送指令</code>

<code>    </code><code>headers</code><code>=</code><code>client.recv(</code><code>4</code><code>)       </code><code>#先接收指令長度,struct子產品生成一個4個位元組的結果</code>

<code>    </code><code>total_size </code><code>=</code> <code>struct.unpack(</code><code>'i'</code><code>, headers)[</code><code>0</code><code>]</code>

<code>    </code><code>recv_size</code><code>=</code><code>0</code>                <code>#再收指令的結果</code>

<code>    </code><code>data</code><code>=</code><code>b''</code>

<code>    </code><code>while</code> <code>recv_size &lt; total_size:</code>

<code>        </code><code>recv_data</code><code>=</code><code>client.recv(</code><code>1024</code><code>)</code>

<code>        </code><code>data</code><code>+</code><code>=</code><code>recv_data</code>

<code>        </code><code>recv_size</code><code>+</code><code>=</code><code>len</code><code>(recv_data)</code>

<code>    </code><code>print</code><code>(data.decode(</code><code>'gbk'</code><code>))</code>

<code>client.close()</code>

8、解決粘包的處理方法加強版

49

50

51

52

53

54

55

56

57

58

59

60

<code>import</code> <code>json</code>

<code>server.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8093</code><code>))</code>

<code>            </code><code>headers </code><code>=</code> <code>{                                        </code><code>#制作報頭</code>

<code>                </code><code>'filepath'</code><code>: </code><code>'a.txt'</code><code>,</code>

<code>                </code><code>'md5'</code><code>: </code><code>'123sxd123x123'</code><code>,</code>

<code>                </code><code>'total_size'</code><code>: </code><code>len</code><code>(stdout) </code><code>+</code> <code>len</code><code>(stderr)</code>

<code>            </code><code>}</code>

<code>            </code><code>headers_json </code><code>=</code> <code>json.dumps(headers)                </code><code>#把headers轉為json格式</code>

<code>            </code><code>headers_bytes </code><code>=</code> <code>headers_json.encode(</code><code>'utf-8'</code><code>)     </code><code>#前面的json結果得到位元組形式</code>

<code>            </code><code>conn.send(struct.pack(</code><code>'i'</code><code>,</code><code>len</code><code>(headers_bytes)))    </code><code>#先發報頭的長度</code>

<code>            </code><code>conn.send(headers_bytes)                          </code><code>#發送報頭</code>

<code>            </code><code>conn.send(stdout)                                 </code><code>#發送真實資料,正确的stdout,錯誤的stderr</code>

<code>client.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8093</code><code>))</code>

<code>    </code><code>client.send(cmd.encode(</code><code>'utf-8'</code><code>))</code>

<code>    </code><code>headers_size</code><code>=</code><code>struct.unpack(</code><code>'i'</code><code>,client.recv(</code><code>4</code><code>))[</code><code>0</code><code>]</code>

<code>    </code><code>headers_bytes</code><code>=</code><code>client.recv(headers_size)</code>

<code>    </code><code>headers_json</code><code>=</code><code>headers_bytes.decode(</code><code>'utf-8'</code><code>)</code>

<code>    </code><code>headers_dic</code><code>=</code><code>json.loads(headers_json)</code>

<code>    </code><code>print</code><code>(</code><code>'========&gt;'</code><code>,headers_dic)</code>

<code>    </code><code>total_size</code><code>=</code><code>headers_dic[</code><code>'total_size'</code><code>]</code>

<code>    </code><code>recv_size</code><code>=</code><code>0</code>

9、檔案下載下傳

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

<code>#服務端</code>

<code>import</code> <code>os</code>

<code>SHARE_DIR</code><code>=</code><code>r</code><code>'F:\SHARE'</code>          <code>#目标檔案路徑</code>

<code>class</code> <code>FtpServer:</code>

<code>    </code><code>def</code> <code>__init__(</code><code>self</code><code>,host,port):</code>

<code>        </code><code>self</code><code>.host</code><code>=</code><code>host</code>

<code>        </code><code>self</code><code>.port</code><code>=</code><code>port</code>

<code>        </code><code>self</code><code>.server</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM)</code>

<code>        </code><code>self</code><code>.server.bind((</code><code>self</code><code>.host,</code><code>self</code><code>.port))</code>

<code>        </code><code>self</code><code>.server.listen(</code><code>5</code><code>)</code>

<code>    </code><code>def</code> <code>serve_forever(</code><code>self</code><code>):</code>

<code>        </code><code>print</code><code>(</code><code>'server starting...'</code><code>)</code>

<code>        </code><code>while</code> <code>True</code><code>:</code>

<code>            </code><code>self</code><code>.conn,</code><code>self</code><code>.client_addr</code><code>=</code><code>self</code><code>.server.accept()</code>

<code>            </code><code>print</code><code>(</code><code>self</code><code>.client_addr)</code>

<code>            </code><code>while</code> <code>True</code><code>:</code>

<code>                </code><code>try</code><code>:</code>

<code>                    </code><code>data</code><code>=</code><code>self</code><code>.conn.recv(</code><code>1024</code><code>)  </code><code>#params_json.encode('utf-8')</code>

<code>                    </code><code>if</code> <code>not</code> <code>data:</code><code>break</code>

<code>                    </code><code>params</code><code>=</code><code>json.loads(data.decode(</code><code>'utf-8'</code><code>)) </code><code>#params=['get','a.txt']</code>

<code>                    </code><code>cmd</code><code>=</code><code>params[</code><code>0</code><code>]</code>

<code>                    </code><code>if</code> <code>hasattr</code><code>(</code><code>self</code><code>,cmd):</code>

<code>                        </code><code>func</code><code>=</code><code>getattr</code><code>(</code><code>self</code><code>,cmd)</code>

<code>                        </code><code>func(params)</code>

<code>                    </code><code>else</code><code>:</code>

<code>                        </code><code>print</code><code>(</code><code>'\033[45mcmd not exists\033[0m'</code><code>)</code>

<code>                </code><code>except</code> <code>ConnectionResetError:</code>

<code>                    </code><code>break</code>

<code>            </code><code>self</code><code>.conn.close()</code>

<code>        </code><code>self</code><code>.server.close()</code>

<code>    </code><code>def</code> <code>get(</code><code>self</code><code>,params): </code><code>#params=['get','a.txt']</code>

<code>        </code><code>filename</code><code>=</code><code>params[</code><code>1</code><code>] </code><code>#filename='a.txt'</code>

<code>        </code><code>filepath</code><code>=</code><code>os.path.join(SHARE_DIR,filename)</code>

<code>        </code><code>if</code> <code>os.path.exists(filepath):</code>

<code>            </code><code>headers </code><code>=</code> <code>{                                           </code><code>#制作報頭</code>

<code>                </code><code>'filename'</code><code>: filename,</code>

<code>                </code><code>'filesize'</code><code>: os.path.getsize(filepath)</code>

<code>            </code><code>headers_json </code><code>=</code> <code>json.dumps(headers)</code>

<code>            </code><code>headers_bytes </code><code>=</code> <code>headers_json.encode(</code><code>'utf-8'</code><code>)</code>

<code>            </code><code>self</code><code>.conn.send(struct.pack(</code><code>'i'</code><code>,</code><code>len</code><code>(headers_bytes)))  </code><code>#先發報頭的長度</code>

<code>            </code><code>self</code><code>.conn.send(headers_bytes)                        </code><code>#發送報頭</code>

<code>            </code><code>with </code><code>open</code><code>(filepath,</code><code>'rb'</code><code>) as f:                     </code><code>#發送真實的資料</code>

<code>                </code><code>for</code> <code>line </code><code>in</code> <code>f:</code>

<code>                    </code><code>self</code><code>.conn.send(line)</code>

<code>    </code><code>def</code> <code>put(</code><code>self</code><code>):</code>

<code>        </code><code>pass</code>

<code>if</code> <code>__name__ </code><code>=</code><code>=</code> <code>'__main__'</code><code>:</code>

<code>    </code><code>server</code><code>=</code><code>FtpServer(</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>)</code>

<code>    </code><code>server.serve_forever()</code>

<code>##用戶端</code>

<code>DOWNLOAD_DIR</code><code>=</code><code>r</code><code>'F:\DOWNLOAD'</code>    <code>#下載下傳路徑</code>

<code>class</code> <code>FtpClient:</code>

<code>        </code><code>self</code><code>.client</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM)</code>

<code>        </code><code>self</code><code>.client.connect((</code><code>self</code><code>.host,</code><code>self</code><code>.port))</code>

<code>    </code><code>def</code> <code>interactive(</code><code>self</code><code>):</code>

<code>            </code><code>data</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip() </code><code>#get a.txt</code>

<code>            </code><code>if</code> <code>not</code> <code>data:</code><code>continue</code>

<code>            </code><code>params</code><code>=</code><code>data.split() </code><code>#parmas=['get','a.txt']</code>

<code>            </code><code>cmd</code><code>=</code><code>params[</code><code>0</code><code>] </code><code>#cmd='get'</code>

<code>            </code><code>if</code> <code>hasattr</code><code>(</code><code>self</code><code>,cmd):</code>

<code>                </code><code>func</code><code>=</code><code>getattr</code><code>(</code><code>self</code><code>,cmd)</code>

<code>                </code><code>func(params) </code><code>#func(['get','a.txt'])</code>

<code>    </code><code>def</code> <code>get(</code><code>self</code><code>,params):</code>

<code>        </code><code>params_json</code><code>=</code><code>json.dumps(params)</code>

<code>        </code><code>self</code><code>.client.send(params_json.encode(</code><code>'utf-8'</code><code>))</code>

<code>        </code><code>headers_size </code><code>=</code> <code>struct.unpack(</code><code>'i'</code><code>, </code><code>self</code><code>.client.recv(</code><code>4</code><code>))[</code><code>0</code><code>]   </code><code>#接收報頭長度</code>

<code>        </code><code>headers_bytes </code><code>=</code> <code>self</code><code>.client.recv(headers_size)         </code><code>#接收報頭</code>

<code>        </code><code>headers_json </code><code>=</code> <code>headers_bytes.decode(</code><code>'utf-8'</code><code>)</code>

<code>        </code><code>headers_dic </code><code>=</code> <code>json.loads(headers_json)</code>

<code>        </code><code># print('========&gt;', headers_dic)</code>

<code>        </code><code>filename </code><code>=</code> <code>headers_dic[</code><code>'filename'</code><code>]</code>

<code>        </code><code>filesize </code><code>=</code> <code>headers_dic[</code><code>'filesize'</code><code>]</code>

<code>        </code><code>filepath </code><code>=</code> <code>os.path.join(DOWNLOAD_DIR, filename)</code>

<code>        </code><code>with </code><code>open</code><code>(filepath, </code><code>'wb'</code><code>) as f:                   </code><code>#接收真實資料</code>

<code>            </code><code>recv_size </code><code>=</code> <code>0</code>

<code>            </code><code>while</code> <code>recv_size &lt; filesize:</code>

<code>                </code><code>line </code><code>=</code> <code>self</code><code>.client.recv(</code><code>1024</code><code>)</code>

<code>                </code><code>recv_size </code><code>+</code><code>=</code> <code>len</code><code>(line)</code>

<code>                </code><code>f.write(line)</code>

<code>            </code><code>print</code><code>(</code><code>'===&gt;下載下傳成功'</code><code>)</code>

<code>    </code><code>client</code><code>=</code><code>FtpClient(</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>)</code>

<code>    </code><code>client.interactive()</code>

本文轉自 宋鵬超 51CTO部落格,原文連結:http://blog.51cto.com/qidian510/2066654,如需轉載請自行聯系原作者