欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Spice工作原理及代碼剖析:03 Channel:客戶(hù)端與服務(wù)器通信機制.docx

〇、基本原理

目前的Channel類(lèi)型枚舉值定義如下:

enum {    SPICE_CHANNEL_MAIN = 1,    SPICE_CHANNEL_DISPLAY,    SPICE_CHANNEL_INPUTS,    SPICE_CHANNEL_CURSOR,    SPICE_CHANNEL_PLAYBACK,    SPICE_CHANNEL_RECORD,    SPICE_CHANNEL_TUNNEL, // 沒(méi)定義USE_TUNNEL,則此Channel無(wú)效    SPICE_END_CHANNEL};


每個(gè)Channel就是客戶(hù)端與服務(wù)端一個(gè)的網(wǎng)絡(luò )連接。

客戶(hù)端將每個(gè)Channel實(shí)現為一個(gè)單獨的線(xiàn)程,實(shí)現方式是定義一個(gè)以單獨線(xiàn)程運轉的RedChannel基類(lèi),然后從此基類(lèi)中派生所需要的具體功能類(lèi),客戶(hù)端Channel類(lèi)包括:RedClient、DisplayChannel、CursorChannel、InputsChannel、PlaybackChannel、RecordChannel、TunnelChannel。

服務(wù)端Channel的工作相對復雜一點(diǎn),部分Channel工作在Qemu主線(xiàn)程,另一部分在工作在libspice的單獨線(xiàn)程中,服務(wù)端的網(wǎng)絡(luò )模型參見(jiàn)文檔02。

客戶(hù)端啟動(dòng)后會(huì )首先與服務(wù)器建立連接,此連接即為MAIN_CHANNEL,MAIN_CHANNEL建立起來(lái)之后,客戶(hù)端首先向服務(wù)器發(fā)送查詢(xún)命令,請求服務(wù)器支持的Channel類(lèi)型,然后客戶(hù)端對所有支持的Channel一一創(chuàng )建對應的Channel類(lèi)實(shí)例,每個(gè)實(shí)例都會(huì )開(kāi)啟自己的工作線(xiàn)程并向服務(wù)端發(fā)起連接請求,建立網(wǎng)絡(luò )連接,下面是大致的工作流程及相關(guān)代碼:

1、客戶(hù)端首先會(huì )與服務(wù)器之間建立一個(gè)MAIN_CHANNEL,客戶(hù)端一側表現為RedClient,此Channel由客戶(hù)端主動(dòng)發(fā)起創(chuàng )建:

     Application構造函數初始化時(shí)會(huì )初始化其RedClient成員_client,RedClient初始化時(shí)就會(huì )啟動(dòng)RedChannel類(lèi)的start函數來(lái)創(chuàng )建工作線(xiàn)程,

