2021-01-20 wayland 協(xié)議的實(shí)現(xiàn)

如何擴(kuò)展 wayland 協(xié)議

為了能夠擴(kuò)展 wayland 協(xié)議,首先需要理解 wayland 協(xié)議,并且知道怎么樣在server和client端實(shí)現(xiàn)協(xié)議中定義的接口。看了一堆文檔,試著按照自己的理解來整理文檔,并動(dòng)手寫簡(jiǎn)單的代碼來加深理解。【希望一個(gè)月之后再讀這篇文章不會(huì)覺得是一坨shit】

wayland 協(xié)議是什么

wayland核心協(xié)議是一個(gè) xml 文件,如果我們安裝了 wayland 開發(fā)包,這個(gè)文件在一般在系統(tǒng)的 /usr/share/wayland/wayland.xml。核心協(xié)議的內(nèi)容有限,不滿足我們平常對(duì)窗口的一些操作,所以為了實(shí)現(xiàn)一些窗口管理的功能,還有很多擴(kuò)展的協(xié)議,比如 xdg-shell 就是為了實(shí)現(xiàn)桌面窗口而擴(kuò)展的協(xié)議。協(xié)議有穩(wěn)定版本和不穩(wěn)定版本,在這篇文檔中我們主要看 /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml,這是一個(gè)穩(wěn)定的版本,它的 xdg_surface 對(duì)象有一個(gè) request 接口是 set_window_geometry,從注釋來看,如果實(shí)現(xiàn)這個(gè)接口,應(yīng)該能滿足我們的需求。

可以通過 wayland-scanner 工具解析這個(gè) xml 生成Server、Client的頭文件以及 glue code一個(gè) C 文件。


wayland-scanner server-header < xdg-shell.xml > xdg_shell_server_header.h

wayland-scanner client-header < xdg-shell.xml > xdg_shell_client_header.h

wayland-scanner private-code < xdg-shell.xml > xdg_shell_protocol.c

wayland 協(xié)議生成 gluecode.png

上面這張圖是網(wǎng)上找到的,開發(fā)的時(shí)候基本上就是這么個(gè)流程。寫一個(gè)server.c 文件,用到生成的 server-protocol.h 和 protocol.c ,編譯命令:

gcc -o server server.c xdg_shell_protocol.c -lwayland-server

編譯Client程序:

gcc -o xdg_client xdg_client.c xdg_shell_protocol.c -lwayland-client

理解 wayland 協(xié)議

wayland 協(xié)議其實(shí)就是我們預(yù)先定義好一個(gè) object 的接口,包括它的 request 和 event,Server實(shí)現(xiàn) request 接口,Client實(shí)現(xiàn)對(duì) event 的監(jiān)聽和響應(yīng)。當(dāng)Client把對(duì)這個(gè)對(duì)象的 request 封裝成消息發(fā)給Server,Server收到消息后,根據(jù)對(duì)象id的和操作碼執(zhí)行對(duì)應(yīng)的響應(yīng)函數(shù);event 也是類似的流程,Server把對(duì)這個(gè)對(duì)象的 event 發(fā)到了Client,Client會(huì)作出響應(yīng)。

在Server和Client之間,對(duì)象是一一對(duì)應(yīng)的,互相知道這個(gè)對(duì)象的狀態(tài)。Client是 wl_proxy,與之對(duì)應(yīng)的,在Server就會(huì)有一個(gè) wl_resource。Server程序需要知道這個(gè) resource 屬于哪個(gè)Client程序。這個(gè)對(duì)象之間映射就是 wayland 的 wl_map 來維護(hù)的。


struct wl_map {

  struct wl_array  client_entries;

  struct wl_array server_entries;

  uint32_t side;

  uint32_t free_list;

};

side 分WL_MAP_CLIENT_SIDE 和 WL_MAP_SERVER_SIDE兩種,表明當(dāng)前map保存的是Client還是 Server對(duì)象。

wayland Server運(yùn)行之后,一般會(huì)有多個(gè) wayland Client程序連接。每當(dāng)Client程序調(diào)用 wl_display_connect 連接到wayland, 對(duì)應(yīng)就會(huì)為它維護(hù)一個(gè) wl_map結(jié)構(gòu):display->objects[代碼wayland-client.c ];如果Server監(jiān)聽到Client連接,在 wl_client_create 的時(shí)候,也會(huì)為之創(chuàng)建一個(gè) wl_map:client->objects[代碼wayland-server.c]。

