Python Http发送模块实现

来源:互联网 发布:手机fps软件 编辑:程序博客网 时间:2024/06/05 06:49

最近开源项目需要一个用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是指向交付的数据的指针

sizenmemb相乘的结果则是代表返回数据的大小

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写的模块了

0 0
原创粉丝点击