spice-gtk如何实现断网提醒

spice-gtk现状

使用过spice-gtk进行远程连接虚拟机的朋友应该都知道,正在连接虚拟机的过程中,如果网络断开,虚拟机窗口界面就会卡死,也不会给出任何提示信息,那么下面我们来实现spice-gtk网络超时后自动退出。

设置socket超时关闭选项

首先找到spice-channel.c这个是spice-gtk的核心网络层,找到setsockopt函数所在位置加入:

    int keepAlive = 1; // 设定 KeepAlive

    int keepIdle = 3; // 开始首次 KeepAlive 探测前的 TCP 空闭时间

    int keepInterval = 3; // 两次 KeepAlive 探测间的时间间隔

    int keepCount = 1; // 判定断开前的 KeepAlive 探测次数

    setsockopt(g_socket_get_fd(c->sock), SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
    setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
    setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
    setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

    int keepAlive = 1;    // 设定 KeepAlive
    int keepIdle = 3;     // 开始首次 KeepAlive 探测前的 TCP 空闭时间
    int keepInterval = 3; // 两次 KeepAlive 探测间的时间间隔
    int keepCount = 1;    // 判定断开前的 KeepAlive 探测次数

    if (setsockopt(g_socket_get_fd(c->sock), SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive)) == -1)
        printf("keepAlive error\n");
    if (setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle)) == -1)
        printf("keepIdle error\n");
    if (setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval)) == -1)
        printf("keepInterval error\n");
    if (setsockopt(g_socket_get_fd(c->sock), SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount)) == -1)
        printf("keepCount error\n");

    rc = setsockopt(g_socket_get_fd(c->sock), IPPROTO_TCP, TCP_NODELAY,
                    (const char*)&delay_val, sizeof(delay_val));

超时后的反应

超时候虚拟机会在下面地方作出反应:spice_channel_iterate这个函数是处理所有信息的循环,一旦socket出现问题,这边都是会做出反应的。在ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR);这个函数中添加一个参数变成:

ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR | G_IO_HUP);   G_IO_HUP就代表着我们io超时的情况,所有这边加了之后一旦网络超时了就会在这边触发,触发之后设置c->event = SPICE_CHANNEL_ERROR_IO;这个信号是传输到spicy.c里面的,毕竟spice-channel.c是库,spicy.c是主界面客户端,是spicy.c调用spice-channel.c库的,一旦库里面的socket出现了问题,会通过信号传输给spicy.c。

static gboolean spice_channel_iterate(SpiceChannel *channel)
{
    SpiceChannelPrivate *c = channel->priv;

    if (c->state == SPICE_CHANNEL_STATE_MIGRATING &&
        !g_coroutine_condition_wait(&c->coroutine, wait_migration, channel))
        CHANNEL_DEBUG(channel, "migration wait cancelled");

    /* flush any pending write and read */
    if (!c->has_error)
        SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel);
    if (!c->has_error)
        SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);

    if (c->has_error) {
        GIOCondition ret;

        if (!c->sock) {
            return FALSE;
        }
        /* We don't want to report an error if the socket was closed gracefully
         * on the other end (VM shutdown) */
        ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR | G_IO_HUP);

        if ((ret & G_IO_ERR) || (ret & G_IO_HUP)) {
            CHANNEL_DEBUG(channel, "channel got error");
            if (c->state > SPICE_CHANNEL_STATE_CONNECTING) {
                if (c->state == SPICE_CHANNEL_STATE_READY)
                    c->event = SPICE_CHANNEL_ERROR_IO;
                else
                    c->event = SPICE_CHANNEL_ERROR_LINK;
            }
        }
        return FALSE;
    }

    return TRUE;
}

spicy.c客户端信号出发

spice-channel.c里面设置c->event = SPICE_CHANNEL_ERROR_IO;之后会在spicy.c里面接收到这个信号,位置在:

static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
                               gpointer data)
{
    const GError *error = NULL;
    spice_connection *conn = data;
    char password[64];
    int rc;

    switch (event) {
        。
        。
        。
        case SPICE_CHANNEL_ERROR_IO:
            g_message("main channel: error io");
            connection_disconnect(conn);
        break;
        。
        。
        。
    }
}

可以直接调用connection_disconnect结束官方版spice-gtk的进程,或者调用自己写的界面,提示用户此时网络已经断开,作出友好提醒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章