在映射表中,Client proxy 對(duì)象從 0 開始,Server resource 對(duì)象從 0xff000000 開始存放,display->objects只用了 client_entries,client->objects 只用了 server_entries。wl_map在Client和Server端各有一個(gè),它們分別存了wl_proxy和wl_resource的數(shù)組,且是一一對(duì)應(yīng)的。這些對(duì)象在這個(gè)數(shù)組中的索引作為它們的id。這樣,參數(shù)中的對(duì)象只要傳id,這個(gè)id被傳到目的地后會(huì)通過查找這個(gè)wl_map表來得到本地相應(yīng)的對(duì)象。

映射表創(chuàng)建后,就可以插入數(shù)據(jù)了。比如我們客戶端創(chuàng)建wl_proxy,設(shè)置interface等信息,然后將該wl_proxy插入到display->objects的wl_map中,返回值為id,其實(shí)就是在wl_map中數(shù)組中的索引值。這個(gè)值是會(huì)被發(fā)到Server端的,這樣Server端就可以創(chuàng)建 wl_resource,并把它插入到Server端的wl_map數(shù)組的相同索引值的位置。這樣邏輯上,就創(chuàng)建了wl_proxy和wl_resource的映射關(guān)系。以后,Client和Server間要相互引用對(duì)象只要傳這個(gè)id就可以了。


wayland.png

上面這張圖被多次引用,它很清楚的描述了Server和Client之間的對(duì)象映射和事件調(diào)用。

object

為了Server和Client之間的通信,第一步就是創(chuàng)建對(duì)象,wayland中默認(rèn)第一個(gè)對(duì)象就是 wl_display,object id 為1,這個(gè)對(duì)象之外的所有其他對(duì)象,都是需要通信來創(chuàng)建的。

在 wayland 中,wl_display 是第一個(gè)對(duì)象,wl_registry 是第二個(gè)對(duì)象,因?yàn)橛辛?wl_registry 之后,我們才能注冊(cè)綁定其他所有的 global object。在運(yùn)行Server程序前,開啟 WAYLAND_DEBUG=1,可以看到Client連接后,Server的 debug 如下:

[1008582.564] wl_display@1.get_registry(new id wl_registry@2)

[1008582.581] -> wl_registry@2.global(1, "wl_output", 1)

[1008582.654] wl_registry@2.bind(1, "wl_output", 1, new id [unknown]@4)

request 和 event

在生成的 glue code代碼中,主要定義了一些對(duì)象,以及這寫對(duì)象的request和event接口。Server頭文件中定義 request 的接口結(jié)構(gòu),因?yàn)镾erver需要響應(yīng)Client請(qǐng)求,所以我們?cè)陂_發(fā)Server程序時(shí)需要實(shí)現(xiàn) request接口;在Client頭文件中定義 event 的 listener,因?yàn)镃lient需要監(jiān)聽Server傳過來的事件,執(zhí)行對(duì)應(yīng)的回調(diào)。所以在開發(fā)Client程序時(shí),需要去 add_listener,實(shí)現(xiàn)收到事件之后要做的工作。

以 xdg_surface 為例,它在生成的xdg_shell_protocol.c 文件中定義如下:


WL_PRIVATE const struct wl_interface xdg_surface_interface = {

        "xdg_surface", 3,

        5, xdg_surface_requests,

        1, xdg_surface_events,

};

這個(gè) xdg_surface_interface 是一個(gè)全局變量,數(shù)據(jù)類型是 wl_interface。這個(gè)結(jié)構(gòu)中成員組成:name、version、request個(gè)數(shù)、request簽名、event個(gè)數(shù)以及event簽名。也就是說 xdg_surface 這個(gè)對(duì)象有 5 個(gè)請(qǐng)求和 1 個(gè)事件。

request 和 event 簽名是 wl_message 結(jié)構(gòu)的,不管是 request 和 event 都會(huì)被封裝成 MESSAGE 在 server 和 client 之間傳遞。


static const struct wl_message xdg_surface_requests[] = {

        { "destroy", "", xdg_shell_types + 0 },

        { "get_toplevel", "n", xdg_shell_types + 7 },

        { "get_popup", "n?oo", xdg_shell_types + 8 },

        { "**set_window_geometry**", "iiii", xdg_shell_types + 0 },

        { "ack_configure", "u", xdg_shell_types + 0 },

};

