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

 

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