使用開源庫 GObject 和 libsoup 提升 C 語言編程能力
GLib 對象系統 (GObject)是一個為 C 語言提供靈活且可擴展的面向對象框架的庫。在這篇文章中,我將使用該庫的 2.4 版本進行演示。
GObject 庫繼承了 ANSI C 標準,擁有一些常見的數據類型,例如:
gchar
:字元型guchar
:無符號字元型gunichar
:32 位定寬 Unicode 字元型gboolean
:布爾型gint8
、gint16
、gint32
、gint64
:有符號 8、16、32 和 64 位整數guint8
、guint16
、guint32
、guint64
:無符號 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 個結構(GObject
和 GObjectClass
)組成,你可以繼承它們以實現你自己的對象。
你需要在結構體中先嵌入 GObject
和 GObjectClass
:
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
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive