一、系統和網絡
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>'>>: '</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>'>>: '</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 < 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>'========>'</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>'>>: '</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('========>', 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 < 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>'===>下載下傳成功'</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,如需轉載請自行聯系原作者