網絡編程

知識儲備html

 

 

 

C/S和B/S架構python

1 # C/S架構 2 client<---->server 3 # B/S架構 4 browser<---->server
View Code

物理層:算法

物理層功能:主要是基於電器特性發送高低電壓(電信號),高電壓對應數字1,低電壓對應數字0shell

數據鏈路層編程

數據鏈路層由來:單純的電信號0和1沒有任何意義,必須規定電信號多少位一組,每組什麼意思json

數據鏈路層功能:定義了電信號的分組方式windows

以太網協議:緩存

造成統一標準,以太網協議ethernet安全

ethernet規定服務器

  • 一組電信號構成一個數據包,叫作’幀‘
  • 每一數據幀分紅:包頭head和數據data兩部分

head包含:(固定18個字節)

  • 源,6個字節
  • 目,6個字節
  • 數據類型,6個字節

data包含:(最短46字節,最長1500字節)

  • 數據包的具體內容
head長度+data長度=最短64字節,最長1518字節,超過最大限制就分片發送
數據包內容  

網絡層

網絡層由來:有了mac地址,同一網絡內的兩臺主機就能夠通訊了(一臺主機經過arp協議獲取另一臺主機的mac地址),ethernet採用最原始的方式,廣播的方式進行通訊,即計算機通訊基本靠吼。

如今還須要一種手段來斷定,必須找出一種方法來區分哪些計算機屬於同一廣播域,哪些不是,若是是就採用廣播的方式發送,若是不是,就採用路由的方式(向不一樣廣播域/子網分發數據包)。

網絡層功能:引入一套新的地址用來區分不一樣的廣播域/子網,這套地址即網絡地址

傳輸層

傳輸層的由來:傳輸層如下都是由操做系統控制,網絡層的ip幫咱們區分子網,以太網層的mac幫咱們找到主機,而後你們使用的都是應用程序,你的電腦上可能同時開啓qq,暴風影音,等多個應用程序,

那麼咱們經過ip和mac找到了一臺特定的主機,如何標識這臺主機上的應用程序,答案就是端口端口即應用程序與網卡關聯的編號

傳輸層功能:創建端口到端口的通訊

補充:端口範圍0-65535,0-1023爲系統佔用端口

tcp協議:

可靠傳輸,TCP數據包沒有長度限制,理論上能夠無限長,可是爲了保證網絡的效率,一般TCP數據包的長度不會超過IP數據包的長度,以確保單個TCP數據包沒必要再分割。

以太網頭 ip 頭               tcp頭               數據                                                    

 

udp協議:

不可靠傳輸,」報頭」部分一共只有8個字節,總長度不超過65,535字節,正好放進一個IP數據包。

以太網頭 ip頭                      udp頭                            數據                                           

 

tcp報文

tcp三次握手和四次揮手

tcp創建的是雙向連接

C -----------------------》 S C <----------------------- S
TCP創建的是雙向連接
C ---- syn=1 seq=x ----> S S ---- ack=1+x syn=1 seq=y ----> C C ---- ack=1+y ----> S
TCP三次握手鍊接
S ---- fin=1 ---->C C ---- ack=1 ----> S C ---- fin=1 ----> S S ---- ack=1 ----> C
四次揮手斷連

應用層

應用層由來:用戶使用的都是應用程序,均工做於應用層,互聯網是開發的,你們均可以開發本身的應用程序,數據多種多樣,必須規定好數據的組織形式 

應用層功能:規定應用程序的數據格式。

例:TCP協議能夠爲各類各樣的程序傳遞數據,好比Email、WWW、FTP等等。那麼,必須有不一樣協議規定電子郵件、網頁、FTP數據的格式,這些應用程序協議就構成了」應用層」。

 

 TCP/UDP

 什麼是網絡?

一、物理鏈接介質;

二、互聯網協議。

 MAC+IP:定位全世界一臺獨一無二的計算機

MAC+IP+PORT:定位全世界一臺獨一無二的計算機上的程序

但在程序中,由於操做系統配備了ARP協議,可實現IP和MAC的轉換

