上一个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)
有了打包过程,我们就能区分信息报文及数据报文,就可以将这两种报文穿插着发送。最后带所有数据收齐,写入新文件。