您的位置:首页 > 编程语言 > Lua

Lua 学习笔记(七) —— 协同程序(三)

2014-10-20 09:22 288 查看
上篇讲协同程序,现在一起学习一个很有趣的例子,通过HTTP下载几个远程文件。首先我们确定如何下载单个文件,然后就是多个文件下载确定是使用顺序一个一个下载还是并发下载。

一、下载单个文件。(本文从环球网协会下载《HTML3.2参考规范》:http://www.w3.org/TR/REC-html32)

require "socket" --加载LuaSocket库

host = "www.w3.org"
file = "/TR/REC-html32.html" --定义主机和下载文件

function download(host,file)
local c = assert(socket.connect(host,80)) --打开一个TCP连接,连接到该站点的80端口
local count = 0 -- 计算字符个数
c:send("GET "..file.." HTTP/1.0\r\n\r\n") --发送请求
while true do
local s,status,partial = c:receive() --接收文件
count = count + #(s or partial)
if status == "closed" then
break
end
end
c:close() --关闭连接
print(file,count) -- 输出文件名和接收到的字符个数
end

download(host,file) --调用主函数
运行结果:



如果你出现这样的结果:



是由于请求命令格式出错。



也就是GET后面应该有个空格,HTTP前面应该有个空格。这是HTTP协议规定的。

现在来总结一下用HTTP下载远程文件的流程好了。

打开TCP连接 ——> 发送请求 ——> 接收文件 ——> 关闭请求

二、下载多个文件方式的确定

如果是按顺序下载的话,优点是思路很清晰,缺点是需要等待一个下载完才能下载另一个,如果下载过程中出现接收超时等问

题,我们就必须等待直至接收完毕,这样很耗时。第二种是并发下载,如果出现哪个文件接收超时或者其他情况,则马上挂起

程序,下载下一个文件;缺点是耗CPU资源略多。所以用第二种方法。

require "socket"
host = "www.w3.org"

function download(host,file) -- 下载单个文件
local c = assert(socket.connect(host,80))
local count = 0
c:send("GET "..file.." HTTP/1.0\r\n\r\n")
while true do
local s,status,partial = receive(c) --这个receive与上面的不同,这里是自己写的函数
count = count + #(s or partial)
if status == "closed" then
break
end
end
c:close()
print(file,count)

end

function receive (connection) --接收函数
connection:settimeout(0)
local s,status,partial = connection:receive(2^10)
if status == "timeout" then --接收超时,挂起本程序
coroutine.yield(connection)
end
return s or partial , status
end

threads = {} -- table

function get(host,file) -- 创建协同程序
local co = coroutine.create(function()
download(host,file)
end)
table.insert(threads,co) --将协同程序加入threads table中
end

function dispatch() --主调用函数
local i = 1
while true do
if threads[i] == nil then --如果table中的第i个程序执行完毕了,即从本table中移除了,则从头开始判断
if threads[1] == nil then --第一个数据也为空,表示此table中没有数据了,即4个文件已经下载成功了
break
end
i = 1
end
local status,res = coroutine.resume(threads[i])如果第i个数据 --没有移除,则再次启动该程序
if not res then -- 数据接收完毕,移除本程序,table中下一个会向前移动一个
table.remove(threads,i)
else
i = i+1 -- 接收出现问题,没有完成,下个程序继续判断
end
end
end

get(host,"/TR/html401/html40.txt")
get(host,"/TR/2002/REC-xhtml1-20020801/xhtml1.pdf")
get(host,"/TR/REC-html32.html")
get(host,"/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")

dispatch() --调用主程序
执行结果:



总结一下流程:

1.首先启动四个协同程序:

get(host,"/TR/html401/html40.txt")
get(host,"/TR/2002/REC-xhtml1-20020801/xhtml1.pdf")
get(host,"/TR/REC-html32.html")
get(host,"/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")
如果它们中有谁在接收过程中遇到问题,即接收超时,就挂起该程序。

2.调用dispatch()函数

循环的查找这四个协同程序谁被挂起了,然后再次启动该程序。成功下载后就从threads 中移除。这里需要注意table中移除一个

数据后,下一个数据就会向前移动一个位置来填充这个空缺。例如:

buffer = {1,2,3,4} --创建table
table.remove(buffer,1)
for i = 1, 4 do
print(buffer[i])
end
这里移除了第一个数据,即1被移除了,遍历此table,结果如下:



三、总结:其实这个下载多个文件的例子,就是非抢先试的多线程问题的解决办法了。协同程序提供了一种协作式的多线程,每个协同程序都等于是一个线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: