1、什么緩沖區,為什么會有緩沖區?緩沖區是指socket通信時,收發命令時的一個中間存放命令的存儲空間因為數據被輸出后在處理的時候需要一定的時間,
為了輸入接着輸入零時差,就需要緩沖了,先預讀並處理一部分信息,然后開始輸出,在輸出的同時進行后面的的處理,然后等緩沖的部分輸出完后,另一部分的數據也處理完畢了,
就可以接着輸出了,如果沒有這個緩沖區,就會很卡輸入輸出的緩沖區一般是8k
tcp粘包演示(一):
import subprocess
ip_port=('127.0.0.1',8080)
BUFSIZE=1024
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
while True:
conn,addr=tcp_socket_server.accept()
print('客戶端>>>',addr)
while True:
cmd=conn.recv(BUFSIZE)
if len(cmd) == 0:break
res=subprocess.Popen(cmd.decode('gbk'),shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr=res.stderr.read()
stdout=res.stdout.read()
conn.send(stderr)
conn.send(stdout)
size = 1024
tcp_sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res = tcp_sk.connect(ip_port)
while True:
msg=input('>>: ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
tcp_sk.send(msg.encode('utf-8'))
act_res=tcp_sk.recv(size)
print('接收的返回結果長度為>',len(act_res))
print('std>>>',act_res.decode('gbk')) #windows返回的內容需要用gbk來解碼,因為windows系統的默認編碼為gbk
tcp粘包演示(二):發送端需要等緩沖區滿才發送出去,造成粘包(發送數據時間間隔很短,數據也很小,會合到一起,產生粘包)
server端代碼示例:(如果兩次發送有一定的時間間隔,那么就不會出現這種粘包情況,試着在兩次發送的中間加一個time.sleep(1))
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(10)
data2=conn.recv(10)
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()
client端代碼示例:
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# res=s.connect_ex(ip_port)
res=s.connect(ip_port)
s.send('hi'.encode('utf-8'))
s.send('meinv'.encode('utf-8'))
udp粘包演示:注意:udp是面向包的,所以udp是不存在粘包的
粘包的原因:主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的
粘包的解決方案
解決方案(一):
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(ip_port)
s.listen(5)
while True:
conn,addr=s.accept()
print('客戶端',addr)
while True:
msg=conn.recv(1024)
if not msg:break
res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
stdin=subprocess.PIPE,\
stderr=subprocess.PIPE,\
stdout=subprocess.PIPE)
err=res.stderr.read()
if err:
ret=err
else:
ret=res.stdout.read()
data_length=len(ret)
conn.send(str(data_length).encode('utf-8'))
data=conn.recv(1024).decode('utf-8')
if data == 'recv_ready':
conn.sendall(ret)
conn.close()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
s.send(msg.encode('utf-8'))
length=int(s.recv(1024).decode('utf-8'))
s.send('recv_ready'.encode('utf-8'))
send_size=0
recv_size=0
data=b''
while recv_size < length:
data+=s.recv(1024)
recv_size+=len(data)
print(data.decode('utf-8'))
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while True:
conn,addr=phone.accept()
while True:
cmd=conn.recv(1024)
if not cmd:break
print('cmd: %s' %cmd)
res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
print(err)
if err:
back_msg=err
else:
back_msg=res.stdout.read()
headers={'data_size':len(back_msg)}
head_json=json.dumps(headers)
head_json_bytes=bytes(head_json,encoding='utf-8')
conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度
conn.send(head_json_bytes) #再發報頭
conn.sendall(back_msg) #在發真實的內容
conn.close()
import struct,json
ip_port=('127.0.0.1',8080)
client=socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)
while True:
cmd=input('>>: ')
if not cmd:continue
client.send(bytes(cmd,encoding='utf-8'))
head=client.recv(4)
head_json_len=struct.unpack('i',head)[0]
head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
data_len=head_json['data_size']
recv_size=0
recv_data=b''
while recv_size < data_len:
recv_data+=client.recv(1024)
recv_size+=len(recv_data)
#print(recv_data.decode('utf-8'))
print(recv_data.decode('gbk')) #windows默認gbk編碼
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "127.0.0.1", 9999
# 設置allow_reuse_address允許服務器重用地址
socketserver.TCPServer.allow_reuse_address = True
# 創建一個server, 將服務地址綁定到127.0.0.1:9999
#server = socketserver.TCPServer((HOST, PORT),Myserver)
server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver)
# 讓server永遠運行下去,除非強制停止程序
server.serve_forever()
HOST, PORT = "127.0.0.1", 9999
data = "hello"
# 創建一個socket鏈接,SOCK_STREAM代表使用TCP協議
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT)) # 鏈接到客戶端
sock.sendall(bytes(data + "\n", "utf-8")) # 向服務端發送數據
received = str(sock.recv(1024), "utf-8")# 從服務端接收數據
print("Sent: {}".format(data))
print("Received: {}".format(received))
4、什么是粘包? socket 中造成粘包的原因是什么? 哪些情況會發生粘包現象?
TCP粘包是指發送方發送的若干包數據包到接收方接收時粘成一包,從接收緩沖區看,后一包數據的頭緊接着前一包數據的尾
(1)發送方原因
TCP默認會使用Nagle算法。而Nagle算法主要做這兩件事1>只有上一個分組得到確認,才會發送下一個分組;2>收集多個小分組,再確認到來時一起發送
所以是Nagle算法造成了發送方造成了粘包現象
(2)接收方原因
TCP接收到分組時,並不會立刻送至應用層處理,或者說,應用層並不會立即處理;實際上,TCP將收到的分組保存至接收緩存里,然后
應用程序主動從緩存里讀收到的分組。這樣一來,如果TCP接收分組的速度大於應用程序讀分組的速度,多個包就會存至緩存,應用程序
讀時,就會讀到多個首尾相接粘到一起的包
哪些情況會發生粘包現象?
(1)發送端需要等待緩沖區滿才發送出去,造成粘包
(2)接收方不及時接收緩沖區的包,造成多個包接收
5、用tcp協議下的socket,寫一個簡易的文件上傳下載的功能,用戶需要登陸認證,認證成功后,客戶端用戶可以選擇上傳或者是下載,
上傳的時候服務端提前設定好上傳文件的路徑,將文件上傳到對應的路徑下,下載文件的時候,服務端將之前設定好的上傳路徑中的所有
文件帶上序號展示給用戶看,用戶輸入文件序號后下載對應的文件,文件下載到客戶端程序的當前路徑下就可以了。