總結:所以【IP+PORT】便可定位定位全世界一臺獨一無二的計算機上的程序

TCP UDP
TCP與UDP基本區別
  1.基於鏈接與無鏈接
  2.TCP要求系統資源較多,UDP較少; 
  3.UDP程序結構較簡單 
  4.流模式(TCP)與數據報模式(UDP); 
  5.TCP保證數據正確性,UDP可能丟包 
  6.TCP保證數據順序,UDP不保證 
  
UDP應用場景:
  1.面向數據報方式
  2.網絡數據大多爲短消息 
  3.擁有大量Client
  4.對數據安全性無特殊要求
  5.網絡負擔很是重,但對響應速度要求高
 
具體編程時的區別
   1.socket()的參數不一樣 
   2.UDP Server不須要調用listen和accept 
   3.UDP收發數據用sendto/recvfrom函數 
   4.TCP:地址信息在connect/accept時肯定 
   5.UDP:在sendto/recvfrom函數中每次均 需指定地址信息 
   6.UDP:shutdown函數無效
 
編程區別
   一般咱們在說到網絡編程時默認是指TCP編程,即用前面提到的socket函數建立一個socket用於TCP通信,函數參數咱們一般填爲SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),這表示創建一個socket用於流式網絡通信。 
   SOCK_STREAM這種的特色是面向鏈接的,即每次收發數據以前必須經過connect創建鏈接,也是雙向的,即任何一方均可以收發數據,協議自己提供了一些保障機制保證它是可靠的、有序的,即每一個包按照發送的順序到達接收方。 

  而SOCK_DGRAM這種是User Datagram Protocol協議的網絡通信,它是無鏈接的,不可靠的,由於通信雙方發送數據後不知道對方是否已經收到數據,是否正常收到數據。任何一方創建一個socket之後就能夠用sendto發送數據,也能夠用recvfrom接收數據。根本不關心對方是否存在,是否發送了數據。它的特色是通信速度比較快。你們都知道TCP是要通過三次握手的,而UDP沒有。 

基於上述不一樣,UDP和TCP編程步驟也有些不一樣,以下:

TCP: 
TCP編程的服務器端通常步驟是: 
  一、建立一個socket,用函數socket(); 
  二、設置socket屬性,用函數setsockopt(); * 可選 
  三、綁定IP地址、端口等信息到socket上,用函數bind(); 
  四、開啓監聽,用函數listen(); 
  五、接收客戶端上來的鏈接,用函數accept(); 
  六、收發數據,用函數send()和recv(),或者read()和write(); 
  七、關閉網絡鏈接; 
  八、關閉監聽; 

TCP編程的客戶端通常步驟是: 
  一、建立一個socket,用函數socket(); 
  二、設置socket屬性,用函數setsockopt();* 可選 
  三、綁定IP地址、端口等信息到socket上,用函數bind();* 可選 
  四、設置要鏈接的對方的IP地址和端口等屬性; 
  五、鏈接服務器,用函數connect(); 
  六、收發數據,用函數send()和recv(),或者read()和write(); 
  七、關閉網絡鏈接;

UDP:
與之對應的UDP編程步驟要簡單許多,分別以下: 
  UDP編程的服務器端通常步驟是: 
  一、建立一個socket,用函數socket(); 
  二、設置socket屬性,用函數setsockopt();* 可選 
  三、綁定IP地址、端口等信息到socket上,用函數bind(); 
  四、循環接收數據,用函數recvfrom(); 
  五、關閉網絡鏈接; 

UDP編程的客戶端通常步驟是: 
  一、建立一個socket,用函數socket(); 
  二、設置socket屬性,用函數setsockopt();* 可選 
  三、綁定IP地址、端口等信息到socket上,用函數bind();* 可選 
  四、設置對方的IP地址和端口等屬性; 
  五、發送數據,用函數sendto(); 
  六、關閉網絡鏈接;

TCP和UDP是OSI模型中的運輸層中的協議。TCP提供可靠的通訊傳輸,而UDP則常被用於讓廣播和細節控制交給應用的通訊傳輸。

