使用GIOService(GLib,Glib-GIO)需要帮助实现简单的套接字服务器

我正在学习使用GLib编写一个简单高效的套接字服务器的基础知识。 我正在试验GSocketService。 到目前为止,我似乎只能接受联系,但他们立即closures。 从文档我不知道我失踪了什么步骤。 我希望有人能为我揭示这一点。

当运行以下内容时:

# telnet localhost 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Connection closed by foreign host. # telnet localhost 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Connection closed by foreign host. # telnet localhost 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Connection closed by foreign host. 

服务器输出:

 # ./server New Connection from 127.0.0.1:36962 New Connection from 127.0.0.1:36963 New Connection from 127.0.0.1:36965 

当前代码:

 /* * server.c * * Created on: Mar 10, 2010 * Author: mark */ #include <glib.h> #include <gio/gio.h> gchar *buffer; gboolean network_read(GIOChannel *source, GIOCondition cond, gpointer data) { GString *s = g_string_new(NULL); GError *error; GIOStatus ret = g_io_channel_read_line_string(source, s, NULL, &error); if (ret == G_IO_STATUS_ERROR) g_error ("Error reading: %s\n", error->message); else g_print("Got: %s\n", s->str); } gboolean new_connection(GSocketService *service, GSocketConnection *connection, GObject *source_object, gpointer user_data) { GSocketAddress *sockaddr = g_socket_connection_get_remote_address(connection, NULL); GInetAddress *addr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(sockaddr)); guint16 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(sockaddr)); g_print("New Connection from %s:%d\n", g_inet_address_to_string(addr), port); GSocket *socket = g_socket_connection_get_socket(connection); gint fd = g_socket_get_fd(socket); GIOChannel *channel = g_io_channel_unix_new(fd); g_io_add_watch(channel, G_IO_IN, (GIOFunc) network_read, NULL); return TRUE; } int main(int argc, char **argv) { g_type_init(); GSocketService *service = g_socket_service_new(); GInetAddress *address = g_inet_address_new_from_string("127.0.0.1"); GSocketAddress *socket_address = g_inet_socket_address_new(address, 4000); g_socket_listener_add_address(G_SOCKET_LISTENER(service), socket_address, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, NULL, NULL, NULL); g_object_unref(socket_address); g_object_unref(address); g_socket_service_start(service); g_signal_connect(service, "incoming", G_CALLBACK(new_connection), NULL); GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); } 

GSocketConnection必须在传入回调中被重新引用,这将保持连接的活跃。 您可以将其传递给数据结构,类或用户数据到手表回调。

 gboolean new_connection(...) { ... g_object_ref (connection); GSocket *socket = g_socket_connection_get_socket(connection); gint fd = g_socket_get_fd(socket); GIOChannel *channel = g_io_channel_unix_new(fd); // Pass connection as user_data to the watch callback g_io_add_watch(channel, G_IO_IN, (GIOFunc) network_read, connection); return TRUE; } 

你没有在watch_backback中返回network_read(),你必须用“return true”来结束它。 从文档:“ 该函数应该返回FALSE,如果事件源应该被删除 ”。

100%的CPU是由于在连接关闭的时候通道仍然存在。 确保在不再需要时正确删除事件源。

 gboolean network_read(GIOChannel *source, GIOCondition cond, gpointer data) { GString *s = g_string_new(NULL); GError *error = NULL; GIOStatus ret = g_io_channel_read_line_string(source, s, NULL, &error); if (ret == G_IO_STATUS_ERROR) { //g_error ("Error reading: %s\n", error->message); g_warning ("Error reading: %s\n", error->message); // Drop last reference on connection g_object_unref (data); // Remove the event source return FALSE; } else g_print("Got: %s\n", s->str); if (ret == G_IO_STATUS_EOF) { return FALSE; } 

它没有在GSocketService文档中记录(我必须通过GLib源查找它),但调用回调函数的例程(本例中为new_connection)在返回后在连接对象*上执行g_object_unref()。 这有效地立即关闭连接new_connection()返回到它。

我不知道为什么这样做,但解决方案是添加一个g_object_ref()进入回调:

 gboolean new_connection(GSocketService *service, GSocketConnection *connection, GObject *source_object, gpointer user_data) { g_object_ref(connection); /* Tell glib not to disconnect */ GSocketAddress *sockaddr = g_socket_connection_get_remote_address(connection, NULL); GInetAddress *addr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(sockaddr)); guint16 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(sockaddr)); 

没有这个补充,轮询主循环中的文件描述符只是返回POLLNVAL,因为连接已经关闭。 在没有处理结果的情况下,它一直这样做 – 这就是造成100%CPU负载的原因。

从GIO文档 :

GIOStream对象拥有输入和输出流,而不是其他方式,因此保持子流活动不会使GIOStream对象保持活动状态。 如果GIOStream对象被释放,它将被关闭,从而关闭子流,所以即使子流保持活动状态,它们总是会为所有操作返回一个G_IO_ERROR_CLOSED。