     具體的網(wǎng)絡(luò )連接操作在工作線(xiàn)程的主循環(huán)內部完成,具體由RedChannelBase基類(lèi)實(shí)現的connect、link兩個(gè)函數來(lái)實(shí)現網(wǎng)絡(luò )連接的建立和Channel link的建立。工作線(xiàn)程的主循環(huán)只處理簡(jiǎn)單的幾個(gè)事件,其余消息處理交給其成員ProcessLoop的消息循環(huán)來(lái)完成。

2、MAIN_CHANNEL建立后,客戶(hù)端RedClient進(jìn)入其ProcessLoop的消息循環(huán),等待服務(wù)端消息。服務(wù)端完成MAIN_CHANNEL相關(guān)工作的初始化(主要就是向Qemu注冊一個(gè)網(wǎng)絡(luò )消息處理結點(diǎn),具體參見(jiàn)select網(wǎng)絡(luò )模型中關(guān)于Watch的講解)后,會(huì )通過(guò)此Channel發(fā)送SPICE_MSG_MAIN_INIT消息,告知客戶(hù)端可以進(jìn)行后續的初始化工作了。

3、客戶(hù)端執行初始化工作后,會(huì )發(fā)送消息請求支持的Channel列表,服務(wù)端將注冊(參見(jiàn)下面Server端Channel實(shí)現)的Channel列表發(fā)送給客戶(hù)端,并通知客戶(hù)端創(chuàng )建Channel。

4、客戶(hù)端其余Channel的創(chuàng )建都通過(guò)一個(gè)統一的接口,從工廠(chǎng)類(lèi)中生產(chǎn)各個(gè)Channel類(lèi)實(shí)例。參見(jiàn)具體實(shí)現如下(略去部分檢查代碼):

void RedClient::create_channel(uint32_t type, uint32_t id){    ChannelFactory* factory = find_factory(type);    RedChannel* channel = factory->construct(*this, id);    _channels.push_back(channel);     channel->start();   // 啟動(dòng)RedChannel類(lèi)的工作線(xiàn)程    channel->connect(); // 激活condition     _migrate.add_channel(new MigChannel(type, id, channel->get_common_caps(), channel->get_caps()));}


建立Channel的詳細步驟及消息傳遞流程如下:(s表示server代碼,c表示client代碼)

s: reds_init_net, 創(chuàng )建listen socket,增加監聽(tīng)watch_add(reds_accept)

c: RedChannel::run-》RedChannelBase::connect() -》RedPeer::connect_unsecure發(fā)送連接請求,等待服務(wù)端accept

s: reds_accept-》reds_accept_connection,

            reds_handle_new_link-》obj->done = reds_handle_read_header_done,

                            async_read_handler-》cb_read 堵塞,等待客戶(hù)端發(fā)送link消息

c: RedChannelBase::link() -》link_mess.channel_type = _type,send 發(fā)送link請求,recive等待

s: obj->done -》reds_handle_read_header_done

            -》(read_header完了,繼續從流中讀取數據)obj->done = reds_handle_read_link_done,async_read_handler

            -》cb_read,obj->done(reds_handle_read_link_done) -》   obj->done = reds_handle_ticket,async_read_handler

            -》cb_read,obj->done(reds_handle_ticket) {

                case channel_type of SPICE_CHANNEL_MAIN:reds_handle_main_link

                otherwise:reds_handle_other_links

              }        

 

c->s: SPICE_CHANNEL_MAIN s: reds_handle_main_link,此過(guò)程的詳細步驟如上

s->c: SPICE_MSG_MAIN_INIT, c: handle_init

c->s: SPICE_MSGC_MAIN_ATTACH_CHANNELS s: reds_send_channels

s->c: SPICE_MSG_MAIN_CHANNELS_LIST c: handle_channels

 

一、Server端 Channel 實(shí)現

1、服務(wù)端注冊Channel。

服務(wù)端最關(guān)鍵的數據結構為RedsState,又有一個(gè)別名叫SpiceServer,Server端會(huì )維護一個(gè)全局的RedsState變量,用來(lái)存儲全局數據。該全局數據結構在CoreInterface初始化時(shí)由Qemu負責發(fā)起創(chuàng )建,并通過(guò)VDI接口將此對象傳遞給libspice。

RedsState中一個(gè)數據成員為Channel* channels,Server端通過(guò)此變量來(lái)維護一個(gè)Channel鏈表,所有Server端支持的Channel都需要通過(guò)reds_register_channel注冊到此鏈表。除了InputChannle是在spice_server_init中注冊的外(即:在CoreInterface初始化時(shí)注冊的),其余Channel都是在Qemu進(jìn)行虛擬設備初始化時(shí),通過(guò)調用spice_server_add_interface函數注冊VDI時(shí)注冊的,列舉如下:

// spice_server_init

inputs_init 中注冊:SPICE_CHANNEL_INPUTS   

// spice_server_add_interface(SPICE_INTERFACE_QXL)

red_dispatcher_init 中注冊:SPICE_CHANNEL_DISPLAY、SPICE_CHANNEL_CURSOR

// spice_server_add_interface(SPICE_INTERFACE_PLAYBACK)                

snd_attach_playback 中注冊:SPICE_CHANNEL_PLAYBACK     

// spice_server_add_interface(SPICE_INTERFACE_RECORD)      

snd_attach_record 中注冊:SPICE_CHANNEL_RECORD 

// spice_server_add_interface(SPICE_INTERFACE_NET_WIRE)            

red_tunnel_attach 中注冊:SPICE_CHANNEL_TUNNEL                 

所謂注冊Channel,就是初始化一個(gè)Channel對象,然后將其插入到RedsState的channels鏈表中供后續的訪(fǎng)問(wèn)處理。Channel結構定義如下:

typedef struct Channel {    struct Channel *next;    uint32_t type;    uint32_t id;    int num_common_caps;    uint32_t *common_caps;    int num_caps;    uint32_t *caps;    void (*link)(struct Channel *, RedsStreamContext *peer, int migration, int num_common_caps,                 uint32_t *common_caps, int num_caps, uint32_t *caps);    void (*shutdown)(struct Channel *);    void (*migrate)(struct Channel *);    void *data;} Channel;


Channel注冊主要是初始化Channel的數據和三個(gè)回調函數:link、shutdown、migrate,用來(lái)對Channel進(jìn)行操作。其中數據成員type就是最開(kāi)始我們列出的枚舉值,用以標識當前Channel類(lèi)型。下面是Spice中Display Channel初始化的代碼:

   

reds_channel = spice_new0(Channel, 1);    reds_channel->type = SPICE_CHANNEL_DISPLAY;    reds_channel->id = qxl->id;    reds_channel->link = red_dispatcher_set_peer;    reds_channel->shutdown = red_dispatcher_shutdown_peer;    reds_channel->migrate = red_dispatcher_migrate;    reds_channel->data = dispatcher;reds_register_channel(reds_channel);


2、Channel的三個(gè)回調函數:link、shutdown、migrate,其中link是在客戶(hù)端與服務(wù)端建立Channel連接的時(shí)候被調用的,下面對其進(jìn)行詳細解說(shuō)

    首先,客戶(hù)端Channel的工作線(xiàn)程啟動(dòng)后,發(fā)起link()操作,服務(wù)端在reds_handle_new_link中響應,最終會(huì )調用各個(gè)注冊的Channel的link()函數完成link操作。這個(gè)過(guò)程中,Server端利用了一種異步消息處理機制,主要是通過(guò)一個(gè)結構加一個(gè)處理函數來(lái)實(shí)現,簡(jiǎn)單介紹一下此消息處理機制:

   AsyncRead是此消息處理機制的一個(gè)非常重要的輔助結構體,定義如下:

   typedef struct AsyncRead {    RedsStreamContext *peer; // 用來(lái)處理網(wǎng)絡(luò )數據流的數據對象    void *opaque;  // 傳遞數據用的,通常作為done函數的參數    uint8_t *now;  // 數據起始,用來(lái)保存接收數據    uint8_t *end;  // 數據結束,通常用來(lái)計算數據長(cháng)度    void (*done)(void *opaque); // 消息處理函數    void (*error)(void *opaque, int err); // 錯誤處理函數   } AsyncRead;


   // 異步消息處理函數,最主要的參數就是data,是一個(gè)AsyncRead指針(省略異常處理,具體異常處理方法參考源代碼)

  

 static void async_read_handler(int fd, int event, void *data){    AsyncRead *obj = (AsyncRead *)data;     for (;;) {        int n = obj->end - obj->now;        if ((n = obj->peer->cb_read(obj->peer->ctx, obj->now, n)) <= 0) {           // 異常處理省略……        } else { // 正常情況處理            obj->now += n;            if (obj->now == obj->end) { // 接收數據完畢了就執行done,否則繼續循環(huán)接收數據                async_read_clear_handlers(obj);                obj->done(obj->opaque);                return;            }        }    }}


利用上面的消息處理機制,Server端的reds_handle_new_link函數最終會(huì )找到具體的new_link(即:Channel)類(lèi)型,然后調用此Channel注冊時(shí)注冊的的link回調函數。

 

link函數的主要工作

對于不同的Channel,link的工作不同,并且Display、Cursor的實(shí)現又與其他Channel有些差異,各個(gè)Channel對應的link函數如下:

SPICE_CHANNEL_DISPLAY   red_dispatcher_set_peerSPICE_CHANNEL_CURSOR        red_dispatcher_set_cursor_peerSPICE_CHANNEL_INPUTS        inputs_linkSPICE_CHANNEL_PLAYBACK snd_set_playback_peerSPICE_CHANNEL_RECORD        snd_set_record_peer


link最主要的工作之一就是網(wǎng)絡(luò )連接的處理,即選擇網(wǎng)絡(luò )事件處理模型,設置網(wǎng)絡(luò )事件響應函數等。spice用了兩種模型:select、epoll

 

1)DisplayChannel的link實(shí)現:

Display的實(shí)現中引入了一個(gè)dispatcher,實(shí)現了各種對外的接口,接口本身大都不做實(shí)際工作,主要負責消息轉發(fā)。

QXL_INTERFACE的初始化函數red_dispatcher_init會(huì )啟動(dòng)一個(gè)專(zhuān)門(mén)的工作線(xiàn)程red_worker_main,此線(xiàn)程有三個(gè)職責,其中之一就是接收dispatcher接口轉發(fā)過(guò)來(lái)的消息,并進(jìn)行實(shí)際的消息處理工作。dispatcher和工作線(xiàn)程之間的通信是通過(guò)一個(gè)socketpair,利用epoll模型機型通信。

此處link函數的實(shí)現同樣也是通過(guò)dispatcher接口red_dispatcher_set_peer將RED_WORKER_MESSAGE_DISPLAY_CONNECT消息發(fā)送給red_worker_main,

red_worker_main中,epoll_wait等待事件,然后調用具體事件處理函數來(lái)處理,此處會(huì )調用handle_dev_input函數。handle_dev_input函數響應RED_WORKER_MESSAGE_DISPLAY_CONNECT消息,調用handle_new_display_channel進(jìn)行具體link工作(簡(jiǎn)化版本):

static void handle_new_display_channel(RedWorker *worker, RedsStreamContext *peer, int migrate){    DisplayChannel *display_channel;    size_t stream_buf_size;      // 先斷開(kāi)原來(lái)的Channel連接,所做工作基本與此函數后面的工作相反    red_disconnect_display((RedChannel *)worker->display_channel);     // __new_channel函數會(huì )new一個(gè)RedChannel,并用該函數的參數初始化RedChannel的成員    // RedChannel數據成員比較多,只揀最主要的說(shuō)明一下:// struct RedChannel {//     EventListener listener;     // 事件觸發(fā)器,網(wǎng)絡(luò )事件處理用//     spice_parse_channel_func_t parser; // 用途待查,網(wǎng)絡(luò )通信解碼器??//     struct RedWorker *worker; // 用以保存RedWorker指針//     RedsStreamContext *peer;  // 用以保存RedsStreamContext指針,此指針在響應客戶(hù)端連接時(shí)創(chuàng  )建,網(wǎng)絡(luò )通信的主要處理對象////     Ring pipe; // 用途待查明,聲明為Ring,表明是一個(gè)鏈表,而非一個(gè)鏈表內的一項。如果聲明為RingItem,表明其容器為一個(gè)Item////     disconnect_channel_proc disconnect;  // 下面四個(gè)是回調函數//     hold_item_proc hold_item;       // 針對不同的Channel有不同的實(shí)現//     release_item_proc release_item;    // 通過(guò)__new_channel函數//     handle_message_proc handle_message; // 的參數傳入來(lái)初始化// };// 初始化上述結構后,__new_channel調用// epoll_ctl(worker->epoll, EPOLL_CTL_ADD, peer->socket, &event)// 將該channel對應的網(wǎng)絡(luò )連接socket加入到red_worker_main的epoll事件監聽(tīng)// 列表中,以在red_worker_main中處理此Channel的網(wǎng)絡(luò )事件if (!(display_channel = (DisplayChannel *)__new_channel(worker, sizeof(*display_channel),SPICE_CHANNEL_DISPLAY, peer,migrate, handle_channel_events,red_disconnect_display,display_channel_hold_item,display_channel_release_item,display_channel_handle_message))) {        return;    }     ring_init(&display_channel->palette_cache_lru);    red_display_init_streams(display_channel);    red_display_share_stream_buf(display_channel);    red_display_init_glz_data(display_channel);    worker->display_channel = display_channel;     on_new_display_channel(worker);}


2)CursorChannel的link實(shí)現:

工作流程與DisplayChannel一樣,通過(guò)dispatcher將消息發(fā)送給工作線(xiàn)程處理。

消息:RED_WORKER_MESSAGE_CURSOR_CONNECT

處理函數:red_connect_cursor,所做工作基本一樣:

 

static void red_connect_cursor(RedWorker *worker, RedsStreamContext *peer, int migrate){    CursorChannel *channel;    red_disconnect_cursor((RedChannel *)worker->cursor_channel);if (!(channel = (CursorChannel *)__new_channel(worker, sizeof(*channel),           SPICE_CHANNEL_CURSOR, peer, migrate,handle_channel_events,red_disconnect_cursor,cursor_channel_hold_item,cursor_channel_release_item,channel_handle_message))) {        return;    }     ring_init(&channel->cursor_cache_lru);    worker->cursor_channel = channel;    on_new_cursor_channel(worker);}

3)InputChannel的link實(shí)現:

與上述兩者不同的是,InputChannel的網(wǎng)絡(luò )處理放在了主循環(huán)的select模型中處理:

static void inputs_link(Channel *channel, RedsStreamContext *peer,int migration,int num_common_caps, uint32_t *common_caps, int num_caps,uint32_t *caps){    InputsState *inputs_state;    inputs_state = spice_new0(InputsState, 1);     // 一些初始化……     peer->watch = core->watch_add(peer->socket, SPICE_WATCH_EVENT_READ,                                  inputs_event, inputs_state);     SpiceMarshaller *m;    SpiceMsgInputsInit inputs_init;    m = marshaller_new_for_outgoing(inputs_state, SPICE_MSG_INPUTS_INIT);    inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);    spice_marshall_msg_inputs_init(m, &inputs_init);}


4)PLAYBACK、Record Channel的link實(shí)現:

大致流程與Display一樣,但網(wǎng)絡(luò )處理與InputChannel一樣,是通過(guò)watch_add方式采用select模型。

static void snd_set_record_peer(Channel *channel, RedsStreamContext *peer,int migration, int num_common_caps, uint32_t *common_caps, int num_caps,                                uint32_t *caps){    snd_disconnect_channel(worker->connection);if (!(record_channel = (RecordChannel *)__new_channel(worker, sizeof(*record_channel),SPICE_CHANNEL_RECORD,peer,migration,        snd_record_send,snd_record_handle_message,snd_record_on_message_done,snd_record_cleanup))) {        goto error_2;    }     on_new_record_channel(worker);     if (worker->active) {        spice_server_record_start(st->sin);    }    snd_record_send(worker->connection);    return;}


3、Channel建立起來(lái)之后,服務(wù)端開(kāi)始推送數據到客戶(hù)端,同時(shí)響應客戶(hù)端的請求,這里涉及到兩大類(lèi)不同的交互:用戶(hù)交互請求、實(shí)時(shí)數據推送

1)用戶(hù)交互請求

用戶(hù)交互請求都通過(guò)InputChannel進(jìn)行網(wǎng)絡(luò )數據收發(fā)處理,上面分析過(guò),InputChannel是通過(guò)watch_add的方式利用select模型在主循環(huán)中收發(fā)網(wǎng)絡(luò )信息的。

   InputChannel link建立時(shí),向系統注冊了自己的消息處理函數:

   watch_add(peer->socket, SPICE_WATCH_EVENT_READ, inputs_event,

 inputs_state) // 從第二個(gè)參數可以看出,InputChannel只關(guān)心讀事件

   網(wǎng)絡(luò )事件到達時(shí),系統將調用input_event函數,同時(shí)將inputs_state作為其opaque參數傳入。

   input_event中對于輸入、輸出事件分別調用handle_incoming、handle_outgoing來(lái)進(jìn)行處理,因此inputs_state比較重要的成員包括InComingHandler和OutGoinghandler,以及負責網(wǎng)絡(luò )通信的RedsStreamContext。對于InputChannel來(lái)說(shuō),OutGoinghandler目前是沒(méi)有用的。

   InComingHandler中最主要的成員是handle_message函數,此處對應于inputs_handle_input,主要響應客戶(hù)端發(fā)來(lái)的鍵盤(pán)鼠標消息:

static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message){    InputsState *state = (InputsState *)opaque;    uint8_t *buf = (uint8_t *)message;    SpiceMarshaller *m;     switch (type) {    case SPICE_MSGC_INPUTS_KEY_DOWN:        case SPICE_MSGC_INPUTS_KEY_UP:    case SPICE_MSGC_INPUTS_MOUSE_MOTION:    case SPICE_MSGC_INPUTS_MOUSE_POSITION:    case SPICE_MSGC_INPUTS_MOUSE_PRESS:    case SPICE_MSGC_INPUTS_MOUSE_RELEASE:    case SPICE_MSGC_INPUTS_KEY_MODIFIERS:    case SPICE_MSGC_DISCONNECTING:        break;    default:        red_printf("unexpected type %d", type);    }}


   2)實(shí)時(shí)數據推送

