一、保留关键字

大小写敏感。
Chunks:语句块(匿名函数),一条或多条语句、函数、几MB的块。

  1. -- 保留字
  2. and break do else elseif
  3. end false for function if
  4. in local nil not or
  5. true repeat return then until
  6. while
  7. --[[ 多行注释
  8. print(10) -- 单行注释
  9. --]]

命令行
lua [options] [script [args]]

  1. prompt>lua -e "print(math.sin(12))" #执行
  2. prompt>lua -i -e "_PROMPT=' lua> '" #进入交互模式
  3. #获取命令行参数:arg全局变量
  4. lua>lua -e "sin=math.sin" script a b
  5. #arg内容如下:
  6. #arg[-3] = "lua"
  7. #arg[-2] = "-e"
  8. #arg[-1] = "sin=math.sin"
  9. #arg[0] = "script"
  10. #arg[1] = "a"
  11. #arg[2] = "b"

二、数据类型

8种基本数据类型:nil、boolean、number、string、userdata、function、thread和table。
变量:没有类型限制,可以是任何值。type函数可查看变量类型:

  1. print(type("Hello world")) --> string
  2. print(type(10.4*3)) --> number
  3. print(type(print)) --> function
  4. print(type(type)) --> function
  5. print(type(true)) --> boolean
  6. print(type(nil)) --> nil
  7. print(type(type(X))) --> string
  8. print(type(a)) --> nil ('a' is not initialized)
  9. a = 10
  10. print(type(a)) --> number
  11. a = "a string!!"
  12. print(type(a)) --> string
  13. a = print -- yes, this is valid!
  14. a(type(a)) --> function

nil:只要一个值nil,全局变量默认值nil,当前仅当全局变量!=nil时,全局变量存在,赋值非nil时创建,赋值nil删除全局变量。
boolean:false,true。

  1. if a then -- afalse/nil时不执行,其他值则执行。所以0也执行。
  2. print "mother fucker"
  3. end

number:只有实数类型,没有整数,支持C/C++任何长整型。

  1. 4 0.4 4.57e-3 0.3e12 5e+20

string:字符串(相当于C的数组,不是C的字符串),不可修改,自动内存管理,可以1MB长度(数据描述是Lua的设计初衷)。和number自动类型转换。注意!lua的字符串不是\0空字符结尾,而是给定的长度,所以与Lua进行交互的时候,要特别注意。

  1. local str = 'asdfasdf' --单引号
  2. local str = "asdfasdf" --双引号
  3. local str = [[
  4. asdfasdf --可以多行,适合大型chunk如代码、数据文件。
  5. [[asdfasd]] --可以嵌套
  6. \n --原样输出,不转义
  7. ]]
  8. ---------------转义字符---------------
  9. \a bell
  10. \b back space -- 后退
  11. \f form feed -- 换页
  12. \n newline -- 换行
  13. \r carriage return -- 回车
  14. \t horizontal tab -- 制表
  15. \v vertical tab
  16. \\ backslash -- "\"
  17. \" double quote -- 双引号
  18. \' single quote -- 单引号
  19. \[ left square bracket -- 左中括号
  20. \] right square bracket -- 右中括号
  21. \ddd --ddd:三位十进制数字
  22. --\97 a
  23. --\10 \n
  24. --\049 1
  25. -- string和number“自适应地”自动类型转换
  26. print("10" + 1) --> 11
  27. print("10 + 1") --> 10 + 1
  28. print("-5.3e - 10" * "2") --> -1.06e-09
  29. print("hello" + 1) --> ERROR (cannot convert "hello")
  30. print(10 .. 20) --> 1020,字符串连接符,10后面必须空格防止解释错误。
  31. print(type(10)) --> number
  32. print(type("10")) --> string
  33. print(type(tostring(10))) --> string
  34. print(type(tonumber("10"))) --> number

