網絡編程-SOCKET開發
網絡編程-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地址根據網絡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()
網絡編程-SOCKET開發