網絡編程自我感受仍是很重要的一部分算法
C/S架構 client/server
B/S架構 server/browser
二者的關係?
B/S架構是C/S架構的一種shell
計算機與計算機之間是如何通訊的?編程
網卡、網線服務器
網卡:提供網線的接口,經過網卡找到計算機,一個網卡擁有全球惟一的mac地址網絡
網卡,網線,交換機架構
交換機:一箇中心節點,全部的網線鏈接到交換機上併發
計算機1尋找計算機2併發消息的過程:socket
計算機1發送消息到交換機,交換機把這個信息廣播給局域網中全部計算機,計算機2收到消息與本身的mac地址比對,確認是發給本身的消息以後,回覆消息給交換機,交換機發消息到計算機1ide
網卡,網線,交換機,路由器函數
路由器:鏈接多個交換機
廣播:計算機1要和計算機2通訊,告訴交換機要找計算機2,交換機會告訴全部的計算機要找計算機2
廣播風暴:全部的計算機同時去找,同一臺計算機會接收到不少與本身不相關的信息,從而形成網絡擁堵
單播:只有計算機2會回覆消息給交換機,交換機發送給計算機1
網關:不一樣的局域網中的計算機之間通訊,同一個局域網的默認網關相同
子網掩碼:ip地址和子網掩碼按位與 (計算出來局域網的網段,同一個網段只有最後一位不一樣,前三位都相同)來判斷是否在同一局域網
按位與:1|1=1,1|0=1,0|0=0
4個點分十進制(4個八位二進制數)範圍:0-255
做用:
經過ip地址能夠找到對應的mac地址 根據arp協議
127.0.0.1:本地迴環地址,本地中的兩個程序通訊
IPV4:0.0.0.0-255.255.255.255
IPV6:0.0.0.0.0.0-255.255.255.255.255.255
做用:1.爲一臺計算機分配ip地址
2.計算計算機的網段
若是想讓別人能訪問到本身的計算機中的程序,必須有一個已經在公網中註冊的ip地址
計算機裏邊的每個須要聯網的程序都有一個端口
qq聊天時,對方不只須要你的ip地址,還須要qq程序的端口號
端口的範圍:0-25535
本身開端口:開8000後的端口
每層的工做
應用層 程序,要發送的數據包
傳輸層 選擇通訊協議,給要發送的數據加上協議
網絡層 給要發送的數據加上ip信息
數據鏈路層 經過arp協議獲得mac地址,給要發送的數據加上mac地址
物理層 兩個計算機連接
每層用到的協議:
應用層 http協議,SMTP協議,ftp協議
傳輸層 TCP/UDP協議
網絡層 ip協議
數據鏈路層 ARP協議
物理層
每層常見物理設備:
應用層
傳輸層 四層交換機,四層路由器
網絡層 路由器,三層交換機(帶路由功能)
數據鏈路層 網橋,交換機,網卡
物理層 中繼器,雙絞線,集線器
TCP協議:可靠的,創建連接的
全雙工通訊:雙方能夠互相收發信息
三次握手:必定是client先請求發送--server端接受請求而且請求發送--client端接收請求 通道鏈接成功
四次揮手:client/server請求斷開鏈接--server/client贊成斷開--server/client請求斷開鏈接--client/server贊成斷開
UDP協議:
不創建連接的,不可靠的
使用socket編程來進行進程間的通信
TCP服務端:建立套接字 (socket)--綁定端口 (bind)--傾聽客戶請求(listen)--接受客戶端鏈接(accept)--接受、發送(recv、send)--關閉套接字(closesocket)
TCP客戶端:建立套接字(socket)--連接服務器(connect)--發送,接受(send、recv)--關閉套接字(closesocket)
import socket soc = socket.socket() #手機開機 soc.bind(('localhost',9999)) #啓動SIM卡 soc.listen() #等待別人打電話 connect,addr = soc.accept() #拿到通話鏈接,別人的電話號 recv = connect.recv(1024) #聽別人講話 send = connect.send(b'hello') #對別人講話 print(str(recv)) connect.close() #掛斷電話 soc.close() #關機
import socket sock = socket.socket() #手機開機 sock.connect(('localhost',9999)) #給別人打電話 sock.send(b'jcc') #對別人講話 rec = sock.recv(1024) #聽別人講話 print(str(rec)) sock.close() #關機
import socket udpsk = socket.socket(type=socket.SOCK_DGRAM) #建立一個服務器的套接字 DGRAM:datagram 基於udp傳輸的數據包 udpsk.bind(('localhost',8888)) #綁定指定服務器 while 1: msg,addr = udpsk.recvfrom(1024) #接受消息 print(msg.decode('utf-8')) if msg.decode('utf-8')=='bye':break inp = input('>>>') udpsk.sendto(bytes(inp,encoding='utf-8'),addr) #發送消息 udpsk.close() #關閉套接字
import socket udpsk = socket.socket(type=socket.SOCK_DGRAM) #建立一個服務器的套接字 DGRAM:datagram 基於udp傳輸的數據包 udpsk.bind(('localhost',8888)) #綁定指定服務器 while 1: msg,addr = udpsk.recvfrom(1024) #接受消息 print(msg.decode('utf-8')) if msg.decode('utf-8')=='bye':break inp = input('>>>') udpsk.sendto(bytes(inp,encoding='utf-8'),addr) #發送消息 udpsk.close() #關閉套接字
現象:
接受和發送不匹配
基於UDP協議的傳輸不會出現黏包現象,一次接收不完的信息會被丟掉
基於TCP協議的傳輸會出現黏包現象,可是不丟包
基於UDP協議的傳輸會限制數據包的大小,超過的信息被丟掉
基於TCP協議的傳輸不會限制數據包的大小
產生黏包的緣由:
TCP協議是面向流的,無邊界的
TCP協議有一種拆包機制,當發送的數據包大於網關的mtu(網絡上傳輸的最大數據包),該數據包就會被拆開,分批發送
Nagle算法:優化傳輸速度(將屢次間隔較小且數據量小的數據合併成一個大的數據塊,封包)致使黏包現象
黏包出現的最終緣由:不知道要接受的數據有多大
先發送數據包的長度,根據長度接受數據(缺點:多了一次客戶服務器之間的交互)
import socket sock = socket.socket() sock.bind(('127.0.0.1',9991)) sock.listen() conn,addr = sock.accept() while 1: cmd = input('>>>') conn.send(cmd.encode('gbk')) len1 = conn.recv(1024) #先接受長度 recv = conn.recv(int(len1.decode('gbk'))) #根據長度接受數據包 print('std:'+recv.decode('gbk'))
import socket import subprocess sock = socket.socket() sock.connect(('127.0.0.1',9991)) while 1: cmd = sock.recv(1024) #執行計算機命令subprocess sb = subprocess.Popen(cmd.decode('gbk'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout = sb.stdout.read() stderr = sb.stderr.read() print('stdout'+str(len(stdout))) print('stderr' + str(len(stderr))) sock.send(str(len(stdout)+len(stderr)).encode('gbk')) #發送長度 sock.send(stdout) #發送消息 sock.send(stderr)
import struct p = struct.pack('i',20)#int轉bytes print(p) num = b'1223' unp = struct.unpack('i',num)[0]#bytes轉int print(unp)
import socketserver class Myserver(socketserver.BaseRequestHandler): #繼承BaseRequestHandler def handle(self): #重寫handle函數 self.data = self.request.recv(1024) #self.request至關與conn,接受信息 print(self.client_address[0]) print(self.data.decode('utf-8')) self.request.sendall(self.data) #發送消息 if __name__ == '__main__': host,port = '127.0.0.1',9999 socketserver.TCPServer.allow_reuse_address = True # 設置allow_reuse_address容許服務器重用地址 server = socketserver.TCPServer((host,port),Myserver) # 建立一個server, 將服務地址綁定到127.0.0.1:9999 server.serve_forever() #程序一直運行