   主要是音頻、視頻數據從Server端實(shí)時(shí)推送到Client。

   視頻包括兩部分:Screen、Cursor

   音頻包括兩部分:Playback、Record(Record屬于Client -> Server)  

   Server端視頻部分涉及到三大模塊:Qxl Qemu device、Qxl Guest OS driver、Channel處理線(xiàn)程red_worker_man

   關(guān)于Qxl Device、Qxl Driver將單獨分析,此處只關(guān)注Channel部分,即red_worker_main的工作。

   Server端音頻部分相對簡(jiǎn)單一些,應該全部都在音頻設備中完成:(這里只關(guān)注了ac97,其他音頻設備工作機制類(lèi)似)

   AUD_register_card() ->

   audio_init() ->qemu_new_timer (vm_clock, audio_timer, s)

// 注意,audio_init 調用qemu_new_timer只是初始化glob_audio_state的timer,而不

// 是立即執行audio_timer函數,因為此時(shí)glob_audio_state這個(gè)關(guān)鍵的全局數據對象

// 還沒(méi)有初始化完,audio_timer函數執行時(shí)所需要的很多信息要等glob_audio_state

// 初始化完后才能正常訪(fǎng)問(wèn)。

   ac97_on_reset() -> ac97_on_reset() -> mixer_reset() -> reset_voices() ->

