WebsocketConnection с использованием libsoup-2.4 иногда блокирует поток пользовательского интерфейса GTK и не позволяет открыть главное окно
У меня есть простое приложение GTK, написанное на Vala с подключением к веб-сокету (с использованием libsoup-2.4). Проблема: мое приложение иногда зависает при запуске (каждые ~10 запусков) и вообще не отображает окно графического интерфейса из-за блокировки где-то в libsoup при подключении к сокету.
Вала 0,40,25 с
- gtk + -3,0
- glib-2.0
- gobject-2.0
- libsoup-2.4
Вот код:
using Gtk;
class WebsocketConnection {
private Soup.WebsocketConnection websocket_connection;
public signal void ws_message(int type, string message);
public signal void connection_succeeded();
public signal void connection_established();
public signal void connection_failed();
public signal void connection_disengaged();
private string host;
public WebsocketConnection(string host) {
this.host = host;
}
private static string decode_bytes(Bytes byt, int n) {
return (string)byt.get_data();
}
public void init_connection_for(string host) {
MainLoop loop = new MainLoop();
var socket_client = new Soup.Session();
string url = "ws://%s:8080/".printf(host);
message(@"connect to $url");
var websocket_message = new Soup.Message("GET", url);
socket_client.websocket_connect_async.begin(websocket_message, null, null, null, (obj, res) => {
try {
websocket_connection = socket_client.websocket_connect_async.end(res);
message("Connected!");
connection_succeeded();
if (websocket_connection != null) {
websocket_connection.message.connect((type, m_message) => {
ws_message(type, decode_bytes(m_message, m_message.length));
});
websocket_connection.closed.connect(() => {
message("Connection closed");
connection_disengaged();
});
}
} catch (Error e) {
message("Remote error: " + e.message + " " + e.code.to_string());
connection_failed();
loop.quit();
}
loop.quit();
});
loop.run();
}
}
class Main : Gtk.Application {
public Main() {
Object(
application_id: "com.github.syfds.websocket-libsoup-vala-example",
flags : ApplicationFlags.FLAGS_NONE
);
}
protected override void activate() {
var window = new ApplicationWindow(this);
window.title = "Hello, World!";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size(350, 70);
window.destroy.connect(Gtk.main_quit);
var grid = new Grid();
grid.orientation = Orientation.VERTICAL;
grid.column_spacing = 5;
grid.row_spacing = 5;
var main_label = new Label("...");
var websocket_host_input = new Entry();
var send_message_btn = new Button.with_label("Connect");
var websocket_host = "192.168.1.252";
var connection = new WebsocketConnection(websocket_host);
connection.connection_succeeded.connect(() => {
message("Connection succeeded");
main_label.set_text("Connection succeeded");
});
connection.connection_failed.connect(() => {
message("Connection failed");
});
connection.ws_message.connect((type, msg) => {
message("message received " + msg);
});
connection.init_connection_for(websocket_host);
grid.add(main_label);
window.add(grid);
window.show_all();
}
public static int main(string[] args) {
var app = new Main();
return app.run(args);
}
}
если я подключаюсь к процессу через gdb, я вижу следующую картину:
(gdb) info thr
Id Target Id Frame
* 1 Thread 0x7f38e6cbbac0 (LWP 29885) "com.github.syfd" 0x00007f38e53098f6 in __libc_recv (
fd=14, buf=0x55890440bc00, len=1024, flags=0) at ../sysdeps/unix/sysv/linux/recv.c:28
2 Thread 0x7f38d5d1c700 (LWP 29886) "gmain" 0x00007f38e52fbcb9 in __GI___poll (
fds=0x558903d13b40, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
3 Thread 0x7f38d551b700 (LWP 29887) "gdbus" 0x00007f38e52fbcb9 in __GI___poll (
fds=0x558903d30760, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
4 Thread 0x7f38cffff700 (LWP 29888) "dconf worker" 0x00007f38e52fbcb9 in __GI___poll (
fds=0x558903f6ccb0, nfds=1, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
(gdb) bt
#0 0x00007f38e53098f6 in __libc_recv (fd=14, buf=0x55890440bc00, len=1024, flags=0)
at ../sysdeps/unix/sysv/linux/recv.c:28
#1 0x00007f38e5eb54f4 in () at /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#2 0x00007f38e5667558 in () at /usr/lib/x86_64-linux-gnu/libsoup-2.4.so.1
#3 0x00007f38e59173a5 in g_main_context_dispatch () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#4 0x00007f38e5917770 in () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#5 0x00007f38e59177fc in g_main_context_iteration () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#6 0x00007f38e5ed8f3d in g_application_run () at /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#7 0x0000558902d69e8f in main_main (args=0x7ffeb2b775f8, args_length1=1)
at /home/sergej/workspace/websocket-libsoup-vala-example-v2/src/Main.vala:121
#8 0x0000558902d69ed2 in main (argc=1, argv=0x7ffeb2b775f8)
at /home/sergej/workspace/websocket-libsoup-vala-example-v2/src/Main.vala:119
Это выглядит как
__libc_recv at ../sysdeps/unix/sysv/linux/recv.c:28
здесь блокируется, но я не могу понять, почему и как сделать соединение неблокирующим способом.
Я ценю любую помощь / подсказки, как решить проблему с заблокированным пользовательским интерфейсом или как создать неблокирующее соединение с веб-сервером с помощью libsoup.
1 ответ
Вы используете асинхронные методы, но для этого нужно немного поработать. Сделайте свой
init_connection_for
метод
async
:
public async void init_connection_for(string host) { ... }
потом
yield
на вызов, чтобы установить соединение с веб-сокетом:
var websocket_connection = yield socket_client.websocket_connect_async(websocket_message, null, null, null);
Затем вы вызываете свою функцию, чтобы настроить ее в начале, прежде чем она уступит и будет вызвана асинхронным соединением:
connection.init_connection_for.begin(websocket_host);
Затем вам также необходимо скомпилировать с GIO,
--pkg gio-2.0
, для асинхронных методов.
Это заставит вас двигаться вперед, но можно сделать гораздо больше, чтобы упростить и перестроить ваш код. Вам не нужно лишнее
MainLoop
Например.
Это разница изменений, которые помогут вам прогрессировать:
--- original.vala 2021-08-08 15:59:41.757378207 +0100
+++ modifed.vala 2021-08-08 17:18:58.680837130 +0100
@@ -18,35 +18,33 @@
return (string)byt.get_data();
}
- public void init_connection_for(string host) {
+ public async void init_connection_for(string host) {
MainLoop loop = new MainLoop();
var socket_client = new Soup.Session();
string url = "ws://%s:8080/".printf(host);
message(@"connect to $url");
var websocket_message = new Soup.Message("GET", url);
- socket_client.websocket_connect_async.begin(websocket_message, null, null, null, (obj, res) => {
- try {
- websocket_connection = socket_client.websocket_connect_async.end(res);
- message("Connected!");
-
- connection_succeeded();
- if (websocket_connection != null) {
- websocket_connection.message.connect((type, m_message) => {
- ws_message(type, decode_bytes(m_message, m_message.length));
- });
- websocket_connection.closed.connect(() => {
- message("Connection closed");
- connection_disengaged();
- });
- }
- } catch (Error e) {
- message("Remote error: " + e.message + " " + e.code.to_string());
- connection_failed();
- loop.quit();
+ var websocket_connection = yield socket_client.websocket_connect_async(websocket_message, null, null, null);
+ try {
+ message("Connected!");
+
+ connection_succeeded();
+ if (websocket_connection != null) {
+ websocket_connection.message.connect((type, m_message) => {
+ ws_message(type, decode_bytes(m_message, m_message.length));
+ });
+ websocket_connection.closed.connect(() => {
+ message("Connection closed");
+ connection_disengaged();
+ });
}
+ } catch (Error e) {
+ message("Remote error: " + e.message + " " + e.code.to_string());
+ connection_failed();
loop.quit();
- });
+ }
+ loop.quit();
loop.run();
}
@@ -94,7 +92,7 @@
message("message received " + msg);
});
- connection.init_connection_for(websocket_host);
+ connection.init_connection_for.begin(websocket_host);
grid.add(main_label);
window.add(grid);
window.show_all();