網路編程-SOCKET開發 網路編程架構分類 B/S架構 B指的是web(網頁),S指的是Server(服務端軟體) C/S架構 C指的是Client(客戶端軟體),S指的是Server(服務端軟體) OSI七層模型 OSI七層模型設計的目的 是成為一個所有電腦廠商都能實現的開放網路模型,來剋服使 ...
網路編程-SOCKET開發
網路編程架構分類
B/S架構
B指的是web(網頁),S指的是Server(服務端軟體)
C/S架構
C指的是Client(客戶端軟體),S指的是Server(服務端軟體)
OSI七層模型
OSI七層模型設計的目的
是成為一個所有電腦廠商都能實現的開放網路模型,來剋服使用眾多私有網路模型所帶來的困難和低效性。
TCP/IP五層模型:
應用層(表示層、會話層)http協議
大概是OS操作系統,系統軟體等用戶層面的。
傳輸層(TCP/UDP協議、埠、四層路由器、四層交換機)
建立埠到埠的通信,有兩種傳輸方式
TCP協議:
TCP是全雙工的通信方式,可靠傳輸,速度慢,對傳遞的數據的長短沒有限制,只要不得到確認,就重新發送數據報,直到收到確認。
TCP的三次握手和四次揮手
SYN::同步標誌(請求連接)
ACK:確認標誌
FIN:結束標誌
UDP協議:
UDP無需連接,不可靠,速度快,傳輸內容長度有限制。
網路層(IP協議、路由器、三層交換機)
IP協議
IP v4、IP v6
IP地址根據網路ID的不同分為五種類型,分別為A、B、C、D、E類地址
- A類:1.0.0.0-126.0.0.0
- B類:128.0.0.0-191.255.255.255
- C類:192.0.0.0-223.255.255.255
- D類:用於多點廣播)
- E類:保留
*特殊:0.0.0.0-當前主機 255.255.255.255-當前子網的廣播地址 127.0.0.1-本機地址,又稱迴環地址。
數據鏈路層(ARP協議、MAC地址相關、網卡、交換機)
ARP協議:地址解析協議,確定目標物理地址
MAC地址:機器唯一標識
物理層(網線)
TCP/IP的傳輸(Socket)
socket是應用層與TCP/IP協議族通信的中間軟體抽象層,相當於一組介面。引用此介面可以實現TCP連接。
socket server端實例代碼:
import socket #導入socket介面 receive = socket.socket() receive.bind(('127.0.0.1', 9999)) #此處的127.0.0.1為IP地址,9999為埠號 receive.listen() #開始TCP監聽 conn, addr = receive.accept() #被動接收TCP客戶端的連接,(阻塞)等待連接。 while True: conn.send('請輸入用戶名:'.encode('utf-8')) ret_user = conn.recv(1024).decode('utf-8') conn.send('請輸入密碼:'.encode('utf-8')) ret_psw = conn.recv(1024).decode('utf-8') if ret_user == 'zhao' and ret_psw == '123': conn.send('登錄成功'.encode('utf-8')) break else: conn.send('用戶名或密碼輸入錯誤'.encode('utf-8')) conn.close() #關閉套接字 receive.close()
socket client端實例代碼:
import socket receive = socket.socket() receive.connect(('127.0.0.1', 9999)) #連接IP地址為127.0.0.1,埠為9999的主機 while True: print(receive.recv(1024).decode('utf-8')) user = input('>>>') receive.send(user.encode('utf-8')) print(receive.recv(1024).decode('utf-8')) psw = input('>>>') receive.send(psw.encode('utf-8')) ret = receive.recv(1024).decode('utf-8') if ret == '登錄成功': print(ret) break else: print(ret) receive.close()
公共用途的socket函數:
s.recv() 接收數據
s.send() 發送數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完,可後面通過實例解釋)
s.sendall() 發送完整的TCP數據(本質就是迴圈調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,迴圈調用send直到發完)
s.recvfrom() 收到的內容為 內容+IP地址
s.close() 關閉套接字
s.getpeername() 連接到當前套接字的遠端的地址
socket.setblocking(flag) #True or False,設置socket為非阻塞模式,以後講io非同步時會用
socket.getaddrinfo(host, port, family=0, type=0, proto=0,flags=0)返回遠程主機的地址信息
socket.getfqdn() 拿到本機的主機名
socket.gethostbyname() 通過功能變數名稱解析ip地址
黏包現象的解決
黏包現象
首先,黏包現象只出現在TCP傳輸中,由於某些原因經過TCP連續發送的信息在很短的時間內某個階段粘連在一起發送,接收方接收到的是一條消息。
造成黏包的原因
1. 在發送端由於兩條消息發送的間隔時間很短,且兩條消息本身也很短,在發送之前被合成了一條消息。
2. 在接收端由於接收不及時導致兩條先後到達的信息在接收端黏在了一起。
黏包的本質
信息與信息之間沒有邊界,且無法解決,因為TCP協議是流式傳輸。
解決黏包問題
struct模塊:
把任意長度的數字變成固定的4個位元組。
l 簡單形式(先發送數據長度,再發送數據)
l 相對規範並複雜的形式(把所有想發送的數據信息放在字典里,發送字典長度,發送字典,發送數據)
struct模塊使用示例:
發送: import struct ret=struct.pack(‘i’,10028) #這裡的’i’代表將int型10028打包 sk.send(ret) 接收: num=sk.recv(4) num=struct.unpack(‘I’,ret)[0] #這裡的’i’代表將ret中的內容解壓為int型,必須加[0],因為它傳過來的是元組。 msg=conn.recv(num).decode(‘utf-8’)
UDP的傳輸
socket.SOCK_DGRAM #UDP傳輸
實例:
server端:
import socket while True: receive = socket.socket(type=socket.SOCK_DGRAM) receive.bind(('0.0.0.0', 9999)) while True: msg, addr = receive.recvfrom(1024) ret = msg.decode('utf-8') if ret.upper() == 'Q': receive.sendto(bytes('您已斷開連接!'.encode('utf-8')), addr) print('對方已與您斷開連接!') break elif ret == '您已斷開連接!': print(ret) break else: print(ret, addr) s = input('>>>').encode('utf-8') receive.sendto(bytes(s), addr) receive.close()
client端:
import socket receive = socket.socket(type=socket.SOCK_DGRAM) while True: addr = input('輸入要連接的ip地址:') addr_port = int(input('輸入埠號:')) receive.connect((addr, addr_port)) while True: n = input('>>>').encode('utf-8') receive.sendto(bytes(n), (addr, addr_port)) msg = receive.recv(1024) ret = msg.decode('utf-8') if ret.upper() == 'Q': receive.sendto(bytes('您已斷開連接!'.encode('utf-8')), (addr, addr_port)) print('對方已與您斷開連接!') break elif ret == '您已斷開連接!': print(ret) break else: print(ret) receive.close()