網路程式設計知識點剖析
網路程式設計知識點剖析
一. C/S 架構: Client / Server 客戶端 / 服務端
B/S 架構: Browser / Server 前端 / 服務端
二.網路程式設計通訊流程.
網絡卡 mac地址 IP地址 子網掩碼 閘道器 DNS伺服器 (進行域名(domain name)和與之相對應的IP地址 (IP address)轉換的伺服器。)
DHCP (自動分配IP) NAT (Network Address Translation,網路地址轉換) 埠 路由器
交換機 集線器 廣播 單播 廣播風暴 arp協議(地址解析協議) 路由協議
三.網路通訊協議
1. osi七層: tcp\ ip 五層:
應用層 應用層
表示層 傳輸層
會話層 網路層
傳輸層 資料鏈路層
網路層 物理層
資料鏈路層
物理層
2. TCP\IP 協議存在 傳輸層
Tcp : 面向連線,訊息可靠,效率相對差,面向流的訊息格式,無訊息保護邊界
Udp : 面向無連線,訊息不可靠,效率高,面向包的訊息格式,有訊息保護邊界
tcp三次握手:1.(client) > [SYN包 ]->(server) 請求建立連線
2.(client) < [SYN/ACK] < (server) severs收到syn 傳送[SYN/ACK]確認包
3.(client)> [ACK] >(server) client收到[SYN/ACK] 在發一個[ACK]確認包
tcp四次揮手:1.(client) > [ACK/FIN] >(server) 傳送包 請求關閉
2.(client) < [ACK] <(server) 收到包 同意關閉
3.(client) <[ACK/FIN] <(server) 收到包 是否收到 同意關閉 訊息
4.(client) > [ACK/FIN] >(server) 傳送包 收到過程2的訊息,
四.socket
1. 服務端與客戶端 手法訊息 基本結構
server>>>>>>>>>>>>>>>>> import socket sk = socket.socket() sk.bind(('127.0.0.1',8898)) #把地址繫結到套接字 sk.listen() #監聽連結 conn,addr = sk.accept() #接受客戶端連結 ret = conn.recv(1024) #接收客戶端資訊 print(ret) #列印客戶端資訊 conn.send(b'hi') #向客戶端傳送資訊 conn.close() #關閉客戶端套接字 sk.close() #關閉伺服器套接字(可選) client>>>>>>>>>>>>>>>> import socket sk = socket.socket() # 建立客戶套接字 sk.connect(('127.0.0.1',8898)) # 嘗試連線伺服器 sk.send(b'hello!') ret = sk.recv(1024) # 對話(傳送/接收) print(ret) sk.close() # 關閉客戶套接字tcp 服務端和客戶端通訊 基本程式碼
server>>>>>>>>>>>>>>>>>>>>>>>> import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #建立一個伺服器的套接字 udp_sk.bind(('127.0.0.1',9000)) #繫結伺服器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 對話(接收與傳送) udp_sk.close() # 關閉伺服器套接字 client>>>>>>>>>>>>>>>>>>>>>>>> import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)udp
2.緩衝區
作用: 提高效率,將傳送或者接受資訊的過程與網路傳輸隔離開,解耦
特性: 1.I/O緩衝區在每個TCP套接字中單獨存在;
2.I/O緩衝區在建立套接字時自動生成;
3.即使關閉套接字也會繼續傳送輸出緩衝區中遺留的資料;
4.關閉套接字將丟失輸入緩衝區中的資料。
大小: 一般預設為 8K, 可以通過 getsockopt() 函式獲取
3.粘包
TCP會粘包、UDP永遠不會粘包
粘包的兩種現象:
1.連續傳送小包並且間隔時間很短,就會發送兩個訊息合併在一起的情況,被一次接受了,(Nagel 優化演算法導致的, 避免連續傳送小包,影響傳輸xiaol)
2.一次傳送資料過大,對方一次未接受完導致下次接受的時候,連同第一次剩下的訊息,一同接受了,導致粘包.
解決粘包的方案:
原因是因為雙方不知道對方傳送的訊息長度
1.傳送訊息之前先發送訊息的長度,然後收到對方的確認資訊後再發送訊息.
2.通過struct模組,自定義包頭,將訊息長度打包成4個位元組長度的資訊,連同你要傳送的資料,一起傳送過去
打包:pack('i',長度) 長度是個整數
解包:unpack('i',接受的那4個位元組),得到的是元祖
Sendall(): 迴圈的send直到資料傳送完畢.
Socketsserver(): 實現tcp協議下,一個服務端可以同時和多個客戶端進行通訊.
import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) phone.listen(5) while True: conn,addr=phone.accept() while True: cmd=conn.recv(1024) if not cmd:break print('cmd: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() print(err) if err: back_msg=err else: back_msg=res.stdout.read() headers={'data_size':len(back_msg)} head_json=json.dumps(headers) head_json_bytes=bytes(head_json,encoding='utf-8') conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度 conn.send(head_json_bytes) #再發報頭 conn.sendall(back_msg) #在發真實的內容 conn.close()解決粘包方案一 server
import socket import struct,json client= socket.socket() client.connect(("127.0.0.1",8080)) while True: cmd=input(">>: ") if not cmd:continue client.send(bytes(cmd,encoding='utf-8')) head=client.recv(4) head_json_len=struct.unpack('i',head)[0] head_json=json.loads(client.recv(head_json_len).decode('utf-8')) data_len=head_json['data_size'] recv_size=0 recv_data=b'' while recv_size < data_len: recv_data = client.recv(1024) + recv_data recv_size = len(recv_data) + recv_size print(recv_data.decode('gbk'))解決粘包解決方案一 client
import socketserver class Myserver(socketserver.BaseRequestHandler): def handle(self): self.data = self.request.recv(1024).strip() print("{} wrote:".format(self.client_address[0])) print(self.data) self.request.sendall(self.data.upper()) if __name__ == "__main__": HOST, PORT = "127.0.0.1", 9999 # 設定allow_reuse_address允許伺服器重用地址 socketserver.TCPServer.allow_reuse_address = True # 建立一個server, 將服務地址繫結到127.0.0.1:9999 #server = socketserver.TCPServer((HOST, PORT),Myserver) server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver) # 讓server永遠執行下去,除非強制停止程式 server.serve_forever()tcp_server_sockserver
import socket HOST, PORT = "127.0.0.1", 9999 data = "hello" # 建立一個socket連結,SOCK_STREAM代表使用TCP協議 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((HOST, PORT)) # 連結到客戶端 sock.sendall(bytes(data + "\n", "utf-8")) # 向服務端傳送資料 received = str(sock.recv(1024), "utf-8")# 從服務端接收資料 print("Sent: {}".format(data)) print("Received: {}".format(received))tcp_client_socketserver