【Skynet】文件傳輸升級版

上一個socket文件傳輸過於簡單,師傅讓我進一步改進,定義包結構,讓文件的傳輸過程更加自由。該實現可以參考./examples/client.lua ,這裏面有數據的打包與解包。


這裏我設計了一個簡易的報文結構。


Length:即Type+Content的長度。

Type:將報文分成兩種,一種爲信息報文,包含文件名及文件大小,以字符串"01"表示,一種爲數據報文,以字符串"02"表示。

Content:即報文內容。


客戶端(打包):

package.cpath = "luaclib/?.so"
package.path = "lualib/?.lua;examples/?.lua"

local socket = require "clientsocket"
local host = "127.0.0.1"
local port = 8888
local path = "/home/mick/"
local filename = "bird"
local blocksize = 65530					#注意報文長度最大不超過65535
local data
local data_len = 0

local socket_fd = socket.connect(host, port)

local function send_package(pack, pack_type)		#打包函數,在報文內容前添加兩字節報文類型,兩字節長度
	pack = pack_type .. pack
	pack = string.pack(">s2", pack)
	socket.send(socket_fd, pack)	
end

print(path .. filename)
local file = io.open(path .. filename, "rb")
local len = file:seek("end") 				#seek("end")將光標跳到文件末尾,需使用seek("set")回到文件首
file:seek("set")

local time
local remain
time = math.modf(len / blocksize) + 1
remain = len % blocksize

data = filename .. '\n' .. tostring(len)
print(len)
send_package(data, "01")
while time > 0 do
	if time > 1 then
		time = time - 1
		data = file:read(blocksize)
		data_len = data_len + #data
		send_package(data, "02")
		print("send:", data_len)
	else
		time = time - 1
		data = file:read(remain)
		data_len = data_len + #data
		send_package(data, "02")
		print("send:", data_len)
	end
end


io.close(file)
socket.close(socket_fd)



服務器(解包):

local skynet = require "skynet"
local socket = require "socket"

local path = "/home/mick/"
local fileName = ""
local fileLen = 0
local data = ""
local remain = 0

local function unpack_package(pack)
	local size = #pack
	--print("size:", size)

	if remain == 0 then					#remain是出於如果之前解析的數據報文的內容尚未收集完畢,可以接着收集
		if size < 4 then
			print ("1111")
			return false, pack
		end
		local len = pack:byte(1) * 256 + pack:byte(2)
		--print("len:", len)
		local pack_type = pack:sub(3, 4)

		if size < len + 2 then
			print("2222")
			if pack_type == "02" then
				remain = len - (size - 2)
				data = data .. pack:sub(5)
				print("==", #data, fileLen, "remain:", remain)
				return false, ""
			end
			return false, pack
		end
		if pack_type == "01" then 
			local index = pack:find("\n", 5)	
			--print("index: ", index)
			fileName = pack:sub(5, index - 1) .. "_copy"
			for i = index + 1, 2 + len do
				--print("i:", i)
				fileLen = fileLen * 10 + tonumber(pack:sub(i, i))
			end
			--print("fileLen:", fileLen)
		elseif pack_type == "02" then
			data = data .. pack:sub(5, 2 + len)
			--print(data)
			print("==", #data, fileLen)
		end

		if #data == fileLen then						#數據收集完畢,退出循環
			return true, nil
		end
		return false, pack:sub(3 + len)
	else
		data = data .. pack:sub(1, remain)
		local remain_old = remain
		remain = remain - #pack:sub(1, remain)
		print("data:", #data, "remain:", remain)

		if #data == fileLen then
			return true, nil
		end
		return false, pack:sub(remain_old + 1)
	end
	
end

local function recv_package(fd, addr)
	socket.start(fd)
	fileName = ""
	fileLen = 0
	data = ""

	local last = ""
	local result = false
	while true do
		result, last = unpack_package(last)
		if result then
			break
		end
		local r = socket.read(fd)		
		if r then
			last = last .. r
		else
			print("no data!")
		end
	end
	
	print("Over")
	local file = io.open(path .. fileName, "wb")
	file:write(data)
	io.close(file)
	socket.close(fd)
end

skynet.start(function()
	local address = "0.0.0.0"
	local port = 8888
	socket_listen = socket.listen(address, port)
	socket.start(socket_listen, recv_package)
end)



有了打包過程,我們就能區分信息報文及數據報文,就可以將這兩種報文穿插着發送。最後帶所有數據收齊,寫入新文件。

發佈了129 篇原創文章 · 獲贊 47 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章