day 29
半連接與粘包問題
半連接數(shù)
1、定義:
三次握手沒有完成的稱之為半連接數(shù)
2、產生半連接的原因:
1)惡意客戶端故意不返回第三次握手信息,服務器就處于time_wait狀態(tài)
洪水攻擊用的就是這種原理
2)服務器沒有時間處理你的握手請求
3、最大半連接數(shù)
在socket語法中l(wèi)isten()函數(shù)的括號中指定的就是最大半連接數(shù)
最大半連接數(shù)指的是同一時間接收請求的最大數(shù)目,超過的請求會被直接拒絕
粘包問題
粘包問題只存在于TCP協(xié)議中,TCP協(xié)議又稱為流式協(xié)議,數(shù)據(jù)之間是沒有分隔的(只能用數(shù)據(jù)的長度來分隔他們)
1、粘包的定義
如果一次讀取指定緩存區(qū)的內容大于或者小于真實數(shù)據(jù)的大小,就被定義被粘包
2、粘包產生的原因
發(fā)送端和接收端都會造成數(shù)據(jù)的粘包問題
--發(fā)送端
1)發(fā)送的數(shù)據(jù)小,并且時間間隔短時,tcp會根據(jù)negal優(yōu)化算法把這兩個數(shù)據(jù)一起發(fā)送,會粘
--接收端
1)接收端一次性讀取了兩次的數(shù)據(jù)內容,會粘
2)接收端一次沒有把數(shù)據(jù)接收完整,剩余內容和下次發(fā)送的也會粘在一起
3、粘包的解決方案
無論時哪種情況,其根本的原因都在于接收端不知道應該接收多少數(shù)據(jù),所以解決的方案就是先把數(shù)據(jù)長度發(fā)給接收端
--發(fā)送端
1)使用struct將真實數(shù)據(jù)的長度轉換成固定的字節(jié)數(shù)據(jù)
2)發(fā)送長度數(shù)據(jù)
3)發(fā)送真實數(shù)據(jù)
import socket import struct client = socket.socket() client.connect(('127.0.0.1',8080))while True:data = '巴拉巴拉一大堆數(shù)據(jù)'.encode('utf-8')data_len = struct.pack('q',len(data)) # 把數(shù)據(jù)長度轉成固定字節(jié)數(shù)client.send(data_len) # 先發(fā)送固定字節(jié)數(shù) 'q'模式的字節(jié)數(shù)是8client.send(data) # 再發(fā)送真實的數(shù)據(jù)--接收端
1)先收長度數(shù)據(jù)? 字節(jié)數(shù)固定
2)再收真實數(shù)據(jù) ,真實可能很長,需要循環(huán)接收
import socket import struct server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen()while True:client,addr = server.accept()while True:data_len_bytes = client.recv(8) # 先接收傳過來的數(shù)據(jù)長度信息的固定字節(jié)'q'模式是8if not data_len_bytes:breakdata_len = struct.unpack('q',data_len_bytes)[0] # 把接收到的二進制轉成數(shù)據(jù)的長度,返回一個元組,取0號位data = client.recv(data_len) # 再根據(jù)長度來接收數(shù)據(jù)(循環(huán)接收請看前面)?
自定義報頭
1、需要原因
有時候我們除了需要把數(shù)據(jù)的長度告訴接收方,還需要把一些其他信息也告訴接收方,例如我們下載文件,
服務器就需要把文件名也給我們傳過來,這樣就需要我們自定義報頭
2、報頭形式
當我們想把數(shù)據(jù)的其他信息傳給對面的時候,字典是我們最好的選擇
對面和你的平臺可能不一樣,這樣我們就需要json格式來傳輸數(shù)據(jù),所以報頭的本質就是json數(shù)據(jù)
想把json數(shù)據(jù)發(fā)過去,又需要先把json數(shù)據(jù)的長度發(fā)過去,真實數(shù)據(jù)的信息都保存在json數(shù)據(jù)中
3、有了報頭之后的發(fā)送流程
--發(fā)送端
1)發(fā)送報頭長度
2)發(fā)送報頭數(shù)據(jù)? ?其中包含了真實文件的長度和其他的任意額外信息
3)發(fā)送真實文件內容
import socket,struct,json client = socket.socket() client.connect(('127.0.0.1',8080))while True:data = "我是一個很大的文件,并且對面要根據(jù)我的名字保存"# 先寫出報頭信息head_info = {"name":"xxx","size":10000}# 把報頭信息轉換成json格式json_head = json.dumps(head_info)# 把json格式的報頭信息的二進制長度轉換成固定字節(jié)json_head_lens = struct.pack('q',len(json_head.encode('utf-8')))# 發(fā)送固定字節(jié),讓多方知道該以多少長度接收報頭信息client.send(json_head_lens)# 發(fā)送報頭信息,里面包含接下來要發(fā)送的真實數(shù)據(jù)的大小以及名字等等client.send(json_head.encode('utf-8'))# 發(fā)送真實數(shù)據(jù)client.send(data.encode('utf-8'))--接收端
1)接收報頭長度
2)接收報頭信息
3)接收文件內容
import socket import struct import json server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen()while True:client,addr = server.accept()while True:head_len_bytes = client.recv(8)# 先把固定字節(jié)數(shù)的報頭長度接收過來if not head_len_bytes:breakhead_len = struct.unpack('q',head_len_bytes)[0]# 轉換成報頭的長度數(shù)據(jù)json_str = client.recv(head_len).decode('utf-8')# 接收報頭的信息,傳過來的是json格式head_info = json.loads(json_str)# 轉成字典格式,方便取值#head_info = {"name":"xxx","size":10000}file_size = head_info.get("size")# 從字典中取出真實文件的大小,根據(jù)大小取值,循環(huán)取值請看之前的data = client.recv(file_size)轉載于:https://www.cnblogs.com/huikejie/p/10966090.html
總結
- 上一篇: Qt在控制台输出中文的解决办法(转载)
- 下一篇: spring注解配置quartz