LuaSocket 學習筆記

2022-02-26 16:52:02 字數 4229 閱讀 7760

——— lua socketlib 和 協程

目標讀者:會使用 lua socketlib;會用協程。

lua socketlib 不僅提供了 tcp-udp/ip 的網路連線。還提供了諸如 tcp、udp 的客戶端和服務端,以及 ftp、http 協議等高階物件。

本教程專注於 lua socketlib 提供的 socket 和 tcp/ip 伺服器。一旦掌握了基本的操作,這個庫裡面的其他元件用起來就是小菜一碟。

協程是 lua 5.1 的特性會在 tcp/ip 伺服器的管理中使用到。

首先, tcp/ip 協議的聖經在此:rfc793 。

tcp/ip 是許多通訊協議的乙個大集合,它基於兩個原始的協議:tcp 和 ip。 tcp 在 osi 模型的第四層(傳輸層),ip在第三層(網路層)。tcp 協議用於從應用向網路的資料傳輸,能夠以可靠的方式處理從源到目標位址的位元組流(stream)。乙個 tcp 連線由乙個五件套表示,它在給定的整個網路中具有唯一性。

乙個連線由 2 個 半套接字(half-sockets):源(客戶端)一半套接字 和 終點(服務端)一半套接字。

在監聽乙個埠的時候, tcp 伺服器建立半個套接字(伺服器套接字)。接收到連線時,伺服器對這半個套接字進行複製並與終點的半個套接字(客戶端套接字)連線,建立通訊套接字,實現資訊資料流的傳輸。因此,伺服器總是擁有乙個可用的半個套接字。

通常建立乙個tcp伺服器的過程可以直接用 socketlib 的函式實現。具體流程如下:

建立服務端套接字

把服務端套接字附加到埠

監聽這個埠

socketlib 是 hyperion 的一部分,它可以像 lua 提供的其他標準庫一樣使用。下面給出建立 tcp 伺服器的**:

function createtcpserver( host, port, backlog) // host 是個啥?

local tcp_server,err = socket.tcp();

if tcp_server == nil then

return nil,err;

endtcp_server:setoption("reuseaddr",true);

local res, err = tcp_server:bind(host,port);

if res==nil then

return nil,err;

endres,err = tcp_server:listen(backlog);

if res == nil then

return nil,err;

endreturn tcp_server;

end

socket.tcp()建立乙個 tcp 服務端物件。函式和物件的詳解見本頁末尾。

在建立物件之後是一些耳熟能詳的步驟:setoption(), bind() 及 listen();

大多數 socketlib 函式成功返回乙個引數,失敗返回兩個引數。在失敗的情況下,引數一返回nil,引數二返回錯誤資訊。

createtcpserver()必須在 host 程式初始化的時候呼叫。在 hyperion 中,我們直接把函式及其呼叫都放在乙個 lua 的初始化指令碼中(run_mode="init")。

最後一件事:注意createtcpserver()函式中的host引數。如果你用localhost來設定它,那麼這個 tcp 伺服器只能接受本地主機(localhost)傳進來的連線(例如客戶端和伺服器端在同乙個電腦裡執行)。所以,如果你希望能夠連放在另一台計算機執行的客戶端程式,你需要把host設定為:host = "*";。這是乙個小技巧,但是相信我,如果你不知道的話可能會浪費很多時間!

建立乙個 tcp 伺服器是最簡單的部分,讓我們來處理有分量的那部分:傳入連線的接收與管理。

處理傳入連線的指導原則是把 tcp 伺服器設定成乙個死迴圈,在迴圈的開始處用server:accept()函式來等待連線。但是我們馬上發現了問題:如果我們進入這個死迴圈,那麼伺服器程式就會凍結在那裡,這是我們無法接受的。

對於這個尖銳的問題,其解決方案是把這個死迴圈轉換為乙個與程式主線程分離的執行路徑。有兩個方法:執行緒或者協程。

執行緒大家都比較了解,不再贅述。

