mongoose用于轻量级web服务使用,只要导入mongoose.c和mongoose.h即可,非常方便。
从6.7版本后,官方版本不再提供多线程接口,多线程服务需要自己实现。
本文以7.9版本为基础,实现多线程服务。
mongoose简介
mongoose下载地址: https://github.com/cesanta/mongoose
mg_mgr: mongoose的管理器。
mg_connection: mongoose的连接,每个连接创建一个对象。
mg_mkpipe: 创建一个socket管道,并返回一个socket,用于工作者线程返回数据
mongoose实现多线程的方式
当一个连接请求(MG_EV_HTTP_MSG)被接收到后,使用mg_mkpipe创建一个socket管道对,并将新建的connection和请求连接的connection连接起来,将socket管道对得到的socket句柄发送给工作者线程。工作者线程处理完成后将数据写入socket,然后通过两个连接间的关系,将数据返回给客户端。如下图所示:

但是,mongoose自带的examples中,每个请求都要调用一次mg_mkpipe,生成一个连接,而且mg_mkpipe使用的是udp协议生成,udp协议关闭时,对方是无法得知的,造成新生成的连接会被浪费掉。
所以要对这块进行改造。
1、在mg_connection结构体中增加一个socket标识(fd_woker),用来作为工作者线程的参数,并在结构体书初始化(mg_alloc_conn)的时候赋值为0,结构体销毁(mg_close_conn)的时候关闭连接。
2、在接收到http请求(MG_EV_HTTP_MSG)时,先判断当前连接有没有对应的fd_woker,如果有就直接使用它,如果没有时再使用mg_mkpipe新建。
struct userdata *data = (userdata *)calloc(1, sizeof(*data)); // worker will free it
// parse headers or use request body, duplicate data and pass it to worker thread
data->body = mg_strdup(hm->body); // worker will free it
data->sock = c->fd_worker ? c->fd_worker : (c->fd_worker = mg_mkpipe(c->mgr, pcb, c, true));
_beginthread(woker_thread_function, 0, data); // Start thread and pass data
3、工作者线程处理完后,不要关闭socket连接,等http连接断开后,都会自动关闭
4、工作者线程回复处理结果时,不要直接向socket发送结果数据(直接send大数据会发送错误),直接发送一个mg_iobuf结构体就好,因为是同一个进程,可以直接从内存中读取数据。
//工作者线程发送
mg_iobuf buf = { 0, 0, 0, 0 };
mg_iobuf_add(&buf, buf.buf.len, ret_msg.c_str(), ret_msg.length());
int e = send(p->sock, (const char *)&buf, sizeof(struct mg_iobuf), 0);
//socket管道线程接收
case MG_EV_READ:
mg_iobuf buf = { 0, 0, 0, 0 };
if (c->recv.len == sizeof(struct mg_iobuf)) {
memcpy(&buf, c->recv.buf, sizeof(struct mg_iobuf));
mg_http_reply(parent, 200, "", "%.*s\n", buf.size, buf.buf); // Respond!
}
文章介绍了如何在mongoose7.9版本上实现多线程服务,强调了从6.7版本后官方不再提供多线程接口。通过mg_mkpipe创建socket管道来处理HTTP请求,文中提出了在mg_connection结构体中增加标识来复用连接,避免每次请求都创建新的连接,从而优化资源使用。工作者线程处理完成后直接通过内存中的mg_iobuf结构体发送结果,而不是直接使用send函数,以防止错误。
1520

被折叠的 条评论
为什么被折叠?