function:第一类值(和number、string完全一样的使用特性),可以存储在变量,可以函数参数,可以函数返回值。极大灵活代码。
userdata:存储C数据,预定义操作只有赋值、比较。
thread:线程,设计协同操作。
table:表。

  1. ---------------表构造---------------
  2. polyline = {
  3. color="blue", --polyline.color或者polyline["polyline"]
  4. thickness=2; --可以用分号
  5. npoints=4,
  6. {x=0, y=0}, --polyline[1]
  7. {x=-10, y=0}, --polyline[2]
  8. {x=-10, y=1}, --polyline[3]
  9. {x=0, y=1}, --polyline[4]
  10. ["+"] = "add", --polyline["+"]
  11. ["-"] = "sub",
  12. ["*"] = "mul",
  13. ["/"] = "div"
  14. }

三、运算符

优先级,一元 > 二元;算术 > 关系 > 逻辑
1、算术运算符
二元运算符:+ - / ^ (加减乘除幂),注意有个幂
一元运算符:- (负值)
操作数为实数。
*2、关系运算符

< > <= >= == ~=
返回true、false。
== ~=:比较两个值,类型不同也不同,nil只和自己相等,tables、userdata、functions都是引用比较,即只有引用同一个对象才相等

  1. a, b = {},{}
  2. c = a
  3. print(a == c) --> true
  4. print(a == b) --> false
  5. print("2" > "15") --> true

3、逻辑运算符
and or not
认为:false和nil为假,其他为真,所以0也是真。
and和or返回不是true、false,和操作数有关,其实就可以看成“算术运算符”。
not返回true、false。

  1. a and b -- 如果afalse,则返回a,否则返回b
  2. a or b -- 如果atrue,则返回a,否则返回b
  3. x = x or v -- 技巧:vx的初始化值。
  4. (a and b) or c --相当于C三元运算符?:
  5. if not not a then end --技巧:返回a对应的boolean值,

4、连接运算符 ..

  1. print("Hello " .. "World") --> Hello World
  2. print(0 .. 1) --> 01 转成了字符串

四、基本语法

  1. ---------------赋值---------------
  2. a = "hello" .. "world" -- 赋值
  3. a, b = 10, 2*x -- 多赋值
  4. x, y = y, x -- 交换xy
  5. a, b, c = 0, 1 -- c=nil,少,补nil
  6. a, b = 0, 1, 3 -- 3丢弃,多,忽略
  7. a, b, c = 1 -- a=1b=nilc=nil,注意!!!
  8. a, b = f() -- 第一个返回值给a,第二个给b
  9. ---------------局部变量---------------
  10. x = 10
  11. local i = 1 -- 在当前的chunk有效
  12. while i<=x do
  13. local x = i*2 --> local to the while body
  14. print(x) --> 2, 4, 6, 8, ...
  15. i = i + 1
  16. end
  17. if i > 20 then
  18. local x --> local to the "then" body
  19. x = 20
  20. print(x) --> 20
  21. else
  22. print(x) --> 10 (the global one)
  23. end
  24. print(x) --> 10 (the global one)
  25. ------------条件控制------------
  26. if condi then --condi可以是任意值,只有falsenil为假。
  27. then-part
  28. elseif conditions then
  29. elseif-part
  30. .. --->多个elseif
  31. else
  32. else-part
  33. end;
  34. ------------while循环------------
  35. while condition do
  36. statements;
  37. end;
  38. ------------repeat循环------------
  39. repeat
  40. statements;
  41. until conditions;
  42. ------------for循环------------
  43. -- for(var = exp1; var < exp2; var += exp3)
  44. -- exp1,exp2,exp3只计算一次,循环前
  45. -- 不要主动改变var,结果未知
  46. for var=exp1,exp2,exp3 do
  47. -- for var=exp1,exp2 do --exp3=1
  48. loop-part
  49. break --退出当前循环,退出双重循环要2break,没有continue
  50. end
  51. ------------泛型for------------
  52. for i,v in ipairs(a) do --遍历array a全部值 a={ 1, 2, 3, 4, 5, }
  53. print(v)
  54. end
  55. for k in pairs(t) do --遍历table a全部keya={ x=1, y=2, z=3 }
  56. print(k)
  57. end
  58. local i = 1
  59. ------------breakreturn------------
  60. function foo ()
  61. while true do
  62. break -- 语法错误
  63. if true then break end --正确
  64. do break end -- 正确
  65. i = i + 1
  66. end
  67. return -- 语法错误
  68. do return end -- Creturn
  69. end