message

當(dāng)Client程序拿到一個(gè)對(duì)象了,就可以給Server發(fā)對(duì)這個(gè)對(duì)象的request,Server收到這個(gè)請(qǐng)求去執(zhí)行對(duì)應(yīng)的工作。比如Client的窗口中有個(gè)entry,我們輸入文字時(shí),窗口內(nèi)容需要更新,那么就可以在Client調(diào)用 wl_surface_damage 告知Server這塊區(qū)域無效需要重新繪制了,其實(shí) wl_surface_damage 這個(gè)函數(shù)其實(shí)就是把就是wl_surface 提供的一個(gè) request“damage”封裝成一條操作消息,通過 socket從Client發(fā)到Server,Server收到了這條消息,解析message,找到操作碼去執(zhí)行對(duì)應(yīng)的操作;同樣Server也會(huì)給Client發(fā)event,Client監(jiān)聽到event去執(zhí)行對(duì)應(yīng)的回調(diào)。

對(duì)于一條 message,我們需要了解它的基本結(jié)構(gòu):

object id + messeage size + opcode + 其他各個(gè)參數(shù)組成

其中 opcode 就是我們請(qǐng)求或者事件的操作碼,這個(gè)碼其實(shí)是由協(xié)議文件 xml 中它的出現(xiàn)順序決定的(從0開始計(jì)數(shù)),比如 damage 在 wl_surface_request 中是第三個(gè),它的 opcode 就是 2,下面這張圖來自 wayland protocol book:

2021-01-16_13-25.png

其實(shí)我想看 set_window_geometry 的,但是不知道怎么樣打印出上面的數(shù)據(jù)。xdg_surface 的 set_window_geometry 是第4個(gè),所以它的 opcode 應(yīng)該是 3,在生成的客戶端頭文件中定義:#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3

協(xié)議消息打包

Client請(qǐng)求

接下來以我們最關(guān)心的xdg-shell 擴(kuò)展協(xié)議中的 xdg_surface_set_window_geometry 為例來介紹消息是如何從Client發(fā)到Server的。這個(gè)函數(shù)是 wayland-scanner 掃描協(xié)議文件xdg-shell-unstable-v5.xml,為xdg_surface 的請(qǐng)求 set_window_geometry 自動(dòng)生成的供Client使用的函數(shù),函數(shù)里面只是簡(jiǎn)單地執(zhí)行了下面這個(gè)語(yǔ)句:

wl_proxy_marshal((struct wl_proxy *) xdg_surface, XDG_SURFACE_SET_WINDOW_GEOMETRY, x, y, width, height);

wl_proxy_marshal 在我們這幾個(gè)生成文件中找不到定義,它是 wayland 庫(kù)提供的,需要在 wayland 代碼中去找,src/wayland-client.c 文件中,通過層層調(diào)用,在函數(shù) wl_proxy_marshal_array_constructor_versioned 中開始構(gòu)建 message 并發(fā)送:

構(gòu)建 wl_closure 消息:

closure = wl_closure_marshal(&proxy->object, opcode, args, message);

發(fā)送消息:

wl_closure_send(closure, proxy->display->connection)

Server和Client的通信通過 socket 來實(shí)現(xiàn),由于這部分跟我們擴(kuò)展協(xié)議暫時(shí)沒什么關(guān)系,所以沒有深入去看,感興趣可以自己去看代碼。附錄簡(jiǎn)單介紹了通信機(jī)制。

Server事件

流程其實(shí)差不多,只不過Server的函數(shù)是 post_event。比如創(chuàng)建對(duì)象時(shí)發(fā)出的global信號(hào):

wl_resource_post_event(resource,
       WL_REGISTRY_GLOBAL,
       global->name,
       global->interface->name,
       global->version);

協(xié)議消息解包

消息解包就是把上面的 marshal 過程再 demarshal,因?yàn)樵诖虬⒌臅r(shí)候知道 object id,interface的接口簽名以及 opcode,這樣就能根據(jù)這幾個(gè)信息從 interface 中解析得到參數(shù)格式,從而把一整條消息解析出來。比如 set_window_geometry,從接口定義中找到它的 wl_message 格式:

{ "set_window_geometry", "iiii", xdg_shell_unstable_v5_types + 0 },

這是一條 wl_message 結(jié)構(gòu)的數(shù)據(jù):


struct wl_message {

    /** Message name */

    const char *name;

