Python HTTP发送模块实现

最近开源项目需要一个用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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值