五、函数

带有词法定界(lexical scoping)的第一类值(first-class values)。
词法定界,指的是函数内部可以访问外部函数的局部变量。

  1. a = {p = print}
  2. a.p("Hello World") --> Hello World
  3. print = math.sin --> `print' now refers to the sine function
  4. a.p(print(1)) --> 0.841470
  5. foo = function (x) return 2*x end -- 赋值函数
  6. -- table.sort(array_table, func) -- 高级函数:以其他函数作为参数
  7. table.sort(network, function (a,b) -- a在b的左边
  8. return (a.name > b.name) -- 左边的name要大于右边的name
  9. end)
  10. Lib = {
  11. foo = function (x,y) return x + y end,
  12. }
  13. Lib = {}
  14. function Lib.goo (x,y)
  15. return x - y
  16. end
  17. local f = function (...)
  18. ...
  19. f(...) --递归调用,报错
  20. end
  21. local function f (...)
  22. ...
  23. f(...) --递归调用,正确
  24. end

调用

  1. function func_name (arguments-list) --参数少,补nil;参数多,忽略。
  2. statements-list;
  3. return ret1, ret2 --多返回值
  4. end;
  5. func(a, b)
  6. func "hellow world" --只有一个参数,可以省略()
  7. func [[a multi-linemessage]] --只有一个参数,可以省略()
  8. func {1,2,3} --只有一个参数,可以省略()
  9. s, e = string.find("hello Lua users", "Lua") --多返回值(起始、结束下标)

(多)返回值

  1. ------------函数返回值作为参数------------
  2. function foo0 () end -- returns no results
  3. function foo1 () return 'a' end -- returns 1 result
  4. function foo2 () return 'a','b' end -- returns 2 results
  5. -- 两种情况:
  6. -- 1func()在最后,返回值尽量补充
  7. x,y = foo2() -- x='a', y='b'
  8. x = foo2() -- x='a', 'b' is discarded
  9. x,y,z = foo2() -- x='a', y='b', z=nil
  10. x,y,z = 10,foo2() -- x=10, y='a', z='b'
  11. -- 2、其他情况,只返回一个值
  12. -- 作为表达式调用的时候:
  13. x,y = foo2(), 20 -- x='a', y=20
  14. x,y = foo0(), 20, 30 -- x='nil', y=20, 30 is discardedfoo0返回值是nil
  15. print(foo1()) --> a
  16. print(foo2()) --> a b
  17. print(foo2(), 1) --> a 1
  18. print(foo2() .. "x") --> ax
  19. a = {foo1()} -- a = {'a'}
  20. a = {foo2()} -- a = {'a', 'b'}
  21. a = {foo0(), foo2(), 4} -- a[1] = nil, a[2] = 'a', a[3] = 4
  22. function foo()
  23. return foo2() -- 完整返回foo2的返回值
  24. end
  25. print(foo()) -- 打印foo2的全部返回值
  26. print((foo())) -- 只返回foo的第一个返回值
  27. local _, x = string.find(s, p) -- _ 称为 哑元,占位用的。

参数

  1. ------------unpack解包参数------------
  2. -- unpack:把表(数组形式)以多返回值形式返回
  3. a = {"hello", "ll"}
  4. print(string.find(unpack(a))) --> 3 4
  5. -- unpack 原理:
  6. function unpack(t, i)
  7. i = i or 1
  8. if t[i] then
  9. return t[i], unpack(t, i + 1)
  10. end
  11. end
  12. ------------可变参数------------
  13. function func(...) --可变参数:三个点
  14. for i,v in ipairs(arg) do --arg:参数列表
  15. ...
  16. arg.n --参数个数
  17. end
  18. end
  19. function g (a, b, ...) end --至少2个参数
  20. g(3) -- a=3, b=nil, arg={n=0}
  21. g(3, 4) -- a=3, b=4, arg={n=0}
  22. g(3, 4, 5, 8) -- a=3, b=4, arg={5, 8; n=2}
  23. ------------参数传入------------
  24. w = Window {
  25. x=0, y=0, width=300, height=200,
  26. title = "Lua", background="blue",
  27. border = true
  28. }
  29. ------------闭包------------
  30. -- 闭包 = 函数 + upvalues
  31. -- 简单理解“带状态”的函数,函数引用了upvalue,这个upvalue就代表状态。
  32. function newCounter()
  33. local i = 0
  34. return function() -- 返回一个闭包(值)
  35. i = i + 1 -- iupvalue
  36. return i
  37. end
  38. end
  39. c1, c2 = newCounter(), newCounter() --c1c2是两个不同的闭包
  40. print(c1()) --> 1
  41. print(c1()) --> 2
  42. print(c2()) --> 1
  43. print(c1()) --> 3
  44. print(c2()) --> 2
  45. ------------尾调用------------
  46. -- Cgoto效果
  47. function f(x)
  48. return g(x) -- 尾调用,不会为g函数额外开辟栈空间
  49. g(x)
  50. return -- 不是尾调用
  51. return g(x) + 1 -- 不是尾调用
  52. return x or g(x) -- 不是尾调用
  53. return (g(x)) -- 不是尾调用
  54. return x[i].foo(x[j] + a*b, i + j) --是尾调用
  55. end

六、迭代器

iterator,简称iter,每调用一次返回被迭代对象的下一个元素。iter应该知道上一个迭代状态,也就是知道自己从哪儿来、要到哪儿去。

  1. ------------泛型for原型------------
  2. -- var-list:变量名列表,逗号隔开,多数情况1个。
  3. -- exp-list:表达式列表,逗号隔开,一般1个。
  4. for <var-list> in <exp-list> do
  5. <body>
  6. end
  7. ------------泛型for解析------------
  8. function iter_create(t) -- t: 迭代常量
  9. local var = 0 -- var: 控制变量
  10. iter = function(t, var) -- iter: 迭代器,这是一个无状态的迭代器,
  11. var = var + 1 -- 因为没有用到闭包,通过参数提供迭代状态(tvar)
  12. -- 严格地说,不是迭代器,而是生成器generator
  13. -- for才完成迭代。
  14. ...
  15. return a, b -- 就是for in之间的变量列表
  16. end
  17. return iter, t, var -- 迭代器,状态常量,控制变量
  18. -- 单状态迭代器:t一般就是被迭代对象本身:
  19. -- 多状态迭代器:t如果是{ x=t }类型,那就是个多状态的迭代器。
  20. end
  21. for a, b in iter_create(t) do -- 泛型for的过程:
  22. -- 1、循环前调用一次iter_create(t),返回一个迭代器,并保存。
  23. -- 2、每一次迭代,iter(t, var)
  24. -- 3、如果var == nil,终止循环
  25. -- a, b: 迭代器的返回值。
  26. ......
  27. end
  28. ------------例子------------
  29. function create_iter (t)
  30. local i = 0
  31. local n = table.getn(t)
  32. return function () --用了闭包,有状态的迭代器
  33. i = i + 1
  34. if i <= n then return t[i] end
  35. end
  36. end
  37. t = {10, 20, 30}
  38. for element in create_iter(t) do -- 这个就叫泛型for
  39. print(element)
  40. end

七、编译、运行、错误

1、编译

文本代码 -> 中间码(编译) -> 机器码 -> 执行
过程其实就是正常的编译+运行过程。
不同在于Lua解释器把整个过程全部完成。而这个Lua解释器是依附于C/C++程序运行的。也就是我们说的运行时编译

  1. -- 编译文件
  2. function loadfile(filename)
  3. -- ->中间码->机器码,并返回编译后的chunk作为函数
  4. ......
  5. if success then return chunk end -- 成功:返回编译后的chunk
  6. else return nil, errinfo -- 失败:返回nil chunk和错误信息。
  7. end
  8. -- 编译文件 + 运行
  9. function dofile (filename) --每次调用,都会编译
  10. local f = assert(loadfile(filename))
  11. return f()
  12. end
  13. local f = loadfile(filename) -- 一次编译
  14. a, b = f(), f() -- 多次运行
  15. print(loadstring("i i")) --> nil [string "i i"]:1: '=' expected near 'i'
  16. local str = [[
  17. function fuck() print("shit") end
  18. ]]
  19. local chunk = loadstring(str) -- 编译了代码,并未执行执行代码
  20. fuck() -- 错误,fuck未定义
  21. chunk()
  22. fuck() -- 正确
  23. -- loadstring不关心词法范围,只是用全局变量。
  24. local i = 0
  25. f = loadstring("i = i + 1") -- i是全局变量
  26. g = function () i = i + 1 end -- i是局部变量

2、require函数

require和dofile功能类似,编译并执行,但前者更优化:
1、搜索目录加载文件
2、避免重复加载
搜索目录
执行require “fuck” 时
1、确定搜索目录路径:
a、检查全局变量:LUA_PATH是否为字符串?是则确定为路径。
b、检查环境变量:LUA_PATH是否有值?是则确定为路径。
c、以上失败:确定路径为 ?;?.lua
d、假设目录路径:
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua;/usr/local/default.lua
2、查找文件
fuck
fuck.lua
c:\windows\fuck
/usr/local/lua/fuck/fuck.lua
没有找到则加载红色文字的文件。
避免重复加载

  1. ------------下面是虚拟代码来展示避免重复加载原理------------
  2. function require(filename) --filename:虚名
  3. if _LOADED[filename] == nil then -- _LOADED是真实有效的
  4. _LOADED[filename] = assert(loadfile(filename))
  5. end
  6. return _LOADED[filename]
  7. end
  8. ------------require小技巧------------
  9. LUA_PATH = "/usr/local/lua/newrequire.lua" --全局变量,require搜索目录
  10. require("fuck")
  11. -- newrequire.lua文件内容如下
  12. if _REQUIREDNAME == "fuck" then --全局变量_REQUIREDNAME保存require的文件名
  13. -- 当其他地方require("fuck")时,会执行这里
  14. end

3、动态链接库:loadlib

动态链接库不是ANSI C的标准,但是Lua为windows、Linux、FreeBSD、Solaris和其他一些Unix平台提供了这种机制。

  1. -- path: 库路径
  2. -- initfunc: 初始化函数名
  3. -- return: 函数chunk, errinfo
  4. loadlib(path, initfunc)
  5. print(loadlib()) -- bad argumentsnil,表示支持动态链接库,否则是不支持。
  6. local path = "/usr/local/lua/lib/libluasocket.so"
  7. local f = assert(loadlib(path, "luaopen_socket")) --返回初始化函数luaopen_socket
  8. f() -- 执行luaopen_socket

4、错误与处理

错误发生,跳出当前chunk。
pcall

  1. assert(func(), errinfo) --func()返回nil,抛出错误,错误信息errinfo
  2. function safeExe()
  3. errinfo = { code = 121 } --错误信息
  4. error(errinfo,stack_level) --抛出错误
  5. end
  6. local status, ret_or_errinfo = pcall(safeExe) -- 保护模式下执行safeExe
  7. if status == true then
  8. -- 执行成功,没有错误
  9. -- ret_or_errinfo:函数返回值
  10. else
  11. -- 执行失败,报错
  12. -- ret_or_errinfo:错误信息
  13. print(errinfo.code) --> 121
  14. end
  15. local status, err = pcall(function () a = 'a'+1 end) --lua自己也会生成错误信息
  16. print(err) -- stdin:1: attempt to perform arithmetic on a string value
  17. -- stdin:文件名
  18. -- 1:行号

xpcall
pcall无法获取异常发生时的栈信息,xpcall可以在错误发生时,回调函数,即可获取栈信息。

  1. function dosmth()
  2. error("got an error.")
  3. end
  4. function ohshit()
  5. print(debug.traceback())
  6. end
  7. xpcall(dosmth, ohshit)

八、协同程序:Thread

线程,同时运行。
协程,一次只能一个。

协程三个状态:挂起态运行态终止态
1、coroutine.create创建函数,进入挂起态。
2、resume,进入运行态,从挂起位置开始运行。
3、函数遇到yield,就被挂起,进入挂起态。
4、函数执行完成,变为终止态,resume会报错。

  1. -- 创建协程thread(进入挂起态)
  2. -- func: function,匿名函数
  3. -- co: thread,内存地址,此时被挂起
  4. local co = coroutine.create(func)
  5. -- 返回协程当前状态:挂起态suspend、终止态dead
  6. -- co: thread
  7. -- status: suspend: 挂起态,刚被创建挂起、yield()处被挂起。
  8. -- dead: 终止态,函数执行完毕。
  9. local status = coroutine.status(co)
  10. -- 唤醒协程,从挂起处运行
  11. -- co: function
  12. -- ...1: 传送 唤醒的yield的参数
  13. -- 第一种:创建处挂起,则传送给函数做参数
  14. -- 第二种:yield处挂起,传送给这个yield函数做参数
  15. -- bool: 执行成功true,一般都成功
  16. -- ...2: “下一次”yield传送来的参数、或者是协程函数返回值
  17. local bool, ...2 = coroutine.resume(co, ...1)
  18. -- 挂起协程,进入挂起态,等待resume唤醒
  19. -- ...1: 传递给下一次resume的参数(谁唤醒我,就给谁参数)
  20. -- ...2: 传递给唤醒这次yieldresume的参数
  21. local ...2 = coroutine.yield(...1)
  22. -----------------例子-----------------
  23. local co = coroutine.create(function (a, b, c)
  24. for i = 1, 10 do
  25. print(i,":", a, b, c)
  26. a, b, c = coroutine.yield(a * 10, b * 10)
  27. end
  28. end)
  29. print(co) -- thread: 0x8071d98 创建成功,返回thread类型。
  30. print(coroutine.status(co)) -- suspended 刚被创建进入挂起态,等待唤醒
  31. print(coroutine.resume(co,1,2,3)) -- 1:1 2 3 参数传送给协程函数,执行到yield处又被挂起。
  32. -- true 10 20 这次resume之后第一次遇到的yield给的参数
  33. print(coroutine.status(co)) -- suspended 第一次yield,挂起。
  34. print(coroutine.resume(co, 4, 5, 6)) -- 2:4 5 6 resume参数传递给第一次yield处的返回值
  35. -- true 40 50 这次resume之后第二次遇到的yield给的参数

生产者-消费者模式(消费者驱动)

  1. function receive ()
  2. local status, value = coroutine.resume(producer)
  3. return value
  4. end
  5. function send (x)
  6. coroutine.yield(x)
  7. end
  8. producer = coroutine.create( function ()
  9. while true do
  10. local x = io.read() -- produce new value
  11. send(x)
  12. end
  13. end)

管道和顾虑器

  1. function receive (prod)
  2. local status, value = coroutine.resume(prod)
  3. return value
  4. end
  5. function send (x)
  6. coroutine.yield(x)
  7. end
  8. function producer ()
  9. return coroutine.create(function ()
  10. while true do
  11. local x = io.read() -- produce new value
  12. send(x)
  13. end
  14. end)
  15. end
  16. function filter (prod)
  17. return coroutine.create(function ()
  18. local line = 1
  19. while true do
  20. local x = receive(prod) -- get new value
  21. x = string.format("%5d %s", line, x)
  22. send(x) -- send it to consumer
  23. line = line + 1
  24. end
  25. end)
  26. end
  27. function consumer (prod)
  28. while true do
  29. local x = receive(prod) -- get new value
  30. io.write(x, "\n") -- consume new value
  31. end
  32. end
  33. consumer(filter(producer()))

迭代器

  1. function permgen (a, n)
  2. if n == 0 then
  3. coroutine.yield(a)
  4. else
  5. for i=1,n do
  6. -- put i-th element as the last one
  7. a[n], a[i] = a[i], a[n]
  8. -- generate all permutations of the other elements
  9. permgen(a, n - 1)
  10. -- restore i-th element
  11. a[n], a[i] = a[i], a[n]
  12. end
  13. end
  14. end
  15. function printResult (a)
  16. for i,v in ipairs(a) do
  17. io.write(v, " ")
  18. end
  19. io.write("\n")
  20. end
  21. function perm (a)
  22. local n = table.getn(a)
  23. local co = coroutine.create(function () permgen(a, n) end)
  24. return function () -- iterator
  25. local code, res = coroutine.resume(co)
  26. return res
  27. end
  28. end
  29. --function perm (a)
  30. -- local n = table.getn(a)
  31. -- return coroutine.wrap(function () permgen(a, n) end)
  32. --end
  33. for p in perm{"a", "b", "c"} do
  34. printResult(p)
  35. end

非抢占式多线程

非抢占式,线程只会主动挂起中断(主动yield),而不会被其他线程挂起。

多线程远程下载文件

通过http协议从远程主机上下载一些文件,使用Diego Nehab开发的LuaSocket库来完成。

  1. function download (host, file)
  2. local c = assert(socket.connect(host, 80)) --创建连接
  3. local count = 0 --已读取字节数
  4. c:send("GET " .. file .. " HTTP/1.0\r\n\r\n") --http请求下载文件
  5. while true do
  6. local s, status = read1KB(c) --读取1KB数据
  7. count = count + string.len(s) --累计已读取数据
  8. if status == "closed" then break end
  9. end
  10. c:close()
  11. print(file, count)
  12. end
  13. function read1KB (connection)
  14. connection:timeout(0) -- 使任何请求都不阻塞
  15. local s, status = connection:receive(2^10)
  16. if status == "timeout" then
  17. coroutine.yield(connection) --没有下载数据,挂起
  18. end
  19. return s, status
  20. end
  21. threads = {} -- 当前下载线程列表
  22. function get (host, file) -- 创建下载线程
  23. local co = coroutine.create(function ()
  24. download(host, file)
  25. end)
  26. table.insert(threads, co)
  27. end
  28. function dispatcher()
  29. while true do
  30. local n = table.getn(threads)
  31. if n == 0 then break end
  32. local connections = {}
  33. for i = 1, n do
  34. local status, res = coroutine.resume(threads[i])
  35. if not res then -- download函数执行完毕,没有返回值
  36. table.remove(threads, i)
  37. break
  38. else -- 没有数据,连接超时
  39. table.insert(connections, res)
  40. end
  41. end
  42. if table.getn(connections) == n then -- 当前所有下载连接都没有数据
  43. -- 当所有的连接都timeout,分配器调用select等待任一连接状态的改变
  44. socket.select(connections)
  45. end
  46. end
  47. end
  48. ---------------------------------------
  49. host = "www.w3c.org"
  50. get(host, "/TR/html401/html40.txt")
  51. get(host, "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf")
  52. get(host, "/TR/REC-html32.html")
  53. get(host,
  54. "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")
  55. dispatcher() -- main loop