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的進程,或者調用自己寫的界面,提示用戶此時網絡已經斷開,作出友好提醒。