承接上文
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,在我这篇文章。