UDP補充:
   UDP不提供複雜的控制機制,利用IP提供面向無鏈接的通訊服務。而且它是將應用程序發來的數據在收到的那一刻,馬上按照原樣發送到網絡上的一種機制。即便是出現網絡擁堵的狀況下,UDP也沒法進行流量控制等避免網絡擁塞的行爲。此外,傳輸途中若是出現了丟包,UDO也不負責重發。甚至當出現包的到達順序亂掉時也沒有糾正的功能。若是須要這些細節控制,那麼不得不交給由採用UDO的應用程序去處理。換句話說,UDP將部分控制轉移到應用程序去處理,本身卻只提供做爲傳輸層協議的最基本功能。UDP有點相似於用戶說什麼聽什麼的機制,可是須要用戶充分考慮好上層協議類型並製做相應的應用程序。

TCP補充:
  TCP充分實現了數據傳輸時各類控制功能,能夠進行丟包的重發控制,還能夠對次序亂掉的分包進行順序控制。而這些在UDP中都沒有。此外,TCP做爲一種面向有鏈接的協議,只有在確認通訊對端存在時纔會發送數據,從而能夠控制通訊流量的浪費。TCP經過檢驗和、序列號、確認應答、重發控制、鏈接管理以及窗口控制等機制實現可靠性傳輸。


TCP與UDP區別總結:
一、TCP面向鏈接(如打電話要先撥號創建鏈接);UDP是無鏈接的,即發送數據以前不須要創建鏈接
二、TCP提供可靠的服務。也就是說,經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保   證可靠交付
三、TCP面向字節流,其實是TCP把數據當作一連串無結構的字節流;UDP是面向報文的
  UDP沒有擁塞控制,所以網絡出現擁塞不會使源主機的發送速率下降(對實時應用頗有用,如IP電話,實時視頻會議等)
四、每一條TCP鏈接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通訊
五、TCP首部開銷20字節;UDP的首部開銷小,只有8個字節
六、TCP的邏輯通訊信道是全雙工的可靠信道,UDP則是不可靠信道
TCP/UDP 編程區別

套接字

 

 TCP套接字工做流程:

 套接字程序編碼:

  一、client.connect() ----》server.accept(),此過程就實現封裝三次握手過程

  二、服務器端的conn套接字對象起到收發消息的做用,便可收消息,可發消息

socket服務端(單步)詳細步驟說明:

 1 import socket  2 
 3 # 買電話
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  5 # 默認socket.AF_INET
 6 # socket.SOCK_STREAM稱爲流式協議(TCP),socket.SOCK_DGRAM爲UDP協議
 7 
 8 # 插手機卡
 9 phone.bind(('127.0.0.1',8080)) # 手機卡里面關鍵是綁定本身的手機號,教室IP:192.168.12.112,端口,0-65535,0-1024給系統用
10 
11 # 開機
12 phone.listen(5) # 同一時間請求數,開啓半連接池,之後通常都是寫在配置文件裏,並非只能創建5個連接,
13 print('start...') 14 
15 # 等電話連接
16 conn,client_addr = phone.accept() # conn,套接字的收發消息對象,客戶端client_addr(隨機變)
17 print('連接來了:',conn,client_addr) 18 
19 # 收發消息
20 msg = conn.recv(1024) #收消息,1024是一個最大的限制,這裏假設不超過1024的狀況
21 print('客戶端的消息:',msg) 22 conn.send(msg.upper()) 23 
24 # 掛電話
25 conn.close() #回收系統資源,跟打開文件後關閉文件是一個道理
26 
27 # 關機
28 phone.close()
Socket服務端(單步)詳細說明

服務端Sever(循環):

 1 import socket  2 
 3 # 買電話
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  5 
 6 # 插手機卡
 7 phone.bind(('127.0.0.1',8081))  8 
 9 # 開機
