在上一篇socket實(shí)現(xiàn)簡(jiǎn)單ssh客戶端中然遏,有的時(shí)候會(huì)出現(xiàn)這個(gè)錯(cuò)誤
這是因?yàn)殚L(zhǎng)度和字符串一起發(fā)送了苦始,長(zhǎng)度在轉(zhuǎn)整形的時(shí)候里面包含了字符串所出現(xiàn)的錯(cuò)誤坎匿,這是由于數(shù)據(jù)傳輸過(guò)程中出現(xiàn)粘包導(dǎo)致的年扩,兩條send語(yǔ)句緊挨著蚁廓,導(dǎo)致數(shù)據(jù)一起發(fā)送過(guò)去了
兩個(gè)send語(yǔ)句緊挨:
# 發(fā)送返回值長(zhǎng)度
conn.send(str(len(response.encode())).encode()) # len里面的response也要加一個(gè).encode()
# 發(fā)送返回值
conn.send(response.encode())
那我們應(yīng)該如何避免這種情況的發(fā)生呢?
可以看到厨幻,一共發(fā)送兩次數(shù)據(jù)相嵌,我們可以在第一次發(fā)送數(shù)據(jù)是讓客戶端隨便返回一些數(shù)據(jù),然后讓服務(wù)器在第一次發(fā)送完數(shù)據(jù)之后接受一次數(shù)據(jù)當(dāng)作第一次數(shù)據(jù)發(fā)送成功的確認(rèn)况脆,也就是:
1.在客戶端第一次接收到數(shù)據(jù)大小之后立刻給服務(wù)器發(fā)送一次任意簡(jiǎn)短數(shù)據(jù)當(dāng)作確認(rèn)信息
2.在服務(wù)器發(fā)送完第一次數(shù)據(jù)之后等待收取一次確認(rèn)信息饭宾,收到確認(rèn)信息后再進(jìn)行第二次的數(shù)據(jù)發(fā)送
服務(wù)器端
import socket
import os
# 生成socket實(shí)例
server = socket.socket()
# 綁定端口
server.bind(('localhost', 6969))
# 監(jiān)聽(tīng)端口
server.listen()
# 等待鏈接
conn, addr = server.accept()
# 進(jìn)入循環(huán)
while True:
print('等待指令')
# 接收指令
data = conn.recv(1024)
# 判斷data是否存在
if not data:
print('斷開(kāi)')
break
# os模塊獲取指令
response = os.popen(data.decode()).read() # 獲得返回 接受字符串,返回結(jié)果也是字符串
# popen無(wú)法獲得錯(cuò)誤返回值格了,所以加一步判斷
if len(response) == 0:
response = 'Error'
# 發(fā)送返回值長(zhǎng)度
conn.send(str(len(response)).encode()) # 如果在win下len里面的response就必須也要加一個(gè)encode()
# 放置粘包
conn.recv(1024)
# 發(fā)送返回值
conn.send(response.encode())
print('send done')
server.close()
客戶端
import socket
# 生成socket實(shí)例
client = socket.socket()
# 鏈接指定ip端口
client.connect(('localhost', 6969))
# 進(jìn)入循環(huán)
while True:
# 獲得指令
cmd = input('請(qǐng)輸入指令:').strip()
# 指令為空繼續(xù)等待輸入
if len(cmd) == 0:
continue
# 發(fā)送指令
client.send(cmd.encode())
# 獲得返回值長(zhǎng)度
res_size = client.recv(1024).decode()
# 發(fā)送確認(rèn)收到信息看铆,防治粘包
client.send(b'111')
print(res_size)
# 字符串轉(zhuǎn)整型
i_res_size = int(res_size)
# 一次收1024,判斷收多少次盛末,每收一次用總長(zhǎng)度減去收到的長(zhǎng)度弹惦,只要剩余長(zhǎng)度大于0就一直收
while i_res_size > 0:
# 收取返回值,這里雖然寫(xiě)的1024但是每次收的有可能小于1024
data = client.recv(1024)
# 減去這一次收取的長(zhǎng)度
i_res_size -= len(data) # 這里不能-1024 因?yàn)閞ecv不一定每次都收1024 有可能小于1024
# 打印返回值
print(data.decode())
# 假如剩余長(zhǎng)度小于等于0了說(shuō)明收完了
else:
# 打印收取返回值的總長(zhǎng)度
print('response recv done...', res_size)
客戶端服務(wù)器端各加一行悄但,就能有效的避免粘包的出現(xiàn)肤频,粘包的情況會(huì)在數(shù)據(jù)傳輸中經(jīng)常出現(xiàn),所以要記住這個(gè)方法
轉(zhuǎn)載請(qǐng)注明出處
python自學(xué)技術(shù)互助扣扣群:670402334