        {open_voice(這里注冊VDI接口), AUD_set_active_out/in}

->audio_reset_timer() 激活timer

 

   AUD_register_card 和 ac97_on_reset 是在ac97_initfn函數中順序調用的,即:

    AUD_register_card()先做初始化,主要是初始化全局對象,glob_audio_state

    ac97_on_reset() :然后開(kāi)始干活,包括注冊VDI,注冊/激活timer等

 

   audio_timer() -> audio_run() --> audio_run_out() --> >pcm_ops->run_out --> line_out_run/line_in_run

  (注意,timer函數本身不是一個(gè)循環(huán),而是被循環(huán)調用,關(guān)于timer工作機制參照文檔02)

 

   具體的執行部分在line_out_run/line_in_run  

   音頻、視頻的工作線(xiàn)程:red_worker_main、line_out_run/line_in_run

      1) 音頻輸出:line_out_run

      音頻部分會(huì )調用spice的幾個(gè)接口:

void spice_server_playback_start(SpicePlaybackInstance *sin);

void spice_server_playback_stop(SpicePlaybackInstance *sin);

void spice_server_playback_get_buffer(SpicePlaybackInstance *sin, uint32_t **samples, uint32_t *nsamples);

void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples);

AUD_set_active_out 時(shí)會(huì )調用 spice_server_playback_start