10 phone.listen(5) 11 print('start...') 12 # 連接循環
13 while True: 14     conn,client_addr = phone.accept() 15     print('客戶端:',client_addr) 16 
17     # 收發消息,通訊循環
18     while True: 19         try: 20             msg = conn.recv(1024) #收消息,1024是一個最大的限制,這裏假設不超過1024的狀況
21             print('客戶端的消息:',msg) 22  conn.send(msg.upper()) 23         except ConnectionResetError: 24             break # 此刻異常出現,conn是無心義對象,故不可用continue,而採用break
25 
26  conn.close() 27     # 運行至此步,進行下一步連接
28 
29 phone.close()
Socket服務器端

客戶端Client:

(補充:程序結束後,回收程序資源的同時也要回收系統資源。最後應補充一段代碼:client.close())

 1 import socket  2 
 3 # 買電話
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  5 
 6 # 撥電話,服務端IP和端口
 7 phone.connect(('127.0.0.1',8081))  8 
 9 # 發消息
10 while True: 11 
12     msg = input('>>>:').strip() 13     phone.send(msg.encode('utf-8')) 14     # 收消息
15     data = phone.recv(1024) 16     print(data.decode('utf-8'))
Socket客戶端

 編寫C/S架構的程序,實現遠程執行命令: 

#subprocess使用當前系統默認編碼,獲得結果爲bytes類型,在windows下須要用gbk解碼
 1 import socket  2 import subprocess  3 
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  5 phone.bind(('127.0.0.1',8082))  6 phone.listen(5)  7 print('連線中...')  8 
 9 while True: 10     conn,client_addr = phone.accept() 11     print('創建鏈接客戶端:',client_addr) 12 
13     while True: 14         try: 15             cmd = conn.recv(1024) 16             print('客戶端的消息:',cmd.decode('utf-8')) 17             obj = subprocess.Popen(cmd.decode('utf-8'), 18                              shell=True, 19                              stdout=subprocess.PIPE, 20                              stderr=subprocess.PIPE 21  ) 22             stdout = obj.stdout.read() # 成功結果
23             stderr = obj.stderr.read() # 錯誤結果
24 
25             conn.send(stdout+stderr) 26 
27         except ConnectionResetError: 28             print('一個客戶端停止連接') 29             break
30  conn.close() 31 
32 phone.close()
遠程執行命令-服務器端
 1 import socket  2 import subprocess  3 
 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  5 phone.connect(('127.0.0.1',8082))  6 
 7 
 8 while True:  9     cmd = input('請輸入通訊內容>>:').strip() 10 
11     phone.send(cmd.encode('utf-8')) 12     msg = phone.recv(1024) 13     print(msg.decode('gbk')) # 服務器端返回命令行內容,需gbk解碼
14 
15 phone.close()
遠程執行命令-客戶端

粘包問題

這裏,會出現一個問題,當命令執行結果大於1024字節時,會有消息冗餘問題。

在找到解決方案前,需瞭解,無論收發,其實數據都是先要通過應用程序內存操做系統緩存的處理過程。 

其次,tcp流協議有兩個特色:

1,像水流同樣,源源不斷從源頭向對方堆砌數據

2,negal算法:優化數據傳輸,將時間間隔比較短和數據比較小的數據預留

那麼,如今只要知道對方要發多少數據,那麼我就能知道如何判斷是否收到所有數據

在此以前補充一下,使用stuct模塊可高度定製化的報頭數據

最後,解決粘包的方法見下:

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 # @Time : 2018/04/19 14:03
 4 # @Author : MJay_Lee
 5 # @File : Server.py
 6 # @Contact : limengjiejj@hotmail.com
 7 
 8 import socket  9 import subprocess 10 import struct 11 import json 12 
