Glib学习(23) IO通道 IO Channels

glib源码下载:http://ftp.gnome.org/pub/gnome/sources/glib/
glib帮助文档:https://developer.gnome.org/glib/

IO通道 - 可移植的支持使用文件,管道和套接字

Includes
#include <glib.h>

描述

GIOChannel数据类型旨在提供一种可移植的方法,用于使用文件描述符,管道和套接字,并将它们集成到main event loop。 目前,在UNIX平台上提供完全支持,对Windows的支持仅部分完成。
要在UNIX系统上创建新的GIOChannel,请使用g_io_channel_unix_new()。 这适用于普通文件描述符,管道和套接字。 或者,可以使用g_io_channel_new_file()以与系统无关的方式为文件创建通道。
创建GIOChannel后,可以使用函数g_io_channel_read_chars(),g_io_channel_write_chars(),g_io_channel_seek_position()和g_io_channel_shutdown()以通用方式使用它。
要将GIOChannel添加到main event loop,请使用g_io_add_watch()或g_io_add_watch_full()。 在这里,您可以在GIOChannel上指定您感兴趣的事件,并提供在发生这些事件时要调用的函数。
创建GIOChannel实例,初始引用计数为1. g_io_channel_ref()和g_io_channel_unref()可分别用于递增或递减引用计数。 当引用计数降至0时,释放GIOChannel。 (虽然它不会自动关闭,除非它是使用g_io_channel_new_file()创建的。)使用g_io_add_watch()或g_io_add_watch_full()增加通道的引用计数。
新函数g_io_channel_read_chars(),g_io_channel_read_line(),g_io_channel_read_line_tring(),g_io_channel_read_to_end(),g_io_channel_write_chars(),g_io_channel_seek_position()和g_io_channel_flush()不应与不推荐使用的函数g_io_channel_read(),g_io_channel_write()和g_io_channel_seek混合使用( )在同一channel上。

函数

g_io_channel_unix_new ()
给定文件描述符创建新的GIOChannel。 在UNIX系统上,这适用于普通文件,管道和套接字。
返回的GIOChannel的引用计数为1。
GIOChannel的默认编码是UTF-8。 如果您的应用程序正在使用via管道从命令读取输出,则可能需要使用g_io_channel_set_encoding()函数将编码设置为当前语言环境的编码(请参阅g_get_charset())。 默认情况下,当删除对GIOChannel数据结构的最终引用时,将不会关闭传递的fd。
如果要在没有解释的情况下读取原始二进制数据,则使用NULL为编码参数调用g_io_channel_set_encoding()函数。
此功能在Windows上的GLib中也可用,但您应该避免在Windows上使用它。 文件描述符和套接字的域重叠。 如果传递给此函数的参数恰好是有效的文件描述符和套接字,则GLib无法知道您的意思。 如果发生这种情况,则会发出警告,并且GLib假定它是您所指的文件描述符。
返回值
一个新的GIOChannel。
g_io_channel_unix_get_fd ()
返回GIOChannel的文件描述符。
在Windows上,此函数返回GIOChannel的文件描述符或套接字。

g_io_channel_init ()
初始化GIOChannel结构。
在创建GIOChannel时,上述每个函数都会调用它,因此应用程序员通常不需要这样做(除非您要创建新类型的GIOChannel)。

g_io_channel_new_file ()
使用mode模式将文件文件名作为GIOChannel打开。 当删除对它的最后一个引用时,该channel将被关闭,因此不需要调用g_io_channel_close()(尽管这样做不会导致问题,只要在关闭后没有尝试访问该通道)。
参数
mode
 “r”,“w”,“a”,“r +”,“w +”,“a +”之一。 这些与fopen()中的含义相同
返回值
成功时的GIOChannel,失败时为NULL。
g_io_channel_read_chars ()
使用新API替换g_io_channel_read()。
参数
count
缓冲区的大小。 请注意,如果剩余数据不是完整字符,即使缓冲区中有数据,缓冲区也可能无法完全填充。
[in]
bytes_read
读取的字节数。 如果count <6并且channel的编码为非NULL,则即使成功也可能为零。 这表示下一个UTF-8字符对于缓冲区来说太宽。
[out][optional]

