1、軟件開發架構前端
C/S架構:客戶端/服務端linux
B/S架構:瀏覽器/服務端shell
手機端:好像是c/s架構比較火,可是b/s架構正在逐步火起來 目的:統一接口,彙集流量用戶 服務端:24小時不間斷提供服務 客戶端:何時想體驗服務,就去找服務端體驗服務 學習網絡編程 你就可以開發一個c/s架構的軟件 學習併發編程 數據庫 前端 你就可以開發一個b/s架構的軟件
2、OSI七層模型數據庫
OSI七層協議: 應用層 表示層 會話層 傳輸層 網絡層 數據鏈路層 物理鏈接層 也有人將上面的七層歸納爲五層 1.應用層 2.傳輸層 3.網絡層 4.數據鏈路層 5.物理鏈接層
1.物理鏈接層 數據都是基於電信號傳輸(010101這樣的二進制) 電信號特色:只有高低電平 2.數據鏈路層 010:我 0101:你 01010101010101 1.規定了電信號的分組方式 2.規定了任何一臺接入互聯網的計算機都必須有一塊網卡 網卡:每臺計算機在出廠的時候網卡上面有刻有一段惟一標識碼(不能重複) 16進制12位數字(前6位:廠商編號 後6位是流水線號) 這個獨一無二的標識碼咱們稱之爲"mac地址" ————》》》上述兩個規定統稱爲"以太網協議" 以太網不可以跨局域網傳輸 交換機:全部的電腦都連交換機就能夠實現多臺電腦之間互聯,你的電腦就不會變成馬蜂窩了 基於以太網協議通訊:通訊基本靠吼 弊端:廣播風暴 3.網絡層(IP協議) 規定了任何一臺互聯網的計算機都必須有一個ip地址:點分十進制(ipv4版本) ip地址目前有兩個版本 ipv4 最小:0.0.0.0 最大:255.255.255.255 ipv6(冒號16進制) 最小:0.0.0.0.0.0 最大:。。。。 ip地址:可以找到全部聯入互聯網任何一臺計算機 4.傳輸層(端口協議) TCP,UDP 端口(port): 用來標識一臺計算機上的某個應用程序 端口範圍(0~65535) 動態分配 0~1024操做系統佔用 端口號推薦使用8000以後的 Django默認端口號8000 flask默認端口號5000 MySQL默認端口號3306 Redis默認端口號6379 端口:用來標識某一臺計算機上的某一個應用程序 注意:同一臺計算機上同一時間同一端口號不能重複 小總結:計算機於計算機之間通訊其實計算機上的應用程序之間的通訊 ip地址 port號 ip+port:可以找到任何接入互聯網一臺計算機上面的某個應用程序 5.應用層(HTTP協議,FTP協議...) TCP協議(流式協議,可靠協議) 三次握手四次揮手 TCP流式協議 它會將數據量比較小的而且時間間隔比較短的數據 一次性發送給對方
3、套接字函數編程
# 服務端套接字函數 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() 建立一個與該套接字相關的文件
4、socket初識json
# 服務端 # 舉例 import socket server = socket.socket() # 買手機 不傳參數默認就是TCP協議 server.bind(('127.0.0.1', 8080)) # 插手機卡,傳輸的是一個元組(IP,端口) server.listen(5) # 半鏈接池:同時容許5個會話連接,超過連接不上,客戶端會報錯,默認爲None conn, addr = server.accept() # 待機 阻塞 data = conn.recv(1024) # 聽別人說話 接收1024個bytes print(data) conn.send(b'hello big baby~') # 回話 conn.close() # 掛電話 server.close() # 關機 # 客戶端 import socket client = socket.socket() # 買手機 client.connect(('127.0.0.1', 8080)) # 撥號 client.send(b'hello big baby~') data = client.recv(1024) print(data) client.close()
# 上面的代碼能夠實現一次簡單的信息傳遞 """ 服務端 1.24小時不間斷提供服務端 2.有固定的ip和port 3.支持高併發 併發:只要看起來像同時進行就能夠稱之爲併發 並行:真正意義上的同時執行 """
socket 通信循環flask
# 服務端 server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) conn, addr = server.accept() while True: try: data = conn.recv(1024) # b'' 若是發送這個字符表明,通話結束 if len(data) == 0:break # 針對mac linux系統而言 print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close() # 端口占用:OSError: [WinError 10048] 一般每一個套接字地址(協議/網絡地址/端口)只容許使用一次。
# 客戶端 # 能夠循環接收發送消息 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('please input your msg>>>:').encode('utf-8') if len(msg) == 0:continue client.send(msg) data = client.recv(1024) print(data)
socket通信循環2windows
# 服務端 import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) # 若是超過5我的鏈接過來就會報錯:ConnectionResetError,因爲目標計算機積極拒絕,沒法鏈接 while True: conn, addr = server.accept() while True: try: data = conn.recv(1024) # b'' if len(data) == 0:break # 針對mac linux系統而言 print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close() # 客戶端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('please input your msg>>>:').encode('utf-8') if len(msg) == 0:continue client.send(msg) data = client.recv(1024) print(data)
subprocess模塊回憶瀏覽器
import subprocess cmd = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = cmd.stdout.read().decode('gbk') stderr = cmd.stderr.read().decode('gbk') print(stdout+stderr)
subprocess模塊模擬遠程命令緩存
# 實現服務端接收客戶端發出的命令並執行返回結果 # 服務端 import socket import subprocess server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: cmd = conn.recv(1024).decode('utf-8') if len(cmd) == 0:break obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print(len(stdout+stderr)) # 查看返回的長度 conn.send(stdout+stderr) # 將數據發回客戶端 except ConnectionResetError as e: print(e) break conn.close() server.close() # 客戶端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: cmd = input('please input your cmd>>:').encode('utf-8') if len(cmd) == 0:continue client.send(cmd) data = client.recv(1024) print(data.decode('gbk')) # 上面測試執行dir能夠返回服務端目錄下的目錄結構 # 可是執行tasklist返回的數據沒有返回完整!!!!! # ----緣由是接收發送數據recv接收的長度是1024,可是若是我不知道接收的長度那怎麼辦呢?
粘包現象:
------ 從上面tasklist中引出若是接收的字節超過規定的字節怎麼處理?
TCP流式協議
它會將數據量比較小的而且時間間隔比較短的數據一次性發送給對方
----服務端接收的時候會將數據當成一行數據接收
-- 那如何解決這個問題呢?
必須有一個數據先傳到服務端告訴服務端接下來傳輸的數據大小
struct模塊
# 打包一個數據,並打包成一個固定長度 import struct res = 'ssssssssssssssssssssssssssssssssss' print(len(res)) # 字符串長度34 head = struct.pack('i',len(res)) # pack打包 print(len(head)) # 屢次改變res長度,head長度仍是爲4 real_len = struct.unpack('i',head)[0] print('real_len解壓後長度',real_len) # 長度34
# 打包字典發送
import json d = { 'file_name':'xxx', 'file_size':123213123123234543545324546536546563465435435423543256546546635463465563565653, 'md5':'dkfkdsafksdklafj;asfdsaf' } data_bytes = json.dumps(d).encode('utf-8') print(len(data_bytes)) head = struct.pack('i',len(data_bytes)) head_bytes = struct.unpack('i',head)[0] print(head_bytes)
socket解決粘包問題初級版本
# 服務端 import socket import subprocess import struct server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024).decode('utf-8') if len(cmd) == 0:break obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print(len(stdout+stderr)) # 先製做報頭 header = struct.pack('i',len(stdout+stderr)) # 發送報頭 conn.send(header) # 再發真實數據 conn.send(stdout+stderr) except ConnectionResetError as e: print(e) break conn.close() server.close() # 客戶端 import socket import struct client = socket.socket() client.connect(('127.0.0.1',8080)) while True: cmd = input('please input your cmd>>>:').encode('utf-8') if len(cmd) == 0:continue client.send(cmd) # 先接收報頭 header = client.recv(4) # 解析真實數據長度 total_size = struct.unpack('i',header)[0] # 循環接收真實數據,判斷數據長度 data = b'' recv_size = 0 while recv_size < total_size: info = client.recv(1024) data += info recv_size += len(info) print(data.decode('gbk'))
socket解決粘包問題終極版本
# 解決上面返回字符長度或不知字符長度問題
# 服務端 import socket import subprocess import struct import json server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024).decode('utf-8') if len(cmd) == 0:break obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print(len(stdout+stderr)) send_dic = {"name":'simon','password':'123','total_size':len(stdout+stderr)} send_bytes = json.dumps(send_dic).encode('utf-8') # 先製做字典的報頭 header = struct.pack('i',len(send_bytes)) # 發送報頭 conn.send(header) # 再發字典 conn.send(send_bytes) # 最後再發真實數據 conn.send(stdout+stderr) except ConnectionResetError as e: print(e) break conn.close() server.close() # 客戶端 import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: cmd = input('please input your cmd>>>:').encode('utf-8') if len(cmd) == 0:continue client.send(cmd) # 先接收報頭 header = client.recv(4) # 解析獲取字典的長度 dic_len = struct.unpack('i',header)[0] dic_bytes = client.recv(dic_len) real_dic = json.loads(dic_bytes.decode('utf-8')) print(real_dic) # 循環接收真實數據 data = b'' recv_size = 0 while recv_size < real_dic['total_size']: info = client.recv(1024) data += info recv_size += len(info) print(data.decode('gbk'))
socket實現大文件上傳:
# 服務端 import socket import struct import json # 解決報錯信息 from socket import SOL_SOCKET,SO_REUSEADDR server = socket.socket() # 解決報錯信息 server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind(('127.0.0.1',8090)) server.listen(5) conn,addr = server.accept() # 先收4個長度報頭 header = conn.recv(4) # 獲取字典長度 dic_len = struct.unpack('i',header)[0] # 接收字典 dic_bytes = conn.recv(dic_len) real_dic = json.loads(dic_bytes.decode('utf-8')) # 獲取文件相關信息 file_name = real_dic.get('file_name') total_size = real_dic.get("file_size") with open(file_name,'wb') as f: # 循環接收文件 recv_size = 0 while recv_size < total_size: data = conn.recv(1024) f.write(data) # 收取就存入文件 recv_size += len(data) print('文件上傳成功') # 客戶端 import socket import struct import json import os client = socket.socket() client.connect(('127.0.0.1',8090)) file_path = r'D:\週末\day\day\01 今日內容.mp4' file_size = os.path.getsize(file_path) # 獲取文件大小 send_dic = {"file_name":'FBI warning.mp4','file_info':"注意身體",'file_size':file_size} send_bytes = json.dumps(send_dic).encode('utf-8') # 製做報頭 head = struct.pack('i',len(send_bytes)) # 發送報頭 client.send(head) # 發字典 client.send(send_bytes) # 發文件 with open(file_path,'rb') as f: for line in f: client.send(line)
5、UDP協議:數據報協議
UDP特色: 1.udp協議客戶端容許發空 2.udp協議不會粘包 3.udp協議服務端不存在的狀況下,客戶端照樣不會報錯
# 服務端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1', 8080)) msg, addr = server.recvfrom(1024) print(msg.decode('utf-8')) server.sendto(b'hello', addr) server.close() #客戶端 import socket client = socket.socket(type=socket.SOCK_DGRAM) # 配置服務端地址端口 server_addr = ('127.0.0.1', 8080) # 發的時候必須帶上IP地址,服務端才能知道你是誰;客戶端容許發空,服務端也接收爲空 client.sendto(b'hello server baby!', server_addr) msg, addr = client.recvfrom(1024) print(msg, addr)
""" # udp特色 >>> 無連接,相似於發短信,發了就行對方愛回不回,沒有任何關係 # 將服務端關了,客戶端起起來照樣可以發數據。由於不須要考慮服務端能不能收到 """ # 驗證udp協議有無粘包問題 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) print(server.recvfrom(1024)) print(server.recvfrom(1024)) print(server.recvfrom(1024)) import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1',8080) client.sendto(b'hello',server_addr) client.sendto(b'hello',server_addr) client.sendto(b'hello',server_addr)
練習:基於UDP實現簡易版本的對話
# 服務端 import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: data, addr = server.recvfrom(1024) print(data.decode('utf-8')) server.sendto(data.upper(),addr) # 多個客戶端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_addr = ('127.0.0.1', 8080) while True: info = input('>>>:') info = ('來自客戶端1的消息:%s'%info).encode('utf-8') # 改中文備註便可 client.sendto(info, server_addr) msg, addr = client.recvfrom(1024) print(msg.decode('utf-8'), addr) client.close() """ 服務端接收: 客戶端1:sdafasewrtwe 客戶端2:erwtwetwe 客戶端3:retwtrwetw 客戶端4:ewrbxcbvxb """
補充:windows電腦和mac電腦的時間同步功能,其實就是基於udp向windows,mac服務器發送請求獲取標準時間
總結:TCP協議就相似於打電話;UDP協議就相似於發短信
# TCP socketserver使用 import socketserver class MyTcpServer(socketserver.BaseRequestHandler): def handle(self): while True: try: data = self.request.recv(1024) # 對於tcp,self.request至關於conn對象 if len(data) == 0:break print(data) self.request.send(data.upper()) except ConnectionResetError: break if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTcpServer) # 請求來了交給MyTcpServer處理 server.serve_forever() # UDP socketserver使用 import socketserver class MyUdpServer(socketserver.BaseRequestHandler): def handle(self): while True: data, sock = self.request print(data) sock.sendto(data.upper(), self.client_address) if __name__ == '__main__': server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyUdpServer) server.serve_forever()
做業:
做業:開發一個支持多用戶在線的FTP程序 要求: 1.用戶加密認證 2.容許同時多用戶登陸 3.每一個用戶有本身的家目錄,且只能訪問本身的家目錄 4.對用戶進行磁盤配額,每一個用戶的可用空間不一樣 5.容許用戶在ftp server 上隨意切換目錄 6.容許用戶查看當前目錄下的文件 7.容許上傳和下載文件,保證文件一致性 8.文件傳輸過程當中顯示進度條 9.附加功能:支持斷點續傳