C-BlogServer博客-基于Django的wsgi接口多线程C服务器

基于第一第二篇博客的内容来综合写的这篇博客

第二篇中的python调用将加入到第一篇的多线程c服务器中

将第二篇中的main函数分割

PyObject *pModule;
PyObject *pFunc;
PyObject *module;
PyObject *response;

int DjangoInit()//c调用python的django,初始化
{
    Py_Initialize();
    if (!Py_IsInitialized()) return -1;
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");
    pModule = PyImport_ImportModule("mysite.wsgi");
    if(!pModule){
            printf("import failed!\n");
            return 1;
        }
    pFunc = PyObject_GetAttrString(pModule, "application");
    if(!pFunc){
               printf("get function failed!\n");
               return 1;
        }
    module = Py_InitModule("start_response", start_response_methods);
    response = PyObject_GetAttrString(module, "start_response");
    return 0;
}

char* DjangoResponse(char* method,char* url,char* query)//初始化后可以返回提交网页请求
{
    PyObject *environ = PyDict_New();
    PyDict_SetItemString(environ, "REQUEST_METHOD", PyString_FromString(method));
    PyDict_SetItemString(environ, "SERVER_NAME", PyString_FromString("BlogServer"));
    PyDict_SetItemString(environ, "SERVER_PORT", PyString_FromString("80"));
    PyDict_SetItemString(environ, "QUERY_STRING", PyString_FromString(query));
    PyDict_SetItemString(environ, "PATH_INFO", PyString_FromString(url));
    PyDict_SetItemString(environ, "wsgi.input", PyString_FromString(""));
    PyObject *pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs, 0, environ);
    PyTuple_SetItem(pArgs, 1, response);
    char *http=PyString_AsString(PyObject_Str(PyObject_CallObject(pFunc, pArgs)));
    return http;
}


void DjangoDestory()//最后销毁
{
    Py_DECREF(response);
    Py_DECREF(pModule);
    Py_DECREF(pFunc);
    Py_Finalize();
    Py_DECREF(module);
}

其中还有一个很重要的回调函数,里面会设置http相应状态码,这个由于是多线程的不能使用静态全局变量,即便是加锁互斥也会对效率造成影响,实际上这个变量完全没有必要在线程间共享,这时候我们要用到线程私有变量,共享变量名

pthread_key_t key;//共享变量名

void dest(void *buf)  
{  
    //printf("destructor excuted in thread %d,param=%s\n",(int)pthread_self(),(char*)buf);  
}

static PyObject *start_response(PyObject *self, PyObject *args)
{
    pthread_setspecific(key,(void *)PyString_AsString(PyObject_Str(PyTuple_GetItem(args, 0))));//写入线程私有key变量
    //printf("%s\n",PyString_AsString(PyObject_Str(PyTuple_GetItem(args, 0))));
    //printf("%s\n",PyString_AsString(PyObject_Str(PyTuple_GetItem(args, 1))));
    //printf("%s\n",PyString_AsString(PyObject_Str(PyTuple_GetItem(args, 2))));
    return Py_None;
}


static struct PyMethodDef start_response_methods[] = {
    {"start_response", start_response, METH_VARARGS},
    {NULL, NULL}};

主函数部分增加django框架初始化以及销毁操作,同时增加创建key操作

int main(int argc,char* argv[])
{
    if(argc != 2) return 1;
    int server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    printf("socket_fd:%d\n", server_fd);
    struct sockaddr_in server_addr;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    int val = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int));
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int));
    if(!bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))) {char buf[30];printf("bind:%s:%d\n",inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),(unsigned int)ntohs(server_addr.sin_port));}
    else return 1;
    if(!listen(server_fd, 100)) {char buf[30];printf("listen:%s:%d\n",inet_ntop(AF_INET, &server_addr.sin_addr, buf, sizeof(buf)),(unsigned int)ntohs(server_addr.sin_port));}
    else return 1;
   
    if(DjangoInit()) return 1;//python初始化
    pthread_key_create(&key,dest);//key创建
    pthread_t ntid;
    pthread_create(&ntid, NULL, ListenClient, &server_fd);
   
    int loop;
    scanf("%d",&loop);

    close(server_fd);
    pthread_key_delete(key);//销毁key
    DjangoDestory();//python环境回收
    return 0;
}

最后就是socket读写交互与django的相应拼接了

void *HttpResponse(void *client)
{
    int client_fd = *(int*)client;
    char recvBuf[1024];
    recv(client_fd, recvBuf, sizeof(recvBuf), 0);
    char *p = strtok(recvBuf, "\r\n");
    int err = 1;
    if(p != NULL){
        char *method = strtok(p, " ");
        if(method != NULL){
            p = strtok(NULL," ");
            char *url = strtok(p,"?");
            if(url != NULL){
                char *query = strtok(NULL," ");
                if(query == NULL) query = "";
                char s1[100];
                char *s2 = DjangoResponse(method, url, query);
                if(s2 != NULL){
                    char *s = strstr(s2,"\r\n\r\n");
                    if(s != NULL){
                        sprintf(s1, "HTTP/1.1 %s\r\nServer:BlogServer\r\nContent-Length:%d\r\n", (char*)pthread_getspecific(key), (int)strlen(s) - 3);
                        //printf("%s\n%s\n%s\n%s\n",method, url, query, s2);
                        int size = strlen(s1)+strlen(s2)+1;
                          char *sendBuf = malloc(size);
                           strcpy(sendBuf, s1);
                            strcat(sendBuf, s2);
                        sendBuf[size-1] = '\0';
                        send(client_fd, sendBuf, size, 0);
                        free(sendBuf);
                    }
                    else err = 0;
                }
                else err = 0;
            }
            else err = 0;
        }
        else err = 0;
    }
    else err = 0;
    if(!err){
        char sendBuf[] = "HTTP/1.1 404 NOT FOUND\r\nServer:BlogServer\r\nContent-Length:0\r\n\r\n";
        send(client_fd, sendBuf, sizeof(sendBuf), 0);
    }
    shutdown(client_fd, SHUT_RDWR);
    close(client_fd);
    pthread_exit((void *)0);
}

对从客户端读取到的第一条重要的信息进行分割

GET /a?b HTTP1.1

得到

method为GET

path为/a

query为b

这样分割得到的消息传到DjangoResponse即可得到正文

正文去掉头再strlen()获得长度作为Content-Length

由于中间涉及太多指针操作,都要判断是否为空避免异常,c也没有很好的异常机制只能手动来处理一下

最后就上个图吧

Unnamed QQ Screenshot20150901210708

其实还是有一些问题没有解决,最主要的是静态文件没法返回,实际上直接用原来的wsgi接口来进行静态文件请求也是失败的,可能wsgi接口处理上仍然不够完善,以及post方法并没有进行测试,这也许要留到下一章进行更深度的分析了

转载于:https://my.oschina.net/tmj1993/blog/500342

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值