返回值
操作的状态。
g_io_channel_read_unichar ()
从通道读取Unicode字符。 无法在具有NULL编码的通道上调用此函数。

g_io_channel_read_line ()
从GIOChannel读取一行(包括终止字符)到新分配的字符串。 如果返回值为G_IO_STATUS_NORMAL,则str_return将包含已分配的内存。
参数
str_return
从GIOChannel读取的行,包括行终止符。 不再需要时,应使用g_free()释放此数据。 这是一个以空字符结尾的字符串。 如果返回零长度,则将为NULL。
[out]
length
存储读取数据长度的位置,或NULL。
[out][optional]
terminator_pos

存储行终止符位置的位置,或NULL。
[out][optional]
error

返回GConvertError或GIOChannelError类型错误的位置
返回值
操作的状态。
g_io_channel_read_line_string ()
使用GString作为缓冲区从GIOChannel读取一行。
参数
g_io_channel_read_to_end ()
从文件中读取所有剩余数据。
返回值
G_IO_STATUS_NORMAL成功。 此函数永远不会返回G_IO_STATUS_EOF。
g_io_channel_write_unichar ()
将Unicode字符写入通道。 无法在具有NULL编码的通道上调用此函数。

g_io_channel_flush ()
刷新GIOChannel的写缓冲区。
返回值
操作的状态:G_IO_STATUS_NORMAL,G_IO_STATUS_AGAIN或G_IO_STATUS_ERROR之一。
g_io_channel_shutdown ()
关闭IO通道。 如果flush为TRUE,则将刷新任何要写入的待处理数据。 在使用g_io_channel_unref()删除最后一个引用之前,不会释放该通道。
g_io_create_watch ()
创建在满足给定通道条件时调度的GSource。 例如,如果condition是G_IO_IN,则当有可用于读取的数据时,将调度source。
对于您希望以默认优先级将source添加到默认主循环上下文的情况,g_io_add_watch()是同一功能的简单接口。

g_io_add_watch ()
使用默认优先级将GIOChannel添加到默认主循环上下文中。
g_io_add_watch_full ()
将GIOChannel添加到具有给定优先级的默认主循环上下文中。
这在内部使用g_io_create_watch()创建一个主循环source,并使用g_source_attach()将其附加到主循环上下文。 如果需要更好的控制,可以手动执行这些步骤。
[rename-to g_io_add_watch]

gboolean
(*GIOFunc) (GIOChannel *source,
            GIOCondition condition,
            gpointer data);
指定传递给g_io_add_watch()或g_io_add_watch_full()的函数类型,该函数在满足GIOChannel上的请求条件时调用。

g_io_channel_get_line_term ()
这将返回GIOChannel用于确定文件在哪里发生换行的字符串。 值NULL表示自动检测。
g_io_channel_set_line_term ()
这将设置GIOChannel用于确定文件中断行发生位置的字符串。

g_io_channel_get_buffered ()
返回缓冲通道。
g_io_channel_set_buffered ()
只有在通道编码为NULL时才能设置缓冲状态。 对于任何其他编码,必须缓冲通道。
如果刷新了通道的内部缓冲区,则只能将缓冲通道设置为无缓冲。 返回G_IO_STATUS_EOF的新创建的频道或频道不需要这样的flush。 对于只写通道,调用g_io_channel_flush()就足够了。 对于所有其他通道,可以通过调用g_io_channel_seek_position()来刷新缓冲区。 这包括寻找类型G_SEEK_CUR和零偏移的可能性。 请注意,这意味着一旦从其中读取数据,就无法将基于套接字的通道设置为无缓冲。
在无缓冲通道上,如果维护旧代码是必要的,则可以安全地混合来自新旧API的读写调用。
缓冲通道的默认状态。
 

示例代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gprintf.h>

