使用这些开源库增强 C 语言的功能

GObject 和 libsoup 为您完成大量工作,因此您可以将注意力转向发明令人惊叹的 C 语言应用程序。
3 位读者喜欢这个。
Why and how to handle exceptions in Python Flask

图片来自 Unsplash.com,知识共享零许可 

GLib 对象系统 (GObject) 是一个库,为 C 语言提供灵活且可扩展的面向对象框架。在本文中,我将演示如何使用该库的 2.4 版本。

GObject 库扩展了 ANSI C 标准,其中包含常用类型的 typedef,例如

  • gchar:字符类型
  • guchar:无符号字符类型
  • gunichar:固定 32 位宽的 unichar 类型
  • 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)组成,您可以继承它们来实现您自己的对象。

您可以将 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 客户端和服务器库。它使用 GObject 和 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;
  }
}

当 Web 服务器请求身份验证时,将调用身份验证回调。

这是一个函数签名

#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 身份验证工作,您必须指定回调和服务器上下文路径。然后,您使用另一个回调添加处理程序。

此示例监听 localhost 端口 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);

在此示例代码中,有两个回调。一个处理身份验证,另一个处理请求本身。

假设您想要一个 Web 服务器允许使用凭据用户名 my-username 和密码 my-password 登录,并设置一个带有随机唯一用户 ID (UUID) 字符串的会话 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 语言发明令人惊叹的应用程序。

标签
This is I face
自由软件爱好者,精通 C 编程语言。我不害怕任何代码复杂性,只要它是以简单的方式编写的。作为 Advanced Gtk+ Sequencer 的开发者,我知道多线程应用程序的挑战性,并且由此我们为未来的需求奠定了良好的基础。我的个人网站

评论已关闭。

© 2025 open-source.net.cn. All rights reserved.