    /** Message signature */

    const char *signature;

    /** Object argument interfaces */

    const struct wl_interface **types;

};

其中消息簽名是”iiii”表示這個(gè) request 的參數(shù)是四個(gè)整形數(shù)據(jù)。第三個(gè)成員 types,需要從數(shù)組 xdg_shell_unstable_v5_types 中去找,這里加 0 表示數(shù)組第一條數(shù)據(jù)。這個(gè)請(qǐng)求不需要?jiǎng)?chuàng)建新的對(duì)象,所以簽名為空。如果需要?jiǎng)?chuàng)建新對(duì)象,消息簽名中會(huì)有“n”,表示 new id,types 就是這個(gè)新對(duì)象的接口定義。比如 wl_display_get_registry 需要返回 wl_registry 對(duì)象,Server和Client需要為這個(gè)新對(duì)象達(dá)成共識(shí),好為后面的request、event傳遞打下基礎(chǔ),所以需要提前定好 interface,它的這個(gè) types 就是 &wl_registry_interface。// { "get_registry", "n", wayland_types + 9 },

回到 set_window_geometry,協(xié)議中定義了這個(gè)請(qǐng)求,Client把這個(gè)請(qǐng)求組成 message 發(fā)送給Server。Server socket 監(jiān)聽機(jī)制監(jiān)聽到這條消息后,就會(huì)執(zhí)行 socket 的回調(diào)函數(shù) wl_client_connection_data[見附錄說明]。這個(gè)函數(shù)里面反序列化消息,得到 wl_closure,找到目標(biāo)對(duì)象對(duì)應(yīng)的接口函數(shù),利用 libffi 執(zhí)行 server 端的 implementation 函數(shù)。

如何找到目標(biāo)對(duì)象的接口函數(shù)?這就需要 server 端來實(shí)現(xiàn)了。wayland server 和 client 之間的對(duì)象是一一對(duì)應(yīng)的,對(duì)于每個(gè) object,它的 interface 定義,有幾個(gè) request,有幾個(gè) event,以及各自的參數(shù)是什么,它們相互之間都很清楚。所以每當(dāng)Client申請(qǐng) bind 一個(gè)對(duì)象,Server需要?jiǎng)?chuàng)建一個(gè)資源與之對(duì)應(yīng),如果這個(gè)對(duì)象有 request 需要實(shí)現(xiàn),Server就需要去實(shí)現(xiàn)這些函數(shù)接口并且 set_implementation。

Server代碼簡(jiǎn)述

為了響應(yīng)Client的請(qǐng)求,Server需要?jiǎng)?chuàng)建對(duì)應(yīng)的對(duì)象。首先我們需要去查協(xié)議中對(duì)象的接口定義,再去實(shí)現(xiàn)對(duì)象接口中定義的request。代碼片段1:


display = wl_display_create ();

wl_display_add_socket_auto (display);

wl_global_create (display, &wl_compositor_interface, 3, NULL, &compositor_bind);

wl_global_create (display, &wl_shell_interface, 1, NULL, &shell_bind);

wl_global_create (display, &xdg_wm_base_interface, 1, NULL, &xdg_wm_base_bind);

wl_global_create (display, &wl_seat_interface, 1, NULL, &seat_bind);

每當(dāng)Server調(diào)用 wl_global_create 創(chuàng)建一個(gè)對(duì)象,就會(huì)將 global 事件打包發(fā)給Client,Client收到 global 事件,在回調(diào)函數(shù)中需要 bind 這個(gè)對(duì)象。上面創(chuàng)建 wl_compositor 對(duì)象時(shí),傳遞了 &compositor_bind 函數(shù)指針,也就是說,如果Client執(zhí)行 wl_registry_bind 綁定 wl_compositor對(duì)象,Server就會(huì)執(zhí)行這個(gè) compositor_bind。在綁定的時(shí)候,Server創(chuàng)建與Client對(duì)象相對(duì)應(yīng)的資源,并且設(shè)置它的實(shí)現(xiàn)函數(shù)。比如 wl_compositor,它的 request 有兩個(gè):


static const struct wl_message wl_compositor_requests[] = {
        { "create_surface", "n", wayland_types + 10 },
        { "create_region", "n", wayland_types + 11 },
};

那么Server代碼中就需要定義這兩個(gè)請(qǐng)求對(duì)應(yīng)的處理函數(shù):


static struct wl_compositor_interface compositor_implementation =