void channel_unix_new_test(void)
{
    g_printf ("%s() in\n", __func__);
    int fd = open("test.txt", O_RDONLY);
    GIOChannel *giofile = g_io_channel_unix_new (fd);

    gchar *str_return;
    gsize length, terminator_pos;
    GError *error = NULL;
    GIOStatus giostatus = g_io_channel_read_line (giofile,
                                                  &str_return,
                                                  &length,
                                                  &terminator_pos,
                                                  &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_line:%s, length:%ld, terminator_pos:%ld\n", str_return, length, terminator_pos);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_line error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_line error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_line error G_IO_STATUS_AGAIN:%s\n", error->message);

    GString *buffer = g_string_new (NULL);
    giostatus = g_io_channel_read_line_string (giofile,
                                               buffer,
                                               &terminator_pos,
                                               &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_line_string:%s\n", buffer->str);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_line_string error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_line_string error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_line_string error G_IO_STATUS_AGAIN:%s\n", error->message);
    g_string_free (buffer, TRUE);
    
    giostatus = g_io_channel_read_to_end (giofile,
                                          &str_return,
                                          &length,
                                          &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_to_end:%s, length:%ld\n", str_return, length);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_to_end error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_to_end error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_to_end error G_IO_STATUS_AGAIN:%s\n", error->message);
    
    gint gfd = g_io_channel_unix_get_fd (giofile);
    g_io_channel_shutdown (giofile, TRUE, &error);
    close(gfd);
    g_printf ("%s() out\n", __func__);
}

void channel_read_chars_test(void)
{
    g_printf ("%s() in\n", __func__);
    GError *error = NULL;
    GIOChannel *giofile = g_io_channel_new_file ("test.txt",
                                                 "r",
                                                 &error);
    gchar *buf = g_new0(gchar, 1024);
    gsize bytes_read;
    GIOStatus giostatus = g_io_channel_read_chars (giofile,
                                                   buf,
                                                   1024,
                                                   &bytes_read,
                                                   &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_chars:%s, length:%ld\n", buf, bytes_read);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_chars error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_chars error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_chars error G_IO_STATUS_AGAIN:%s\n", error->message);
    g_io_channel_shutdown (giofile, TRUE, &error);
    g_free (buf);
    g_printf ("%s() out\n", __func__);
}

void channel_read_unichar_test(void)
{
    g_printf ("%s() in\n", __func__);
    GError *error = NULL;
    GIOChannel *giofile = g_io_channel_new_file ("ansi.txt",
                                                 "r",
                                                 &error);
    GError *err = NULL;
    GIOStatus giostatus = g_io_channel_set_encoding (giofile,
                                                     "ANSI",
                                                     &err);
    gunichar thechar;
    giostatus = g_io_channel_read_unichar (giofile,
                                           &thechar,
                                           &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_unichar:%d\n", thechar);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_unichar error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_unichar error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_unichar error G_IO_STATUS_AGAIN:%s\n", error->message);
    g_io_channel_shutdown (giofile, TRUE, &error);
    g_printf ("%s() out\n", __func__);
}

