上一個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)
有了打包過程,我們就能區分信息報文及數據報文,就可以將這兩種報文穿插着發送。最後帶所有數據收齊,寫入新文件。