最近开源项目需要一个用C语言实现的Http模块,用于网络数据的发送和接受
然后呢就看了很多网上的教程,使用了一个C语言中的curl方法
源代码如下
#include <stdio.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>
#include <python2.7/Python.h>
size_t memory_callback(void *data, size_t size, size_t nmemb, void *content)
{
size_t realsize = size *nmemb;
char *p = *(char **)content;
size_t len = p ? strlen(p) : 0;
*(char **)content = realloc(p, len + realsize + 1);
p = *(char **)content;
memcpy(p + len, data, realsize);
p[len + realsize] = '\0';
return realsize;
}
size_t get_content(char *appid, char *secret_key, char *q, char *from ,char *to, char **content)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl)
{
char myurl[1000] = "http://api.fanyi.baidu.com/api/trans/vip/translate?";
char salt[60];
int a = rand();
sprintf(salt,"%d",a);
char sign[120] = "";
strcat(sign,appid);
strcat(sign,q);
strcat(sign,salt);
strcat(sign,secret_key);
//printf("%s\n",sign);
unsigned char md[16];
int i;
char tmp[3]={'\0'},buf[33]={'\0'};
MD5((const unsigned char *)sign, strlen((const char *)sign), md);
for (i = 0; i < 16; i++)
{
sprintf(tmp,"%2.2x",md[i]);
strcat(buf,tmp);
}
//printf("%s\n",buf);
strcat(myurl,"appid=");
strcat(myurl,appid);
strcat(myurl,"&q=");
strcat(myurl,q);
strcat(myurl,"&from=");
strcat(myurl,from);
strcat(myurl,"&to=");
strcat(myurl,to);
strcat(myurl,"&salt=");
strcat(myurl,salt);
strcat(myurl,"&sign=");
strcat(myurl,buf);
printf("%s\n",myurl);
/* always cleanup */
curl_easy_setopt(curl, CURLOPT_URL, myurl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memory_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)content);
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return 0;
}
static PyObject *xhttp(PyObject *self, PyObject *args)
{
char *content = NULL;
char *appid;
char *secret_key;
char *q;
char *from;
char *to;
if(!PyArg_ParseTuple(args, "s|ssss", &appid, &secret_key, &q, &from, &to))
{
return NULL;
}
get_content(appid, secret_key, q, from, to, &content);
return Py_BuildValue("s", content);
if(content) free(content);
}
static PyMethodDef httpMethods[] =
{
{"xhttp", (PyCFunction)xhttp, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initxhttp(void)
{
Py_InitModule("xhttp", httpMethods);
}
我们从头来研究一下这个代码吧
首先是三个函数库,比较特殊
#include <curl/curl.h>
#include <openssl/md5.h>
#include <python2.7/Python.h>
curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中,并且有DOS和Win32、Win64下的移植版本
但是我们这里使用的是libcurl
并不是linux的某个命令,而是一个函数库
#include <openssl/md5.h>
是加密函数,用于计算md5值的
#include <python2.7/Python.h>
这里也可以写作
#include "Python.h"
但是我们现在直接指定到特定的头文件,可以减少很多编译时候的麻烦
然后便是这个包装过的C函数
size_t memory_callback(void *data, size_t size, size_t nmemb, void *content)
{
size_t realsize = size *nmemb;
char *p = *(char **)content;
size_t len = p ? strlen(p) : 0;
*(char **)content = realloc(p, len + realsize + 1);
p = *(char **)content;
memcpy(p + len, data, realsize);
p[len + realsize] = '\0';
return realsize;
}
这个函数是我们在调用
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memory_callback);
时候,调用的处理函数
通过CURLOPT_WRITEFUNCTION
指定curl_easy_setopt
的处理方式,在接受到返回的http包时候,自动调用memory_callback
函数来处理
而memory_callback
函数的结构所在cURL
的API说明中定义的
我们可以去cURL
的官网去查看说明,这里我摘查了一下
#include <curl/curl.h>
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_WRITEFUNCTION, write_callback);
现在我们来解释一下其中的几个参数的意义
首先ptr
是指向交付的数据的指针
size
和nmemb
相乘的结果则是代表返回数据的大小
而size
代表的是每个块的大小,nmemb
代表的是块的数目
而userdata
并不是在我们这里函数中定义的
userdata
是定义在这里
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_WRITEDATA, void *pointer)
注意:
我们这里定义时使用的名为是pointer
的指针,但是结合我们前面那个定义,pointer
代表的就是要传进write_callback
的第四个参数userdata
上面那个定义就是我们对userdata
的定义
如果你没有定义回写函数(wirte_callback
),那么,pointer
就必须初始化为一个FILE
类型的的文件句柄
然后通过这个文件句柄将送回的http包的内容写入文件中
char *p = *(char **)content;
这句话的意思是将返回的http包内容content
的地址赋给*p
然后将p指向的内存的大小计算出来,放入变量len
中
size_t len = p ? strlen(p) : 0;
最后,使用realloc
重新分配content
所指向的内存大小为返回的数据的大小
*(char **)content = realloc(p, len + realsize + 1);
最后把指针p
指向新的content
p = *(char **)content;
指针p
也只是一个中间变量,主要是未来防止丢失包的地址,先将其保存在p
指针中
然后改变完content
后,再用p
指向新的content
最后将数据data
存入新扩展的内存中
然后,我们进入主函数中
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memory_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)content);
主函数中的
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memory_callback);
将传回的数据通过回调函数处理,存入适当的空间中,然后执行下一个函数
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)content);
将content指针指向数据存储的位置
然后检查curl的工作情况
res = curl_easy_perform(curl);
最后判断一下res
的值,是否为正常的值
最后我们就可以使用python来包装我们的C代码了
if(!PyArg_ParseTuple(args, "s|ssss", &appid, &secret_key, &q, &from, &to))
{
return NULL;
}
从python中传入的五个参数,都是为string
类型的
然后我们调用C语言的函数
get_content(appid, secret_key, q, from, to, &content);
将返回的值写入content中,最后将C语言中的值转换成python的值然后返回python中
return Py_BuildValue("s", content);
最后的最后,那就是编译了
将要链接的库函数接在程序的后面
gcc -shared -I/usr/include/python2.7 -fPIC xhttp.c -lcurl -lssl -pthread -o xhttp.so
编译成so文件,就可以在这个目录下导入这个我们用C写的模块了