main_loop循環(huán)執行timer,timer調用line_out_run函數,此函數大致流程如下:

static int line_out_run (HWVoiceOut *hw, int live){    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);    decr = rate_get_samples (&hw->info, &out->rate);     while (samples) {        if (!out->frame) {            spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); // 從spice中獲取緩沖區            out->fpos = out->frame;        }        if (out->frame) {            hw->clip (out->fpos, hw->mix_buf + rpos, len); // 虛擬聲卡捕獲音頻數據應該是放在mix_buf中            out->fsize -= len;            out->fpos  += len;            if (out->fsize == 0) {                spice_server_playback_put_samples (&out->sin, out->frame);// 往緩沖區寫(xiě)數據,實(shí)際會(huì )將數據發(fā)送到客戶(hù)端                out->frame = out->fpos = NULL;            }        }        rpos = (rpos + len) % hw->samples;        samples -= len;    }}


    2) 視頻輸出:

    A、red_worker_main (on spice server)

   

 void *red_worker_main(void *arg){    RedWorker worker;     red_init(&worker, (WorkerInitData *)arg);    red_init_quic(&worker);    red_init_lz(&worker);    red_init_jpeg(&worker);    red_init_zlib(&worker);    worker.epoll_timeout = INF_EPOLL_WAIT;    for (;;) {        num_events = epoll_wait(worker.epoll, events, MAX_EPOLL_SOURCES, worker.epoll_timeout);        red_handle_streams_timout(&worker);         if (worker.display_channel && worker.display_channel->glz_dict) {              red_display_handle_glz_drawables_to_free(worker.display_channel);        } for (event = events, end = event + num_events; event < end; event++) {            EventListener *evt_listener = (EventListener *)event->data.ptr;             if (evt_listener->refs > 1) {                evt_listener->action(evt_listener, event->events);                if (--evt_listener->refs) {                    continue;                }            }            free(evt_listener); // refs == 0 , release it!        }         if (worker.running) {            int ring_is_empty;// 處理Cursor command,從虛擬設備中獲取數據            red_process_cursor(&worker, MAX_PIPE_SIZE, &ring_is_empty);// 處理Display command,從虛擬設備中獲取實(shí)時(shí)數據            red_process_commands(&worker, MAX_PIPE_SIZE,&ring_is_empty);}        red_push(&worker); // 往客戶(hù)端push顯示命令    }    red_printf("exit");    return 0;}


二、客戶(hù)端Channel實(shí)現

1、Channel類(lèi)介紹

客戶(hù)端Channel通過(guò)基類(lèi)RedChannel進(jìn)行了基本功能的封裝,該基類(lèi)的繼承層也非常深:

RedChannel: public RedChannelBase: public RedPeer: protected EventSources::Socket: public EventSource

其他具體的Channel則派生于RedChannel,各基類(lèi)的功能函數如下(豎排):

RedChannel:public RedChannelBase:  public RedPeer:  protected EventSources::Socket:public EventSource

start         get_type        connect_unsecure     get_socket() = 0       action

connect       get_id          connect_secure  

disconnect    connect         disconnect

recive                        do_send

post_message                  send

get_message_handler           recive

worker_main                     

Message處理:_message_handler

run

可以簡(jiǎn)單區分一下每個(gè)基類(lèi)的職責:

RedChannel:開(kāi)啟工作線(xiàn)程,分發(fā)、處理消息

RedChannelBase:維護type、id信息,連接網(wǎng)絡(luò )

RedPeer:網(wǎng)絡(luò )數據收發(fā)處理

EventSources::Socket:Socket類(lèi)型的事件虛基類(lèi)

EventSources:事件資源公共基類(lèi)

 

2、消息處理

客戶(hù)端內部實(shí)現了消息處理機制,不看懂消息處理機制,無(wú)法真正理解Client的執行流程,下面是消息處理實(shí)現相關(guān)的類(lèi):

基類(lèi):

class RedChannel::MessageHandler {public:    MessageHandler() {}    virtual ~MessageHandler() {}    virtual void handle_message(RedPeer::CompoundInMessage& message) = 0;};


消息模板:

template <class HandlerClass, unsigned int channel_id>class MessageHandlerImp: public RedChannel::MessageHandler {public:    MessageHandlerImp(HandlerClass& obj);    ~MessageHandlerImp() { delete [] _handlers; };    virtual void handle_message(RedPeer::CompoundInMessage& message);    typedef void (HandlerClass::*Handler)(RedPeer::InMessage* message);    void set_handler(unsigned int id, Handler handler); private:    HandlerClass& _obj;    unsigned int _max_messages;    spice_parse_channel_func_t _parser;    Handler *_handlers;};


消息實(shí)例:

class MainChannelLoop: public MessageHandlerImp<RedClient, SPICE_CHANNEL_MAIN> {public:    MainChannelLoop(RedClient& client): MessageHandlerImp<RedClient, SPICE_CHANNEL_MAIN>(client) {}};


模板類(lèi)中封裝了消息處理的公共函數,對于不同的Channel,需要實(shí)例化自己的消息處理類(lèi),以MAIN_CHANNLE為例,看看Client的Channel建立及消息處理機制實(shí)現。Client中MAIN_CHANNEL通過(guò)RedClient類(lèi)進(jìn)行封裝,其建立及消息機制初始化過(guò)程如下:

1) Application默認構造函數執行時(shí),構造其RedClient成員_client

2)實(shí)例化基類(lèi)的 RedChannel::_message_handler

       RedClient::RedClient(Application& application)

        : RedChannel(*this, SPICE_CHANNEL_MAIN,0, new MainChannelLoop(*this)) …

3)初始化MessageHandler的回調函數