void channel_write_chars_test(void)
{
    g_printf ("%s() in\n", __func__);
    GError *error = NULL;
    GIOChannel *giofile = g_io_channel_new_file ("write.txt",
                                                 "a+",
                                                 &error);
    gsize bytes_written;
    GIOStatus giostatus = g_io_channel_write_chars (giofile,
                                                      "g_io_channel_write_chars",
                                                      -1,
                                                      &bytes_written,
                                                      &error);
    if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("write_chars error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("write_chars error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("write_chars error G_IO_STATUS_AGAIN:%s\n", error->message);
    giostatus = g_io_channel_flush (giofile,
                                    &error);
    if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("flush error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("flush error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("flush error G_IO_STATUS_AGAIN:%s\n", error->message);
    g_io_channel_shutdown (giofile, TRUE, &error);
    g_printf ("%s() out\n", __func__);
}

gboolean GIOFifoFunc (GIOChannel *source, GIOCondition condition, gpointer data)
{
    g_printf ("GIOFifoFunc data:%s\n", (char *)data);
    g_printf ("condition:%d\n", condition);
    if(condition & G_IO_HUP) {
        g_error("error:Pipe Disconnected!\n");
    }
    GError *error = NULL;
    gsize bytes_read;
    gsize length, terminator_pos;
    gchar *str_return;
    GIOStatus giostatus = g_io_channel_read_line (source,
                                                  &str_return,
                                                  &length,
                                                  &terminator_pos,
                                                  &error);
    if (giostatus == G_IO_STATUS_NORMAL)
        g_printf ("read_line:%s, length:%ld, terminator_pos:%ld\n", str_return, length, terminator_pos);
    else if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("read_chars error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("read_chars error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("read_chars error G_IO_STATUS_AGAIN:%s\n", error->message);
    g_free (str_return);
    return TRUE;
}

void FifoDestroyNotify (gpointer data)
{
    g_printf ("FifoDestroyNotify data:%s\n", (char *)data);
}

gboolean TimeoutSourceFunc (gpointer user_data)
{
    g_printf ("%s() in\n", __func__);
    GMainLoop *loop = (GMainLoop *)user_data;
    g_main_loop_quit (loop);
    return FALSE;
}

void setup_child(gint input[]) {
    g_printf ("%s() in\n", __func__);
    GIOChannel *channel_read;
    char *user_data = "test user data!";
    // 关闭不必要的 Pipe 输入
    close(input[1]);

    GMainLoop *loop = g_main_loop_new (NULL, FALSE);
    channel_read = g_io_channel_unix_new(input[0]);
    if(channel_read == NULL) {
        g_error("error:Unable to establish GIOChannels!\n");
    }
    
    guint source_id = g_io_add_watch_full(channel_read, G_PRIORITY_DEFAULT, G_IO_IN|G_IO_HUP, GIOFifoFunc, (gpointer) user_data, FifoDestroyNotify);
    
    if(source_id == 0) {
        g_error("error:Unable to add watch!\n");
    }

    g_timeout_add (1000, TimeoutSourceFunc, (gpointer)loop);
    g_main_loop_run (loop);

    g_source_remove (source_id);
    g_printf ("g_source_remove\n");
    sleep(1);
}

void setup_parent(gint output[]) {
    g_printf ("%s() in\n", __func__);
    sleep(1);
    GIOChannel *channel_write;
    GError *error = NULL;
    gsize bytes_written;
    // 关闭不用的 Pipe 输出
    close(output[0]);

    // 建立 GIOChannel
    channel_write = g_io_channel_unix_new(output[1]);
    if(channel_write == NULL) {
        g_error("错误:无法建立 GIOChannels!\n");
    }

    GIOStatus giostatus = g_io_channel_write_chars (channel_write,
                                                    "setup parent\n",
                                                    -1,
                                                    &bytes_written,
                                                    &error);
    giostatus = g_io_channel_flush (channel_write, &error);
    if (giostatus == G_IO_STATUS_ERROR)
        g_printf ("flush error G_IO_STATUS_ERROR:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_EOF)
        g_printf ("flush error G_IO_STATUS_EOF:%s\n", error->message);
    else if (giostatus == G_IO_STATUS_AGAIN)
        g_printf ("flush error G_IO_STATUS_AGAIN:%s\n", error->message);
    sleep(5);
}

void io_add_watch(void)
{
    g_printf ("%s() in\n", __func__);
    gint parent_to_child[2];

    if(pipe(parent_to_child) == -1) { // 开启Pipe
        g_error("Error:%s\n", g_strerror(errno));
        return;
    }

    // fork 子程序
    switch(fork()) {
        case -1:
            g_error("error:%s\n", g_strerror(errno));
            break;
        case 0: // 这是子程序
            setup_child(parent_to_child);
            break;
        default: // 这是父程序
            setup_parent(parent_to_child);
    }
    
    g_printf ("%s() out\n", __func__);
}

int main(int argc, char **argv)
{
    g_printf ("main() in\n");

    channel_unix_new_test();
    channel_read_chars_test();
    channel_read_unichar_test();
    channel_write_chars_test();
    io_add_watch();
    
    g_printf ("main() out\n");
    return 0;
}

输出结果:

~/glibtest$ ./gio
main() in
io_add_watch() in
setup_parent() in
setup_child() in
GIOFifoFunc data:test user data!
condition:1
read_line:setup parent
, length:13, terminator_pos:12
TimeoutSourceFunc() in
FifoDestroyNotify data:test user data!
g_source_remove
io_add_watch() out
main() out

io_add_watch() out
main() out

文本文件:

ansi.txt

test.txt

write.txt

 

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