Python學(xué)習(xí)筆記八
類的高級(jí)用法
多態(tài):
在其他語(yǔ)言,使用的是類的繼承。
在python中,不需要指定數(shù)據(jù)類型。
基于TCP協(xié)議的socket通信實(shí)現(xiàn):
類似于打電話的情景。
服務(wù)端:
1.買手機(jī)
2.插卡
3.開機(jī)
4.等待電話鏈接
5.收消息
6.發(fā)消息
7.掛電話
8.關(guān)手機(jī)
具體代碼實(shí)現(xiàn)如下:
import socket #導(dǎo)入一個(gè)socket模塊 server1=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機(jī),基于socket的網(wǎng)絡(luò)通信,使用的是TCP協(xié)議 server1.bind(('127.0.0.1',8080)) #插卡,指定服務(wù)器的IP地址和端口號(hào) server1.listen(5) #開機(jī),設(shè)置監(jiān)聽等待數(shù) conn,client_addr=server1.accept() #等待電話鏈接 while True: recv_data=conn.recv(1024) #收消息,限制單個(gè)消息的最大數(shù)為1024字節(jié) print(recv_data) conn.send(recv_data.upper()) #發(fā)消息 conn.close() #掛手機(jī) server1.close() #關(guān)機(jī)
客戶端:
1.買手機(jī)
2.打電話
3.發(fā)消息
4.收消息
具體代碼實(shí)現(xiàn):
import socket client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) #打電話 while True: message=input('數(shù)據(jù)>>').strip() client.send(message.encode('utf-8')) #發(fā)消息 recv=client.recv(1024) #收消息 print(recv)
上面的服務(wù)端和客戶端都添加了while True,實(shí)現(xiàn)了通信循環(huán)。
但是現(xiàn)在有個(gè)問題:就是如果客戶端輸入為空,客戶端會(huì)卡住。
分析這個(gè)問題:
要么就是客戶端沒有發(fā)送出去,要么就是客戶端沒有收到。
結(jié)果:因?yàn)榭蛻舳税l(fā)送為空的消息,但是服務(wù)器并沒有收到該消息,所以客戶端也不會(huì)收到空消息,所以會(huì)被卡住。
所以需要在客戶端上設(shè)置,用戶不可以發(fā)送為空的消息。
修改后的代碼:
import socket client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',8080)) #打電話 while True: message=input('數(shù)據(jù)>>').strip() if not message:continue client.send(message.encode('utf-8')) #發(fā)消息 recv=client.recv(1024) #收消息 print(recv)
加上鏈接循環(huán):
import socket #導(dǎo)入一個(gè)socket模塊 server1=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機(jī),基于socket的網(wǎng)絡(luò)通信,使用的是TCP協(xié)議 server1.bind(('127.0.0.1',8080)) #插卡,指定服務(wù)器的IP地址和端口號(hào) server1.listen(5) #開機(jī),設(shè)置監(jiān)聽等待數(shù) while True: conn,client_addr=server1.accept() #等待電話鏈接 while True: try: recv_data=conn.recv(1024) #收消息,限制單個(gè)消息的最大數(shù)為1024字節(jié)
if not recv_date:break print(recv_data) conn.send(recv_data.upper()) #發(fā)消息 except ConnectionResetError: break conn.close() #掛手機(jī) server1.close() #關(guān)機(jī)
前提是:客戶端單方面斷開鏈接
如果服務(wù)端是windows系統(tǒng),就添加一個(gè)try錯(cuò)誤處理,如果服務(wù)端是linux,就不會(huì)有異常,服務(wù)端一直在接受空,所以需要加一個(gè)判斷,如果為空就退出通信循環(huán)
命令的結(jié)果:如何獲取
os.system('命令'): 這種方法只是執(zhí)行了一下命令,并返回一個(gè)值。可以通過這個(gè)值,判斷命令是否執(zhí)行成功。
subprocess.Popen('命令',shell=True):這個(gè)方法是執(zhí)行的結(jié)果是內(nèi)存地址。它可以指定執(zhí)行的結(jié)果放到某個(gè)位置。
subprocess.Popen('命令',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
import subprocess res=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res_stdout=res.stdout.read() res_stderr=res.stderr.read() print(res_stdout.decode('gbk')) res_stdout1=res.stdout.read() print('jieguo',res_stdout1.decode('gbk')) #管道讀取一次以后,管道就為空了。因?yàn)槿∽吡恕?br>結(jié)果如下:
C:\Users\yangjianbo\AppData\Local\Programs\Python\Python36\python.exe C:/Users/yangjianbo/PycharmProjects/untitled/第八課面向?qū)ο蟾呒?jí)/subprocess模塊.py
驅(qū)動(dòng)器 C 中的卷是 Windows
卷的序列號(hào)是 58DC-A9CB
C:\Users\yangjianbo\PycharmProjects\untitled\第八課面向?qū)ο蟾呒?jí) 的目錄
2017/11/08 22:10 <DIR> .
2017/11/08 22:10 <DIR> ..
2017/11/05 12:25 19 b.py
2017/11/08 21:35 355 socket客戶端.py
2017/11/08 21:35 764 socket服務(wù)器.py
2017/11/08 22:10 417 subprocess模塊.py
2017/11/05 12:10 0 __init__.py
2017/11/08 21:50 <DIR> __pycache__
2017/11/07 23:32 352 客戶端2.py
2017/11/07 23:32 352 客戶端3.py
2017/11/07 23:32 352 客戶端4.py
2017/11/07 23:32 352 客戶端5.py
2017/11/07 23:32 352 客戶端6.py
2017/11/07 23:34 352 客戶端7.py
11 個(gè)文件 3,667 字節(jié)
3 個(gè)目錄 458,471,485,440 可用字節(jié)
第二次讀取管道的命令結(jié)果==========》
可以看到結(jié)果:第一次的結(jié)果是正確執(zhí)行命令后的結(jié)果,第二次的命令執(zhí)行結(jié)果就為空了。
命令執(zhí)行錯(cuò)誤的結(jié)果,會(huì)放到stderr的管道中。
import subprocess res=subprocess.Popen('啊啊啊',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res_stdout=res.stdout.read() res_stderr=res.stderr.read() print(res_stdout.decode('gbk')) res_stdout1=res.stdout.read() print('第二次讀取管道的命令結(jié)果==========》',res_stdout1.decode('gbk')) print(res_stderr.decode('gbk')) 結(jié)果:
C:\Users\yangjianbo\AppData\Local\Programs\Python\Python36\python.exe C:/Users/yangjianbo/PycharmProjects/untitled/第八課面向?qū)ο蟾呒?jí)/subprocess模塊.py
第二次讀取管道的命令結(jié)果==========》
'啊啊啊' 不是內(nèi)部或外部命令,也不是可運(yùn)行的程序
或批處理文件。
stdin參數(shù):用來把第一個(gè)命令執(zhí)行的結(jié)果輸入到第二個(gè)命令中。
簡(jiǎn)單SSH程序:
服務(wù)端代碼:
import socket #導(dǎo)入一個(gè)socket模塊 import subprocess server1=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機(jī),基于socket的網(wǎng)絡(luò)通信,使用的是TCP協(xié)議 server1.bind(('127.0.0.1',8080)) #插卡,指定服務(wù)器的IP地址和端口號(hào) server1.listen(5) #開機(jī),設(shè)置監(jiān)聽等待數(shù) while True: conn,client_addr=server1.accept() #等待電話鏈接 while True: try: cmd=conn.recv(1024) #收消息,限制單個(gè)消息的最大數(shù)為1024字節(jié) if not cmd :break cmd=cmd.decode('utf-8') cmd_res=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout_res=cmd_res.stdout.read() stderr_res=cmd_res.stderr.read() # 返回的結(jié)果就是bytes類型,所以不需要再decode了。 conn.send(stdout_res+stderr_res) #發(fā)消息 except ConnectionResetError: break conn.close() #掛手機(jī) server1.close() #關(guān)機(jī)
客戶端代碼:
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080)) #打電話
while True:
cmd=input('數(shù)據(jù)>>').strip()
if not cmd:continue
client.send(cmd.encode('utf-8')) #發(fā)消息
recv=client.recv(1024).decode('gbk') #收消息 因?yàn)檩敵鲈趙indows系統(tǒng)下,所以客戶端的編碼應(yīng)該為gbk.
print(recv)
client.close()
關(guān)于SSH遠(yuǎn)程程序執(zhí)行cd命令的問題:
以一個(gè)變量為標(biāo)準(zhǔn),當(dāng)執(zhí)行cd命令的時(shí)候,把這個(gè)變量的值加上你要切換的目錄,這才是整個(gè)完整的路徑。
關(guān)于SSH遠(yuǎn)程程序執(zhí)行top命令的問題:
時(shí)時(shí)刷新top命令,其實(shí)是服務(wù)端在定期刷新一下,返回結(jié)果給客戶端。不可能實(shí)現(xiàn)實(shí)時(shí)刷新的效果,肯定是在定期刷新的。
1024: 收包的大小限制,最大為1024字節(jié)。只是限制收消息的大小。這個(gè)值最大設(shè)置為8096,再大就沒有必要了。
解決方法:
把客戶端接收的包大小調(diào)大,但是這種方法不合適,因?yàn)闊o法預(yù)測(cè)服務(wù)端返回的結(jié)果大小。
而且收到的數(shù)據(jù)都存放在內(nèi)存中,太消耗資源了。
TCP粘包現(xiàn)象:
TCP為了避免網(wǎng)絡(luò)中的大量小包,會(huì)使用一個(gè)nagle算法,將包粘在一起發(fā)送。數(shù)據(jù)量小,時(shí)間間隔小,TCP會(huì)發(fā)生粘包現(xiàn)象。
為了解決粘包的問題,協(xié)議會(huì)采用一種固定的方式:報(bào)頭+數(shù)據(jù)
struct模塊:
把數(shù)字轉(zhuǎn)換成bytes類型。
struct.pack('i',后面的數(shù)字) i代表打包后的結(jié)果是4個(gè)bytes,打包的數(shù)字就是整型數(shù)字。
struct.unpack('i',后面的數(shù)字)
最終版:
制作報(bào)頭
header_dic={}
序列化:
打包報(bào)頭
把報(bào)頭大小打包成固定長(zhǎng)度。

浙公網(wǎng)安備 33010602011771號(hào)