Linux中國

使用開源庫 GObject 和 libsoup 提升 C 語言編程能力

開源庫 GObjectlibsoup 做了很多工作,因此你可以專註於使用 C 語言開發神奇的應用。

GLib 對象系統 Object System GObject)是一個為 C 語言提供靈活且可擴展的面向對象框架的庫。在這篇文章中,我將使用該庫的 2.4 版本進行演示。

GObject 庫繼承了 ANSI C 標準,擁有一些常見的數據類型,例如:

  • gchar:字元型
  • guchar:無符號字元型
  • gunichar:32 位定寬 Unicode 字元型
  • gboolean:布爾型
  • gint8gint16gint32gint64:有符號 8、16、32 和 64 位整數
  • guint8guint16guint32guint64:無符號 8、16、32 和 64 位整數
  • gfloat:IEEE 754 標準單精度浮點數
  • gdouble:IEEE 754 標準雙精度浮點數
  • gpointer:泛指針

函數指針

GObject 庫還引入了類和介面的類型和對象體系。之所以可以,是因為 ANSI C 語言可以理解函數指針。

你可以這樣做來聲明函數指針:

void (*my_callback)(gpointer data);

首先,你需要給變數 my_callback 賦值:

void my_callback_func(gpointer data)
{
  //do something
}

my_callback = my_callback_func;

函數指針 my_callback 可以這樣來調用:

gpointer data;
data = g_malloc(512 * sizeof(gint16));
my_callback(data);

對象類

GObject 基類由 2 個結構(GObjectGObjectClass)組成,你可以繼承它們以實現你自己的對象。

你需要在結構體中先嵌入 GObjectGObjectClass

struct _MyObject
{
  GObject gobject;
  //your fields
};

struct _MyObjectClass
{
  GObjectClass gobject;
  //your class methods
};

GType my_object_get_type(void);

對象的實現包含了公有成員。GObject 也提供了私有成員的方法。這實際上是 C 源文件中的一個結構,而不是在頭文件。該類通常只包含函數指針。

一個介面不能派生自另一個介面,比如:

struct _MyInterface
{
  GInterface ginterface;
  //your interface methods
};

通過調用 g_object_get()g_object_set() 函數來訪問屬性。若要獲取屬性,你必須提供特定類型的返回位置。建議先初始化返回位置:

gchar *str

str = NULL;

g_object_get(gobject,
  "my-name", &str,
  NULL);

或者你想要設置屬性:

g_object_set(gobject,
  "my-name", "Anderson",
  NULL);

libsoup HTTP 庫

libsoup 項目為 GNOME 提供了 HTTP 客服端和服務端使用的庫。它使用 GObjects 和 glib 主循環與集成到 GNOME 應用,並且還具有用於命令行的同步 API。

首先,創建一個特定身份驗證回調的 libsoup 會話。你也可以使用 cookie。

SoupSession *soup_session;
SoupCookieJar *jar;

soup_session = soup_session_new_with_options(SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_BASIC,
  SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_DIGEST,
  NULL);

jar = soup_cookie_jar_text_new("cookies.txt",
  FALSE);     

soup_session_add_feature(soup_session, jar);
g_signal_connect(soup_session, "authenticate",
  G_CALLBACK(my_authenticate_callback), NULL);

然後你可以像這樣創建一個 HTTP GET 請求:

SoupMessage *msg;
SoupMessageHeaders *response_headers;
SoupMessageBody *response_body;
guint status;
GError *error;

msg = soup_form_request_new("GET",
  "http://127.0.0.1:8080/my-xmlrpc",
  NULL);

status = soup_session_send_message(soup_session,
  msg);

response_headers = NULL;
response_body = NULL;

g_object_get(msg,
  "response-headers", &response_headers,
  "response-body", &response_body,
  NULL);

g_message("status %d", status);
cookie = NULL;
soup_message_headers_iter_init(&iter,
response_headers);

while(soup_message_headers_iter_next(&iter, &name, &value)){    
  g_message("%s: %s", name, value);
}

g_message("%s", response_body->data);
if(status == 200){
  cookie = soup_cookies_from_response(msg);
  while(cookie != NULL){
    char *cookie_name;
    cookie_name = soup_cookie_get_name(cookie->data);
    //parse cookies
    cookie = cookie->next;
  }
}