協程與執行緒一樣是乙個分離的執行佇列,但是不由 os 管理,而是由主程式來控制協程的切換。主程式每隔一小段時間會遍歷一遍目前存在的協程。

協程也被稱作協作的多執行緒,意思是協程必須相互合作來使得多執行緒正確工作。如果乙個協程因為某種原因停止合作,整個程式都會受到影響。如果乙個應用程式卡死,那麼很可能不是由於作業系統的問題,而是由於應用程式中不合理的協作關係。

為了能夠正確進行協作,協程中無限迴圈的每一次迴圈都必須盡快結束。協程中最關鍵的函式是accept()。這個函式在預設情況下直到新的連線到來才會執行下一步。這種行為是高度非協作性的。因此我們需要通過server:settimeout()來設定乙個等待的最大時間。在後面提供的demo_lua_tcp_server_coroutine.xmldemo 中,這個時間被設定為 10ms。

故事到此還沒有結束。協程需要告訴主程式什麼時候能夠出讓空間給下乙個協程。這個功能由coroutine.yield()函式提供。yield是協程無限迴圈中的最後乙個指令。通過這個函式,主程式會獲知當前協程已經執行完畢,輪到執行下乙個協程。另乙個協程的執行通過coroutine.resume()函式來實現。

現在,讓我們以更具體的方式來看一看。先來看看等待傳入連線的核心實現部分。

function runtcpserver()

local stop_server = 0;

local num_cnx = 0;

while( stop_server == 0) do

local err = "";

local tcp_client = 0;

g_tcp_server:settimeout(0.01);

tcp_client,err = g_tcp_server:accept();

if tcp_client ~= nil then

g_client_msg, err = tcp_client:receive('*|');

if( g_client_msg ~= nil) then

if (g_client_msg == "stop") then

stop_server = 1;

else

tcp_client:send("message :"..g_client_msg);

endend

if tcp_client ~= nil then

tcp_client:close();

endend

coroutine.yield();

endend

這個協程的建立是在初始化部分通過coroutine.create()函式實現。它的引數是等待處理的協程。這個函式會返回乙個新建協程的控制代碼。

g_tcp_co = coroutine.create(runtcpserver);
通過這個控制代碼,我們可以按固定間隔來執行對應的協程(例如每幀呼叫一次),呼叫coroutine.resume()函式即可。

coroutine.resume( g_tcp_co);
一旦有連線傳進來,tcp 伺服器通過server:receive()函式接收到資料。引數中*|表示接收到的資料以lf\n結尾。這裡 tcp 伺服器以反射模式工作:通過server:send()把接收到的資料原路返回給客戶端。

為了測試 tcp 服務端,這裡有乙個tcp 客戶端可供使用:

這個客戶端用起來很簡單。只要輸入伺服器名稱(主機名稱或者ip位址,例如:127.0.0.1)、伺服器的埠號和需要傳送的訊息。這個客戶端會自動新增訊息結尾。然後點選傳送就ok啦,伺服器的反饋資訊會在底部區域顯示。

the lua socketlib and the coroutines

tutorial:networking with udp

lua 網路程式設計

編譯使用luasocket

編譯lua5.1 編譯luasocket 使用luasocket 建乙個測試目錄test,把mime.dll和socket.dll放在test socket 中 把luasocket中src目錄下的.lua檔案放在test src 中 寫乙個測試檔案main.lua package.path d t...

lua模組luasocket安裝

wget 2 解壓縮包 tar zxvf tar.gz 3 查詢lua安裝路徑和修改config檔案 whereis lua 進入luasocket目錄修改config檔案 install top share usr share lua 5.1 install top lib usr lib64 l...

Lua基礎 安裝LuaSocket

這裡為 lua基礎 coroutine lua的多執行緒程式設計 做一下準備工作,因為用到了socket庫,這裡就說明一下怎麼在fedora上安裝luasocket,以防有的朋友的開發環境跟博主的一樣,預設沒有該庫,又得自己到處去查怎麼安裝。note 該庫從文件看,好像只支援lua5.1,博主沒有嘗...