MainChannelLoop* message_loop =

             static_cast<MainChannelLoop*>(get_message_handler());

      message_loop->set_handler(MSG_MIGRATE, &RedClient::handle_migrate);

      message_loop->set_handler(MSG_LIST, &RedClient::handle_channels);

       ……

4) 啟動(dòng)工作線(xiàn)程

start(){

            _worker = new Thread(RedChannel::worker_main, this);

            ……

      }

5) 工作線(xiàn)程中處理消息

工作線(xiàn)程起來(lái)后會(huì )wait condition,RedClient的condition由LoginDialog的handle_connect函數調用application().connect來(lái)激活。

注:LoginDialog是用CEGUI庫寫(xiě)的界面,button 響應函數的寫(xiě)法如下:

    add_bottom_button(wnd, // 父窗

res_get_string(STR_BUTTON_CONNECT),                         CEGUI::Event::Subscriber(&LoginDialog::handle_connect, this),//響應函數

        x_pos);

工作線(xiàn)程詳細執行流程:

RedChannel::worker_main(){        RedChannel::run(){       // 這里用了condition 相關(guān)的pthread庫函數來(lái)進(jìn)行信號控制// pthread_cond_signal、pthread_cond_wait……       // 工作線(xiàn)程啟動(dòng)后會(huì )等待RedClient的connect、disconnect、quit等操作       // RedClient的connect會(huì )pthread_cond_signal(cond)       _action_cond.wait --》 pthread_cond_wait(cond) // 等吧        // _action 用來(lái)標識等來(lái)的是個(gè)什么操作,一共有三個(gè):// CONNECT_ACTION、DISCONNECT_ACTION、QUIT_ACTION       // 后面兩個(gè)是做一些清理工作,主要是第一個(gè):CONNECT_ACTION       case _action of CONNECT_ACTION:{          RedChannelBase::connect // 調用基類(lèi)的connect,進(jìn)行網(wǎng)絡(luò )連接          _marshallers = spice_message_marshallers_get // 初始化marshallers          on_connect --> _migrate.add_channel()          on_event {send_messages,recive_messages}                 // ProcessLoop 消息循環(huán)          _loop.add_socket(*this); // --> add_event, 往_loop中的// _event_sources增加event,以進(jìn)行監聽(tīng)……// 注意了:上面使用了EventSources中的socket類(lèi),用于網(wǎng)絡(luò )事件的監聽(tīng)// 具體方法參見(jiàn)代碼:// HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); // 創(chuàng  )建一個(gè)Event,然后:// WSAEventSelect(socket.get_socket(), event,FD_READ | FD_WRITE | FD_CLOSE)// 上面這個(gè)函數將event 與 socket綁定,當socket上有事件被激活時(shí),event也將被激活        _loop.run() {           _event_sources.wait_events(){            DWORD wait_res = MsgWaitForMultipleObjectsEx(_handles.size(),  &_handles[0], timeout_ms,                                                 QS_ALLINPUT, 0); // 此函數用來(lái)等待事件,對于同步事件,此函數返回前會(huì )修改事件狀態(tài)                                   int event_index = wait_res - WAIT_OBJECT_0;            _events[event_index]->action() --> RedChannel::on_event{               send_messages(){                get_outgoing_message                send               }               recive_messages(){                RedPeer::recive()                on_message_recived()                _message_handler->handle_message(*(*message)){                   (_obj.*_handlers[type])(&main_message); // 通過(guò)實(shí)例調用其成員函數,與SetHandler是所賦的成員函數對象一致                }               }            }           }   


    3、DisplayChannel、CursorChannel、InputChannel的創(chuàng )建啟動(dòng)過(guò)程

    1)注冊ChannelFactory

void Application::register_channels(){    if (_enabled_channels[SPICE_CHANNEL_DISPLAY]) { //注冊channelFactory        _client.register_channel_factory(DisplayChannel::Factory());    }    ……}


2) 創(chuàng )建:響應服務(wù)端發(fā)過(guò)來(lái)的SPICE_MSG_MAIN_CHANNELS_LIST消息,調用handle_channels,創(chuàng )建各個(gè)channel

void RedClient::handle_channels(RedPeer::InMessage* message){    SpiceMsgChannels *init = (SpiceMsgChannels *)message->data();    SpiceChannelId* channels = init->channels;    for (unsigned int i = 0; i < init->num_of_channels; i++) {        create_channel(channels[i].type, channels[i].id);    }}


3)啟動(dòng):與RedClient類(lèi)似,這里在create_channel中,創(chuàng )建channel后,調用start函數啟動(dòng)channel主線(xiàn)程worker_main

void RedClient::create_channel(uint32_t type, uint32_t id){    ChannelFactory* factory = find_factory(type);    RedChannel* channel = factory->construct(*this, id);    _channels.push_back(channel);    channel->start();   // 創(chuàng  )建工作線(xiàn)程    channel->connect(); // 工作線(xiàn)程會(huì )等待CONNECT_ACTION condition來(lái)激活線(xiàn)程_migrate.add_channel(new MigChannel(type, id, channel->get_common_caps(), channel->get_caps()));}

 

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
nginx源碼分析
Java NIO框架Netty教程(十六)
用Golang處理每分鐘百萬(wàn)級請求
22 Go常見(jiàn)的并發(fā)模式和并發(fā)模型
Linux工作隊列實(shí)現機制
Mjpeg-streamer源碼分析(一)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久