{
    &compositor_create_surface,
    &compositor_create_region
};

在 compositor_bind 中,主要做這兩步工作:


struct wl_resource *resource = wl_resource_create (client, &wl_compositor_interface, 1, id);
wl_resource_set_implementation (resource, &compositor_implementation, NULL, NULL);

如果我們定義的 compositor_create_surface沒有問題,Client調(diào)用 wl_compositor_create_surface 函數(shù)的時(shí)候,Server就能執(zhí)行到 compositor_create_surface,從而打通從Client到Server的請(qǐng)求流程。

但是,由于 wl_compositor,wl_surface 這些對(duì)象要實(shí)現(xiàn)的接口太太多了,所以在測(cè)試代碼中,這些對(duì)象的 request 我就只定義了空的函數(shù)體,保證Client請(qǐng)求能拿到對(duì)象,但是沒有實(shí)際工作。代碼片段如下:


static const struct **wl_output_interface** wl_output_implementation = {

   .release = wl_output_handle_release,

};

在綁定對(duì)象時(shí),設(shè)置 request 接口的實(shí)現(xiàn)


struct wl_resource *resource = wl_resource_create (client, &wl_output_implementation, wl_output_interface.version, id);

wl_resource_set_implementation(resource, &wl_output_implementation, client_output, wl_output_handle_resource_destroy);

wayland 中有兩個(gè) wl_output_interface,千萬不要混淆了。其中一個(gè)是wl_interface 類型的變量,指明了 name version request 和 event,在生成的glue code文件中定義:


WL_PRIVATE const struct wl_interface ***wl_output_interface*** = {

        "wl_output", 3,

        1, wl_output_requests,

        4, wl_output_events,

};

另一個(gè)是結(jié)構(gòu)體,成員是待實(shí)現(xiàn)的函數(shù)指針,在生成的server 頭文件中定義:


struct ***wl_output_interface*** {

        void (*release)(struct wl_client *client,

                        struct wl_resource *resource);

};

每個(gè)對(duì)象都會(huì)有這兩個(gè)讓人迷糊的 wl_xxx_interface,一定要注意區(qū)分。結(jié)構(gòu)體 wl_output_interface 里面的函數(shù)指針就對(duì)應(yīng)是這個(gè)對(duì)象需要實(shí)現(xiàn)的 request。

實(shí)現(xiàn)request

qtwayland 無法設(shè)置 Client 窗口的坐標(biāo),理論上來說,可能是下面兩種原因:

1、客戶端程序的 set_window_geometry 沒有給 wayland Server 發(fā)請(qǐng)求

2、雖然客戶端發(fā)了請(qǐng)求,但是 wayland Server 端實(shí)際上沒有實(shí)現(xiàn)它。

很多現(xiàn)有的合成器都沒有實(shí)現(xiàn) set_window_geometry 接口,因?yàn)?wayland 設(shè)計(jì)理念就是這樣。它明確表示不希望客戶端程序自己設(shè)置坐標(biāo),而是覺得客戶端的坐標(biāo)應(yīng)該由合成器去做決定。

我找到了一個(gè)非常非常簡(jiǎn)單的開源合成器代碼,它沒有實(shí)現(xiàn) xdg-shell 的 set_window_geometry 請(qǐng)求。一開始我在運(yùn)行 weston-flower 客戶程序時(shí),不管怎么調(diào)用 xdg_surface_set_window_geometry嘗試設(shè)置坐標(biāo)都無效,一直都顯示在位置0,0.
補(bǔ)充:開源代碼地址:https://github.com/eyelash/tutorials.git

通過修改合成器代碼,實(shí)現(xiàn) xdg-shell 的協(xié)議來達(dá)到設(shè)置客戶端窗口的目標(biāo)。

首先我們需要維護(hù)資源的狀態(tài),可以定義一個(gè)結(jié)構(gòu)體,維護(hù)當(dāng)前 xdg_surface,當(dāng)前坐標(biāo) x,y 等。

第一步:在創(chuàng)建對(duì)象時(shí),綁定我們的實(shí)現(xiàn)接口:


struct surface *surface = wl_resource_get_user_data (_surface);

surface->xdg_surface = wl_resource_create (client, &xdg_surface_interface, 1, id);

wl_resource_set_implementation (surface->xdg_surface, &xdg_surface_implementation, surface, NULL);