當網路伺服器進行身份認證時,會調用身份認證回調函數。

這是一個函數簽名:

#define MY_AUTHENTICATE_LOGIN "my-username"
#define MY_AUTHENTICATE_PASSWORD "my-password"

void my_authenticate_callback(SoupSession *session,
  SoupMessage *msg,
  SoupAuth *auth,
  gboolean retrying,
  gpointer user_data)
{
  g_message("authenticate: ****");
  soup_auth_authenticate(auth,
                         MY_AUTHENTICATE_LOGIN,
                         MY_AUTHENTICATE_PASSWORD);
}

一個 libsoup 伺服器

想要基礎的 HTTP 身份認證能夠運行,你需要指定回調函數和伺服器上下文路徑。然後再添加一個帶有另一個回調的處理程序。

下面這個例子展示了在 8080 埠監聽任何 IPv4 地址的消息:

SoupServer *soup_server;
SoupAuthDomain *auth_domain;
GSocket *ip4_socket;
GSocketAddress *ip4_address;
MyObject *my_object;
GError *error;

soup_server = soup_server_new(NULL);
auth_domain = soup_auth_domain_basic_new(SOUP_AUTH_DOMAIN_REALM, "my-realm",
  SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, my_xmlrpc_server_auth_callback,
  SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, my_object,
  SOUP_AUTH_DOMAIN_ADD_PATH, "my-xmlrpc",
  NULL);

soup_server_add_auth_domain(soup_server, auth_domain);
soup_server_add_handler(soup_server,
  "my-xmlrpc",
  my_xmlrpc_server_callback,
  my_object,
  NULL);

ip4_socket = g_socket_new(G_SOCKET_FAMILY_IPV4,
  G_SOCKET_TYPE_STREAM,
  G_SOCKET_PROTOCOL_TCP,
  &error);

ip4_address = g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4),
  8080);
error = NULL;
g_socket_bind(ip4_socket,
  ip4_address,
  TRUE,
  &error);
error = NULL;
g_socket_listen(ip4_socket, &error);

error = NULL;
soup_server_listen_socket(soup_server,
  ip4_socket, 0, &error);

示例代碼中,有兩個回調函數。一個處理身份認證,另一個處理對它的請求。

假設你想要網頁伺服器允許用戶名為 my-username 和口令為 my-password 的憑證登錄,並且用一個隨機且唯一的用戶 ID 字元串設置會話 cookie。

gboolean my_xmlrpc_server_auth_callback(SoupAuthDomain *domain,
  SoupMessage *msg,
  const char *username,
  const char *password,
  MyObject *my_object)
{
  if(username == NULL || password == NULL){
    return(FALSE);
  }

  if(!strcmp(username, "my-username") &&
     !strcmp(password, "my-password")){
    SoupCookie *session_cookie;
    GSList *cookie;
    gchar *security_token;
    cookie = NULL;

    security_token = g_uuid_string_random();
    session_cookie = soup_cookie_new("my-srv-security-token",
      security_token,
      "localhost",
      "my-xmlrpc",
      -1);

     cookie = g_slist_prepend(cookie,
       session_cookie);  
     soup_cookies_to_request(cookie,
       msg);
    return(TRUE);
  }
  return(FALSE);
}

對上下文路徑 my-xmlrpc 進行處理的函數:

void my_xmlrpc_server_callback(SoupServer *soup_server,
  SoupMessage *msg,
  const char *path,
  GHashTable *query,
  SoupClientContext *client,
  MyObject *my_object)
{
  GSList *cookie;
  cookie = soup_cookies_from_request(msg);
  //check cookies
}

更加強大的 C 語言

希望我的示例展現了 GObject 和 libsoup 項目給 C 語言帶來了真正的提升。像這樣在字面意義上擴展 C 語言,可以使 C 語言更易於使用。它們已經為你做了許多工作,這樣你可以專註於用 C 語言開發簡單、直接的應用程序了。

via: https://opensource.com/article/22/5/libsoup-gobject-c

作者:Joël Krähemann 選題:lkxed 譯者:Donkey-Hao 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國