guacamole学习小结(三)

承接上文

https://blog.csdn.net/qq_38781075/article/details/106994385

这次我们来聊一聊创建guacd的user线程前后发生的事情。

这篇文章将会以SSH为例讲解,并且是以第一个连接远程桌面的人的视角,也就是这个远程桌面的拥有者。

这篇文章涉及的参数比较多,推荐使用ctrl+f加关键词能找到我说的参数,我用括号标注了。

先从guac_client_init函数讲起,每个不同的远程协议都有不同的guac_client_init函数的实现。

首先先拿到一些参数,这些都是定义好的。

client->args = GUAC_SSH_CLIENT_ARGS;

参数内容就是这样,这是SSH的。

const char* GUAC_SSH_CLIENT_ARGS[] = {
    "hostname",
    "host-key",
    "port",
    "username",
    "password",
    "font-name",
    "font-size",
    "enable-sftp",
    "sftp-root-directory",
    "private-key",
    "passphrase",
#ifdef ENABLE_SSH_AGENT
    "enable-agent",
#endif
    "color-scheme",
    "command",
    "typescript-path",
    "typescript-name",
    "create-typescript-path",
    "recording-path",
    "recording-name",
    "recording-exclude-output",
    "recording-exclude-mouse",
    "recording-include-keys",
    "create-recording-path",
    "read-only",
    "server-alive-interval",
    "backspace",
    "terminal-type",
    "scrollback",
    "locale",
    "timezone",
    NULL
};

接着绑定一些回调函数,如

client->join_handler = guac_ssh_user_join_handler;
client->free_handler = guac_ssh_client_free_handler;

两个函数分别是用户加入时调用的函数和用户离开时释放的函数。

接着就是一个大循环了,这个循环之后就会创建user线程,之后的操作就跟这个远程桌面拥有者无关了,不管你是不是这个拥有者,接下来大家就一样了。像这样:

/* Add each received file descriptor as a new user */
    int received_fd;
    while ((received_fd = guacd_recv_fd(proc->fd_socket)) != -1) {

    guacd_proc_add_user(proc, received_fd, owner);

/* Future file descriptors are not owners */
    owner = 0;

    }

guacd_proc_add_user函数就是用来添加新的用户的,里面会创建user线程。接下来就主要讲这个函数。

这个线程首先是把参数(GUAC_SSH_CLIENT_ARGS)发送给web应用,当然是以Guacamole协议的格式发送的(接下来说的所有的命令收发都是用了这个协议)。

看那些参数(GUAC_SSH_CLIENT_ARGS)的名字大概能猜到上面意思,就是向web应用请求一些如hostname、port之类的,不过web应用不会马上把内容发送给guacd,可能先把下面结构体(__guac_handshake_handler_map)里面的命令发过来。下面是一个结构体数组,也是命令加回调函数的形式早就定义了的。定义了web应用可能发来的命令,大概是跟用户端的浏览器有关,大小屏幕等一些适配。

__guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
    {"size",     __guac_handshake_size_handler},
    {"audio",    __guac_handshake_audio_handler},
    {"video",    __guac_handshake_video_handler},
    {"image",    __guac_handshake_image_handler},
    {"timezone", __guac_handshake_timezone_handler},
    {NULL,       NULL}
};

前面是命令的名称,后面是回调函数,上面的东西,所有的协议都是一样的。值得一提的是,根据代码,不一定会把上面所有命令(__guac_handshake_handler_map)都发过来,不像那个我前面提到的那一长串参数(GUAC_SSH_CLIENT_ARGS),那个参数是要全发的。

这个只要web应用发了这个命令——"connect",就代表要回应那一长串参数的内容了,接着只需要接收就行了,user线程会调用一个函数去处理web应用发来的参数。

接着还会发送一个ready的报文,参数是用户ID。

有一张图比较好,我就不盗了。推荐看看下面这个链接的第二张图,虽然讲的是RDP,但是各个协议差别不会太大。

链接:https://blog.csdn.net/cherrybomb1111/article/details/67633366

处理完参数之后,就会绑定一些用户操作的回调函数,比如键盘的输入、鼠标的点击等。像这样(以SSH为例,不同协议绑定的回调函数不一样):

/* General mouse/keyboard/clipboard events */
        user->key_handler       = guac_ssh_user_key_handler;
        user->mouse_handler     = guac_ssh_user_mouse_handler;
        user->clipboard_handler = guac_ssh_clipboard_handler;

/* STDIN redirection */
        user->pipe_handler = guac_ssh_pipe_handler;

/* Updates to connection parameters */
        user->argv_handler = guac_ssh_argv_handler;
/* Display size change events */
        user->size_handler = guac_ssh_user_size_handler;

当走到这一步时,用户与服务器的连接是真正完成了。

然后user线程又会创建一个新的线程(暂时叫做input线程),input线程就是最终循环的线程,而user线程此时的作用改变了,变成等待input线程结束将其回收。

input线程里面就是最终的循环了,里面也有类似的回调机制。下面也是一个结构体,web应用按照规则要发送下面这个结构体数组(__guac_instruction_handler_map)内容的命令。

__guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
   {"sync",       __guac_handle_sync},
   {"mouse",      __guac_handle_mouse},
   {"key",        __guac_handle_key},
   {"clipboard",  __guac_handle_clipboard},
   {"disconnect", __guac_handle_disconnect},
   {"size",       __guac_handle_size},
   {"file",       __guac_handle_file},
   {"pipe",       __guac_handle_pipe},
   {"ack",        __guac_handle_ack},
   {"blob",       __guac_handle_blob},
   {"end",        __guac_handle_end},
   {"get",        __guac_handle_get},
   {"put",        __guac_handle_put},
   {"audio",      __guac_handle_audio},
   {"argv",       __guac_handle_argv},
   {"nop",        __guac_handle_nop},
   {NULL,         NULL}
};

input线程就一直死循环等待web应用发来的命令,其实就是用户发来的命令,比如键盘的输入、鼠标的点击等,直到用户退出。

上面结构体数组(__guac_instruction_handler_map)回调函数的内部就会用到远程桌面协议的函数了,拿那个鼠标函数(__guac_handle_mouse)举例:

int __guac_handle_mouse(guac_user* user, int argc, char** argv) {
    if (user->mouse_handler)
        return user->mouse_handler(
            user,
            atoi(argv[0]), /* x */
            atoi(argv[1]), /* y */
            atoi(argv[2])  /* mask */
        );
    return 0;
}

最后调用mouse_handler的回调函数,这个函数是之前绑定过的,可以看我文章中间写的,用ctrl+f搜索mouse_handler,在我这篇文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值