13 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 14 phone.bind(('127.0.0.1',8082)) 15 phone.listen(5) 16 print('連線中...') 17 
18 while True: 19     conn,client_addr = phone.accept() 20     print('創建鏈接客戶端:',client_addr) 21 
22     while True: 23         try: 24             cmd = conn.recv(1024) 25             print('客戶端的消息:',cmd.decode('utf-8')) 26             obj = subprocess.Popen(cmd.decode('utf-8'), 27                              shell=True, 28                              stdout=subprocess.PIPE, 29                              stderr=subprocess.PIPE 30  ) 31             stdout = obj.stdout.read() # 成功結果
32             stderr = obj.stderr.read() # 錯誤結果
33 
34             print(len(stdout)+len(stderr)) # 打印出服務器端返回消息的長度
35             # conn.send(stdout+stderr)
36 
37 
38             #一、發送數據長度,發送數據長度,製做報頭
39             header_dic = { 40                 'total_size':len(stdout) + len(stderr), 41                 'md5':'asdasdqasdasd', 42                 'filename':'test.txt'
43  } 44             header_json = json.dumps(header_dic) 45             header_bytes = header_json.encode('utf-8') 46 
47             # 2 先發送報頭的長度
48             header_size = len(header_bytes) 49             conn.send(struct.pack('i',header_size)) 50 
51             #三、發送報頭
52  conn.send(header_bytes) 53 
54             #四、發送真實數據
55  conn.send(stdout) 56  conn.send(stderr) 57 
58         except ConnectionResetError: 59             print('一個客戶端停止連接') 60             break
61  conn.close() 62 phone.close()
服務器端終極版
 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 # @Time : 2018/04/19 14:03
 4 # @Author : MJay_Lee
 5 # @File : Client.py
 6 # @Contact : limengjiejj@hotmail.com
 7 
 8 import socket  9 import subprocess 10 import struct 11 import json 12 
13 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 14 client.connect(('127.0.0.1',8082)) 15 
16 
17 while True: 18     cmd = input('請輸入通訊內容>>:').strip() 19     client.send(cmd.encode('utf-8')) 20     #一、先收報頭長度
21     header_size = struct.unpack('i',client.recv(4))[0] 22     #二、接收報頭
23     header_bytes = client.recv(header_size) 24     #三、解析報頭
25     header_json = header_bytes.decode('utf-8') 26     header_dic = json.loads(header_json) 27     print(header_dic) 28 
29 
30     total_size = header_dic['total_size'] 31     print(total_size) 32     #四、根據報頭內的信息,收取真實的數據
33     recv_size = 0 34     res = b''
35     while recv_size < total_size: 36         recv_data = client.recv(1024) 37         res += recv_data 38         recv_size += len(recv_data) 39     print(res.decode('gbk')) 40 
41 client.close()
客戶端終極版

補充:客戶端鏈接個數超出服務器設置的backlog值,出現:「ConnectionRefusedError: [WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接。」,解決方案以下「優化客戶端終極版」

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 # @Time : 2018/04/23 14:08
 4 # @Author : MJay_Lee
 5 # @File : client.py
 6 # @Contact : limengjiejj@hotmail.com
 7 
 8 import socket  9 import time 10 import json 11 import struct 12 
13 while True: 14     try: 15         client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16         client.connect(('127.0.0.1', 8080)) 17         break
18     except ConnectionRefusedError: 19         print('等待3秒') 20         time.sleep(3) 21 
22 
23 while True: 24     cmd = input('請輸入通訊內容:').strip() 25     if not cmd:continue
26     client.send(cmd.encode('utf-8')) 27     # 得到報頭長度
28     header_size = struct.unpack('i',client.recv(4))[0] 29     # 獲取報頭
30     header_bytes = client.recv(header_size) 31     # 解析報頭
32     header_json = header_bytes.decode('utf-8') 33     header_dic = json.loads(header_json) 34 
35     total_size = header_dic['header_len'] 36     recv_size = 0 37     res = b''
38 
39     while recv_size < total_size: 40         recv_data = client.recv(1024) 41         res += recv_data 42         recv_size += len(recv_data) 43 
44     print('來自服務器的消息:',res.decode('gbk')) 45 
46 client.close()
優化客戶端終極版

 基於UDP協議通訊的套接字

相比較於UDP,TCP之因此可靠,是由於TCP的工做原理(每發一個數據,獲得迴應,纔會清掉系統緩存裏的數據。),而非多一個連接。

場景:多用於查詢場景,可靠性要求不高

注意:UDP協議發消息的時候,有效傳輸最大51bytes,不然就容易出現丟包

套接字常識:WEB服務端端口80,DNS端口53

進程

三個狀態

正文請移步至此

相關文章
相關標籤/搜索