這個(gè)surface 就是自定義的結(jié)構(gòu)體,把它設(shè)置為服務(wù)端資源的user_data:resource->data = surface,當(dāng)接收到客戶端請(qǐng)求的時(shí)候,這個(gè)數(shù)據(jù)會(huì)作為 wl_resource 的參數(shù)一起傳遞過來,用于為維護(hù)處于不斷變化中的資源的狀態(tài)。

第二步:定義各個(gè) request 實(shí)現(xiàn):


static struct xdg_surface_interface xdg_surface_implementation = {
        &xdg_surface_destroy,
        &xdg_surface_get_toplevel,
        &xdg_surface_get_popup,
        &xdg_surface_set_window_geometry,
        &xdg_surface_ack_configure
};

第三步:實(shí)際的實(shí)現(xiàn)接口:


static void xdg_surface_set_window_geometry (struct wl_client *client, struct wl_resource *resource,
                                                          int32_t x, int32_t y, int32_t width, int32_t height)
{
        struct surface *surface = wl_resource_get_user_data (resource);

        surface->x = x;
        surface->y = y;
        redraw_needed = 1;
}

由于我們想要更新服務(wù)端 resource 的位置,首先需要拿到服務(wù)端維護(hù)的xdg_surface,在綁定實(shí)現(xiàn)接口時(shí),傳入的 surface 參數(shù),可以取出來。redraw_needed 是為了觸發(fā)下一幀畫面時(shí)重新渲染客戶端窗口,要不然不會(huì)更新。

附錄

wayland通信

在開發(fā) wayland Client程序時(shí),第一步工作就是連接到wayland Server拿到資源對(duì)象,一般是wl_display_connect 再wl_display_get_registry,拿到 wl_registry 對(duì)象后,就會(huì)監(jiān)聽 global 信號(hào),通過調(diào)用 wl_registry_add_listener 來監(jiān)聽,注冊(cè)信號(hào)回調(diào)函數(shù)。

Server會(huì)把可用對(duì)象挨個(gè)發(fā)出 global 信號(hào),Client程序在 global 信號(hào)回調(diào)函數(shù)中就可以 wl_registry_bind 這些對(duì)象,生成Client的可用對(duì)象。[參考 weston 的客戶程序代碼 window.c] 但是信號(hào)機(jī)制一般是對(duì)同一個(gè)進(jìn)程來說的,我們可以監(jiān)聽某個(gè)對(duì)象的某個(gè)信號(hào),當(dāng)收到信號(hào)時(shí)執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù);而這里其實(shí)是兩個(gè)程序,Server和Client,這種跨進(jìn)程的信號(hào)不是簡(jiǎn)單地 connect 就可以的,而是需要通過 socket 來傳遞。 要讓兩個(gè)進(jìn)程通過 socket 進(jìn)行函數(shù)調(diào)用,首先需要將調(diào)用抽象成數(shù)據(jù)流的形式,這些信息通過 wl_closure_marshal 寫入 wl_closure 結(jié)構(gòu),再由 serialize_closure 變成數(shù)據(jù)流;等到了目標(biāo)進(jìn)程,從數(shù)據(jù)流中通過 wl_connection_demarshal 轉(zhuǎn)回 wl_closure 結(jié)構(gòu)。

1、wayland server 啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè) socket(_wl_display_add_socket),并將這個(gè) socket fd 加入 epoll 中,這樣一旦有有Client程序連接,epoll 就會(huì)通知Server,從而執(zhí)行回調(diào)函數(shù) socket_data。Client可能會(huì)有很多,用 epoll 會(huì)比較有效率。

2、當(dāng)有Client程序通過調(diào)用wl_display_connect 連接到 server, 在回調(diào)函數(shù) socket_data 中會(huì)調(diào)用 accept 創(chuàng)建新的 socket fd,緊接著創(chuàng)建 wl_client并在創(chuàng)建 wl_client 的時(shí)候,wl_client_create (display, fd) 將這個(gè) socket fd 加入 epoll,繼續(xù)監(jiān)聽新的 socket,為的是響應(yīng)從Client發(fā)過來的請(qǐng)求,回調(diào)函數(shù)是 wl_client_connect_data。

一個(gè) socket 負(fù)責(zé)監(jiān)聽Client連接,對(duì)于每個(gè)Client還有一個(gè)socket負(fù)責(zé)監(jiān)聽Client的請(qǐng)求。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,416評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,597評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,029評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,403評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,746評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容