基于第一第二篇博客的内容来综合写的这篇博客
第二篇中的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也没有很好的异常机制只能手动来处理一下
最后就上个图吧
其实还是有一些问题没有解决,最主要的是静态文件没法返回,实际上直接用原来的wsgi接口来进行静态文件请求也是失败的,可能wsgi接口处理上仍然不够完善,以及post方法并没有进行测试,这也许要留到下一章进行更深度的分析了