您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何在Python中解決Socket與ScoketServer的通信問題,億速云小編覺得不錯,現(xiàn)在分享給大家,也給大家做個參考,一起跟隨億速云小編來看看吧!
Python主要應用于:1、Web開發(fā);2、數(shù)據(jù)科學研究;3、網(wǎng)絡(luò)爬蟲;4、嵌入式應用開發(fā);5、游戲開發(fā);6、桌面應用開發(fā)。
Socket有一個緩沖區(qū),緩沖區(qū)是一個流,先進先出,發(fā)送和取出的可自定義大小的,如果取出的數(shù)據(jù)未取完緩沖區(qū),則可能存在數(shù)據(jù)怠慢。其中【recv(1024)】表示從緩沖區(qū)里取最大為1024個字節(jié),但實際取值大小是不確定的,推薦其值小于等于8192。
黏包問題:
Socket發(fā)送兩條連續(xù)數(shù)據(jù)時,可能最終會拼接成一條進行發(fā)送
解決方法一:
兩條數(shù)據(jù)間進行延時發(fā)送,如【tiem.sleep(0.5) #延時0.5s】
解決方法二:
每次發(fā)送后等待對方確認接收信息數(shù)據(jù),發(fā)送一條后就立即接收等待
解決方法三:
設(shè)定接收數(shù)據(jù)大小,發(fā)送端每次發(fā)送需要發(fā)送的數(shù)據(jù)的數(shù)據(jù)大小,接收端通過設(shè)置【recv(xx)】只接收確定的大小
Socket基本使用:
簡單的服務器:
import socket sser=socket.socket()#得到socket對象 sser.bind(("0.0.0.0",2699))#建立監(jiān)聽 sser.listen(3) print("等等客戶端連接") conn,addr=sser.accept() #等待連接,會一直處于阻塞,返回連接對象和對方地址 print("有客戶端已經(jīng)連接,IP地址和端口為:",addr) #接收數(shù)據(jù),會一直阻塞,建議低于8192 #使用連接對象操作 rdata=conn.recv(1024) print(rdata.decode("gbk")) conn.send("服務器返回,收到數(shù)據(jù)".encode("gbk"))#發(fā)送數(shù)據(jù),使用連接對象操作 #關(guān)閉連接 sser.close()
簡單的客戶端:
import socket sclient=socket.socket()#得到socket對象 #連接服務器 #失敗會報錯:ConnectionRefusedError sclient.connect(("192.168.1.135",2699)) sclient.send("東小東".encode("gbk"))#發(fā)送數(shù)據(jù) #接收數(shù)據(jù),會一直阻塞,建議低于8192 rdata=sclient.recv(1024) print(rdata.decode("gbk")) #關(guān)閉連接 sclient.close()
Soket進階:
服務器進階:
實現(xiàn)客戶循環(huán)連接及數(shù)據(jù)循環(huán)收發(fā)和判斷客戶端是否斷開
import socket sser=socket.socket()#得到socket對象 sser.bind(("0.0.0.0",2699))#建立監(jiān)聽 sser.listen(3) while True: print("等等客戶端連接") conn,addr=sser.accept() #等待連接,會一直處于阻塞,返回連接對象和對方地址 print("有客戶端已經(jīng)連接,IP地址和端口為:",addr) conn.send(("服務器歡迎你:%s\r\n"%(str(addr))).encode("gbk")) # 發(fā)送數(shù)據(jù),使用連接對象操作 while True: #接收數(shù)據(jù),會一直阻塞 #使用連接對象操作 rdata=conn.recv(1024) if not rdata: break #判斷客戶端是否斷開,斷開則收到空數(shù)據(jù) print(rdata.decode("gbk")) conn.send("服務器返回,收到數(shù)據(jù)\r\n".encode("gbk"))#發(fā)送數(shù)據(jù),使用連接對象操作 #關(guān)閉連接 sser.close()
客戶端進階:
實現(xiàn)循環(huán)收發(fā)和判斷服務器是否斷開
import socket sclient=socket.socket()#得到socket對象 #連接服務器 #失敗會報錯:ConnectionRefusedError sclient.connect(("192.168.1.135",2699)) while True: #接收數(shù)據(jù),會一直阻塞 rdata=sclient.recv(1024) if not rdata: break # 判斷服務器是否斷開,斷開則收到空數(shù)據(jù) print(rdata.decode("gbk")) sclient.send(("客戶端收到數(shù)據(jù):%s\r\n"%rdata.decode("gbk")).encode("gbk")) # 發(fā)送數(shù)據(jù) #關(guān)閉連接 sclient.close()
注意:
發(fā)送數(shù)據(jù)不可發(fā)送空字符,否則會卡住,解決方法為判斷輸入的是否為空值,空值則進行數(shù)據(jù)發(fā)送
strx=input("輸入:").strip()#得到控制臺輸入值 if(len(strx)==0):continue #如果為空字符,則跳出本次循環(huán) print(strx) #打印 sclient.send(strx.encode("gbk")) # 發(fā)送數(shù)據(jù)
發(fā)送大數(shù)據(jù):
先發(fā)送總體數(shù)據(jù)大小,socket另一端判斷實際接收的數(shù)據(jù)大小與總數(shù)據(jù)大小進行比較,循環(huán)recv()進行數(shù)據(jù)接收
簡單的ssh實現(xiàn):
服務端:
import os import socket sser=socket.socket()#得到socket對象 sser.bind(("0.0.0.0",2697))#建立監(jiān)聽 sser.listen(3) while True: print("等等客戶端連接") conn,addr=sser.accept() #等待連接,會一直處于阻塞,返回連接對象和對方地址 print("有客戶端已經(jīng)連接,IP地址和端口為:",addr) conn.send(("服務器歡迎你:%s\r\n"%(str(addr))).encode("gbk")) # 發(fā)送數(shù)據(jù),使用連接對象操作 while True: #接收數(shù)據(jù),會一直阻塞 rdata=conn.recv(1024) if not rdata: break #判斷客戶端是否斷開,斷開則收到空數(shù)據(jù) sendtox=os.popen(rdata.decode("gbk")).read() #執(zhí)行命令 conn.send(str(len(sendtox)).encode("gbk")) #發(fā)送執(zhí)行命令的結(jié)果長度 #如果長度大于0 則發(fā)送命令結(jié)果數(shù)據(jù) if len(sendtox)>0: if conn.recv(10).decode("gbk")=="1": conn.sendall(sendtox.encode("gbk"))#發(fā)送最終數(shù)據(jù) #關(guān)閉連接 sser.close()
客戶端:
import socket sclient=socket.socket()#得到socket對象 #連接服務器 #失敗會報錯:ConnectionRefusedError sclient.connect(("192.168.43.21",2697)) # 接收數(shù)據(jù),會一直阻塞 rdata = sclient.recv(1024) print(rdata.decode("gbk")) while True: strx = input("請輸入命令:").strip() # 得到控制臺輸入值 if (len(strx) == 0): continue # 如果為空字符,則跳出本次循環(huán) sclient.send(strx.encode("gbk")) # 發(fā)送數(shù)據(jù) rdata = sclient.recv(10) if not rdata: break # 判斷服務器是否斷開,斷開則收到空數(shù)據(jù) #判斷命令是否執(zhí)行成功,0為失敗 dataall=int(rdata.decode("gbk")) if dataall==0: continue sclient.send("1".encode("gbk")) # 發(fā)送確認接收數(shù)據(jù)命令 #循環(huán)接收數(shù)據(jù) datanew=0 while dataall !=datanew: rdata=sclient.recv(1024).decode("gbk") datanew+=len(rdata) print(rdata) #關(guān)閉連接 sclient.close()
發(fā)送文件:
發(fā)送文件,使用read()讀取文件數(shù)據(jù)后,可循環(huán)調(diào)用send()發(fā)送數(shù)據(jù),或者使用sendall()一次性發(fā)送所有數(shù)據(jù),socket另一端接收可循環(huán)recv()進行數(shù)據(jù)接收,且每次接收的數(shù)據(jù)大小是不確定的。文件傳輸需要驗證發(fā)送和接受的數(shù)據(jù)是否完全一致,可以通過數(shù)據(jù)大小加md5雙重驗證,發(fā)送端:md5在每次發(fā)送一條數(shù)據(jù)時進行update(),在數(shù)據(jù)發(fā)送完成后再發(fā)送md5值;接受端:md5在每次接收到一條數(shù)據(jù)后進行update(),在文件接收完成后再接收發(fā)送端發(fā)送的md5值,將兩值進行比較,相同則表示傳輸無丟包,但加入md5校驗,將會影響傳輸速率。
發(fā)送文件數(shù)據(jù):先發(fā)送總體數(shù)據(jù)大小,socket另一端判斷實際接收的數(shù)據(jù)大小與總數(shù)據(jù)大小進行比較,循環(huán)recv()進行數(shù)據(jù)接收
示例:客戶端發(fā)送文件名,服務器判斷文件是否存在,如果不存在或者是空文件則不進行傳輸,服務器進行文件發(fā)送,客戶端實現(xiàn)文件接收
服務器
import os import socket sser=socket.socket()#得到socket對象 sser.bind(("0.0.0.0",2697))#建立監(jiān)聽 sser.listen(3) while True: print("等等客戶端連接") conn,addr=sser.accept() #等待連接,會一直處于阻塞,返回連接對象和對方地址 print("有客戶端已經(jīng)連接,IP地址和端口為:",addr) conn.send(("服務器歡迎你:%s\r\n"%(str(addr))).encode("gbk")) # 發(fā)送數(shù)據(jù),使用連接對象操作 while True: #接收數(shù)據(jù),會一直阻塞 rdata=conn.recv(1024) if not rdata: break #判斷客戶端是否斷開,斷開則收到空數(shù)據(jù) filesize=0 filenamex=rdata.decode("gbk") if os.path.isfile(filenamex): #判斷是否是文件 filesize=os.stat(filenamex).st_size #得到文件大小 conn.send(str(filesize).encode("gbk")) #不是文件則發(fā)送0,是文件則是實際大小 #如果文件大小大于0 則發(fā)送文件 if filesize>0: if conn.recv(10).decode("gbk")=="1": #等待確認接收命令 #一行一行發(fā)送數(shù)據(jù) f=open(filenamex,"rb") for linex in f: conn.sendall(linex)#發(fā)送最終數(shù)據(jù) #關(guān)閉連接 sser.close()
客戶端
import socket sclient=socket.socket()#得到socket對象 #連接服務器 #失敗會報錯:ConnectionRefusedError sclient.connect(("192.168.43.21",2697)) # 接收數(shù)據(jù),會一直阻塞 rdata = sclient.recv(1024) print(rdata.decode("gbk")) while True: filenamex = input("請輸入文件名:").strip() # 得到控制臺輸入值 if (len(filenamex) == 0): continue # 如果為空字符,則跳出本次循環(huán) sclient.send(filenamex.encode("gbk")) # 發(fā)送數(shù)據(jù) rdata = sclient.recv(10) if not rdata: break # 判斷服務器是否斷開,斷開則收到空數(shù)據(jù) #判斷命令是否執(zhí)行成功,0為失敗 dataall=int(rdata.decode("gbk")) if dataall==0: print("文件不存在或者為空") continue sclient.send("1".encode("gbk")) # 發(fā)送確認接收數(shù)據(jù)命令 #打開文件 f=open(filenamex,"wb") #循環(huán)接收數(shù)據(jù) datanew=0 while dataall !=datanew: rdata=sclient.recv(1024) datanew+=len(rdata) f.write(rdata) print("文件(%s)接收完畢"%filenamex) f.close() #關(guān)閉文件 #關(guān)閉連接 sclient.close()
ScoketServer
服務端實現(xiàn)多并發(fā)效果,可以同時接入多個客戶端
import socketserver #建立一個類,必須繼承 socketserver.BaseRequestHandler 類 class DongSocket(socketserver.BaseRequestHandler): #必須重寫handle方法 def handle(self): print("建立新連接,對方地址為:{}".format(self.client_address)) while True: try: self.datax=self.request.recv(1024).decode("gbk") #接收數(shù)據(jù) print("接收的數(shù)據(jù)為:%s"%self.datax) self.request.send(("服務器返回數(shù)據(jù):%s"%self.datax).encode("gbk")) except Exception as e: print("斷開,再見:{}".format(self.client_address)) break #參數(shù):(("ip",端口),自定義類) #ss=socketserver.TCPServer(("0.0.0.0",2351),DongSocket) #與之前的socket服務器效果一致,同時只能連接一個客戶端 ss=socketserver.ThreadingTCPServer(("0.0.0.0",2351),DongSocket) #同時可以連接多個客戶端,多并發(fā) ss.serve_forever()
以上就是億速云小編為大家收集整理的如何在Python中解決Socket與ScoketServer的通信問題,如何覺得億速云網(wǎng)站的內(nèi)容還不錯,歡迎將億速云網(wǎng)站推薦給身邊好友。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。