socket:socket就是實現服務器和客戶端數據的交換,服務器端接收併發送數據,客戶端發送並接收數據,而且須要注意的是,在python3中,socket值接收字節。由於客戶端在發送鏈接給服務器的時候,要轉換爲字節碼;服務器端在返回給客戶端的時候,也要轉換爲字節碼。python
以下所示:編程
服務器端:緩存
import socket,os server = socket.socket() server.bind(("localhost",9999)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客戶端已斷開!") break print("執行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(cmd_res.encode('utf-8')) print("send done") server.close()
上面是服務器端,使用os.popen()實現數據的處理,不過只能處理字符串,所以須要decode()成字符串格式,而後發送的時候要轉換爲字節碼,encode()。服務器
客戶端:網絡
import socket client = socket.socket() client.connect(("localhost",9999)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) '''服務器端發送爲空,客戶端是卡主的''' cmd_res = client.recv(1024) print(cmd_res.decode()) client.close()
首先啓動服務器端,而後啓動客戶端,以下所示:併發
服務器端發送數據:socket
>>:1 cmd has no output..... >>:dir build_server.py get_ip.py socket_server.py 客戶端建立過程.py class_method.py lib s_server.py 類的方法.py class的方法.py property屬性.py static_method.py 人類.py error_handle.py s_client.py 動態導入.py 上節內容 get_attr.py socket_client.py 反射.py >>:ls build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property屬性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 動態導入.py 反射.py 客戶端建立過程.py 類的方法.py 人類.py 上節內容
客戶端接收數據:ui
new conn: ('127.0.0.1', 55324) 執行指令: b'1' before send 0 send done /bin/sh: 1: 1: not found 執行指令: b'dir' before send 249 send done 執行指令: b'ls' before send 219 send done
當客戶端卡頓的時候,說明服務器端是沒有數據發送過來的,由於客戶端不能接收空的數據,服務器端也不能接收空的數據。這樣就會形成卡頓的狀況。spa
在接收的時候,會有一個緩存,緩存不滿的時候,是不會發送數據的,客戶端就接收不到數據。code
因爲緩存的存在,客戶端接收數據有時候接收不完整,如何避免呢?要求服務器告訴客戶端數據的大小,客戶端根據數據的大小來接收數據。
收發同樣大,告訴客戶端接收數據的大小,以下:
服務器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客戶端已斷開!") break print("執行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res)).encode('utf-8')) #先發送數據的大小給客戶端 第一個send()發送數據 time.sleep(1) #連續發送數據會形成粘包現象,所以要有區分,否則容易粘包,這裏讓程序休眠一秒,先另一個接收執行 conn.send(cmd_res.encode('utf-8')) #第二個send()發送數據 print("send done") server.close()
在服務器端上,咱們計算了發送給客戶端的數據大小,先把數據的大小告知客戶端,接收多大的數據,而後在發送真正的數據給客戶端。在數據發送的時候,要防止粘包,由於send()兩次同時發送,形成粘包的狀況,所以讓程序休眠一秒,time.sleep(1),讓客戶端先接收數據,過了一秒從新接收數據,這樣就不會粘包。
粘包狀況以下;
>>:1 命令結果大小: b'22cmd has no output.....' Traceback (most recent call last): File "/home/zhuzhu/第七天/s_client.py", line 15, in <module> while received_size < int(cmd_res_size.decode()): ValueError: invalid literal for int() with base 10: '22cmd has no output.....'
客戶端發送數據的時候,服務器返回的時候,因爲兩個send()同時發送數據形成粘包的狀況,會出現錯誤。兩條數據發送的時候鏈接到一塊兒,形成粘包。兩次數據發送連接到一塊兒,爲何形成粘包,是由於程序執行的太快,客戶端接收數據很快,速度趕在下一次發送以前。
客戶端:
import socket client = socket.socket() client.connect(("localhost",9998)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) '''服務器端發送爲空,客戶端是卡主的''' cmd_res_size = client.recv(1024) #接收命令結果的長度(服務器發送的) print("命令結果大小:",cmd_res_size) received_size = 0 while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) #每次收到的有可能小於1024,因此必須用len()判斷 print(data.decode()) print(received_size) else: print("cmd res receive done......",received_size) # cmd_res = client.recv(1024) # # print(cmd_res.decode()) client.close()
在客戶端中,咱們先接收服務器發來的數據的大小,而後開始接收數據,當數據長度小於接收長度時,繼續接收,一直等到沒有數據接收爲止。
在客戶端中,接收的數據不必定等於規定的長度。而且要統一格式,咱們知道,漢字和英文的長度是不一致的,漢字是由3個字節組成,而由於是由兩個字節組成的。
首先啓動客戶端,而後啓動服務器端,以下:
客戶端發送數據: >>:1 命令結果大小: b'22' cmd has no output..... 22 cmd res receive done...... 22 >>:ls 命令結果大小: b'275' build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property屬性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 動態導入.py 反射.py 客戶端建立過程.py 類的方法.py 人類.py 上節內容 275 cmd res receive done...... 275 >>:dir 命令結果大小: b'305' build_server.py get_ip.py socket_server.py 客戶端建立過程.py class_method.py lib s_server.py 類的方法.py class的方法.py property屬性.py static_method.py 人類.py error_handle.py s_client.py 動態導入.py 上節內容 get_attr.py socket_client.py 反射.py 305 cmd res receive done...... 305
從接收數據能夠看出,若是有中文的話,接收數據的長度是不一致的,就是因爲中文的字節是3個,所以都要轉換爲統一格式,如今調整服務器端,讓服務器發送數據的長度的時候也是按照字節的形式發送長度,以下:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客戶端已斷開!") break print("執行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8')) #先發送數據的大小給客戶端 time.sleep(1) #連續發送數據會形成粘包現象,所以要有區分,否則容易粘包,這裏讓程序休眠一秒,先另一個接收執行 conn.send(cmd_res.encode('utf-8')) print("send done") server.close()
從新啓動服務器,啓動客戶端發送數據,以下:
客戶端輸入指令: >>:1 命令結果大小: b'22' cmd has no output..... 22 cmd res receive done...... 22 >>:ls 命令結果大小: b'275' build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property屬性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 動態導入.py 反射.py 客戶端建立過程.py 類的方法.py 人類.py 上節內容 275 cmd res receive done...... 275 >>:dir 命令結果大小: b'305' build_server.py get_ip.py socket_server.py 客戶端建立過程.py class_method.py lib s_server.py 類的方法.py class的方法.py property屬性.py static_method.py 人類.py error_handle.py s_client.py 動態導入.py 上節內容 get_attr.py socket_client.py 反射.py 305 cmd res receive done...... 305
從上面能夠看出,當調整服務器端發送長度的方式以後,接收數據的長度和服務器告知客戶端的長度是一致的。所以,在接收和發送數據的時候要以字節碼方式統一計算長度,格式統一很重要,在socket中,計算長度統一格式爲:字節碼,發送和接收數據都是以字節碼的形式操做。
socket網絡編程,其實就是收發數據,只能收發字節的形式,要統一字節格式,要進行及時傳喚,要告知客戶端數據的大小。而後接收的時候,按照大小來接收。接收完成以後在繼續進行執行程序。
粘包:兩次發送數據粘到一塊兒。
如何解決粘包:
(1)sleep(),停留一秒,time.sleep(0.5);
服務器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9999)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客戶端已斷開!") break print("執行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8')) #先發送數據的大小給客戶端 time.sleep(1) #連續發送數據會形成粘包現象,所以要有區分,否則容易粘包,這裏讓程序休眠一秒,先另一個接收執行 conn.send(cmd_res.encode('utf-8')) print("send done") server.close()
服務器兩次發送數據的時間有間隔,這樣就能避免數據發送粘包的狀況。不過使用sleep()太low了。
(2)第一次發送以後,接收數據,接收客戶端發來的第一次數據發送接收成功的消息,進行第二次發送,這樣就能隔斷數據的發送
服務器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客戶端已斷開!") break print("執行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8')) #先發送數據的大小給客戶端 # time.sleep(1) #連續發送數據會形成粘包現象,所以要有區分,否則容易粘包,這裏讓程序休眠一秒,先另一個接收執行 clicent_ack = conn.recv(1024) #等待接收數據,讓下面的發送send()暫時不執行,要收到客戶端的數據大小進行響應。wait to confirm print("ack from client:",clicent_ack) conn.send(cmd_res.encode('utf-8')) print("send done") server.close()
服務器端,要想防止粘包,則在兩次發送數據直接增長一個第一次發送數據成功的確認,即接收成功發送數據的指令,如上面所示,conn.recv()其實也沒有什麼功能,就是隔斷第二次發送的執行時間,讓第二次發送數據在第一次執行完成以後再進行執行;
客戶端:
import socket client = socket.socket() client.connect(("localhost",9998)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode('utf-8')) '''服務器端發送爲空,客戶端是卡主的''' cmd_res_size = client.recv(1024) #接收命令結果的長度(服務器發送的) print("命令結果大小:",cmd_res_size) client.send("準備好接收了,loser,能夠發了".encode('utf-8')) received_size = 0 while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) #每次收到的有可能小於1024,因此必須用len()判斷 print(data.decode()) print(received_size) else: print("cmd res receive done......",received_size) # cmd_res = client.recv(1024) # # print(cmd_res.decode()) client.close()
客戶端上,數據接收完成以後,發送一條指令,即讓服務器接着發送第二條指令。第一條指令是發送數據的大小,第二條指令是發送數據;
上面就解決了粘包的狀況。
其實,socket在發送和就是數據的時候都是同步的,要想隔斷,只能進行成功驗證,先隔斷不讓後面send()執行,只有第一次發送成功以後,而且經過驗證,再來進行第二次執行。