Vâng, nó có thể được sử dụng. Những người khác đã đề cập đến các phương pháp khác nhau. Đây là cách tiếp cận của riêng tôi. Ưu điểm là nó hoàn toàn di động và khép kín, tất cả các thư viện được chọn chỉ phụ thuộc vào ANSI C. Thiết lập nó chỉ yêu cầu Linux Kernel và trình biên dịch C (Và những thứ rõ ràng như Busybox, bash, v.v.) (hoặc Windows và một trình biên dịch), không cần thêm thư viện, không cần cài đặt lớn.
Kết quả là một chương trình duy nhất vừa là máy chủ web vừa là trình tạo trang động (Thay thế cả "apache" và "php"), nó cũng sẽ có quyền truy cập cơ sở dữ liệu qua sqlite.
Thư viện đã sử dụng:
- Mongoose - Máy chủ http
- Sqlite - Cơ sở dữ liệu SQL
- MiniXML - Làm cho việc tạo trang động dễ dàng hơn. giống như Javascript
createElement
Phần còn lại của câu trả lời này là một hướng dẫn thiết lập hoàn chỉnh cho Linux. Cả SQlite và MiniXML đều là tùy chọn, nhưng hướng dẫn bao gồm toàn bộ cài đặt. Tùy thuộc vào bạn để nhận xét các phần không cần thiết nếu bạn quan tâm đến việc vô hiệu hóa sqlite hoặc MiniXML.
1. Tải xuống 3 thư viện
2. Chuẩn bị thư mục của bạn
- Tạo một thư mục trống (Chúng tôi sẽ gọi nó là thư mục chính)
- Đặt các tệp sau vào đó:
- Từ tar.gz sqlite:
sqlite3.c , sqlite3.h
- Từ zip Mongoose:
mongoose.c , mongoose.h
- Từ mxml tar.gz:
mxml.h
3. Biên dịch mxml
Bạn có thể nhận thấy mxml.c bị thiếu, điều này là do chúng ta cần tạo một thư viện mxml tĩnh. Chuyển đến thư mục nơi mxml tar.gz đã được tải xuống và thực hiện:
tar -xvf mxml-<version>.tar.gz #Extract the tar
cd mxml-<version> #Go to the newly extracted directory
./configure #prepare the compiler
make #compile, you may need to install "make" first.
Sau khi quá trình biên dịch kết thúc, nhiều tệp sẽ được tạo, tệp quan tâm duy nhất đối với chúng tôi là libmxml.a
, sao chép tệp đó vào thư mục chính.
3.1 Doublecheck
Kiểm tra xem thư mục chính có các mục sau:
- Đối với cầy mangut:
mongoose.c, mongoose.h
- Đối với mxml:
libmxml.a, mxml.h
- cho sqlite:
sqlite.c, sqlite.h
4. chính.c
Hãy tạo chương trình thực tế, tạo một main.c
tệp trong thư mục chính, đây là bộ xương để bạn bắt đầu.
#include <string.h>
#include <stdio.h>
#include "mongoose.h"
#include "mxml.h"
#include "sqlite3.h"
/***Sqlite initialization stuff***/
//comment out everything sqlite related if you don't want sqlite, including the callback function and the include "sqlite3.h"
static int callback(void * custom, int argc, char **argv, char **azColName);
char *zErrMsg = 0;
sqlite3 *db;
int rc;
/***Just some laziness shortcut functions I made***/
typedef mxml_node_t * dom; //type "dom" instead of "mxml_node_t *"
#define c mxmlNewElement //type "c" instead of "mxmlNewElement"
inline void t(dom parent,const char *string) //etc
{
mxmlNewText(parent, 0, string);
}
//type "sa" instead of "mxmlElementSetAttr"
inline void sa(dom element,const char * attribute,const char * value)
{
mxmlElementSetAttr(element,attribute,value);
}
//The only non boilerplate code around in this program is this function
void serve_hello_page(struct mg_connection *conn)
{
char output[1000];
mg_send_header(conn,"Content-Type","text/html; charset=utf-8");
mg_printf_data(conn, "%s", "<!DOCTYPE html>");
//This literally prints into the html document
/*Let's generate some html, we could have avoided the
* xml parser and just spat out pure html with mg_printf_data
* e.g. mg_printF_data(conn,"%s", "<html>hello</html>") */
//...But xml is cleaner, here we go:
dom html=mxmlNewElement(MXML_NO_PARENT,"html");
dom head=c(html,"head");
dom meta=c(head,"meta");
sa(meta,"charset","utf-8");
dom body=c(html,"body");
t(body,"Hello, world<<"); //The < is auto escaped, neat!
c(body,"br");
t(body,"Fred ate bred");
dom table=c(body,"table");
sa(table,"border","1");
//populate the table via sqlite
rc = sqlite3_exec(db, "SELECT * from myCoolTable", callback, table, &zErrMsg);
if( rc!=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
mxmlSaveString (html,output,1000, MXML_NO_CALLBACK);
mg_printf_data(conn, "%s", output);
mxmlDelete(html);
}
//sqlite callback
static int callback(void * custom, int argc, char **argv, char **azColName)
{
//this function is executed for each row
dom table=(dom)custom;
dom tr=c(table,"tr");
dom td;
int i;
for(i=0; i<argc; i++)
{
td=c(tr,"td");
if (argv[i])
t(td, argv[i]);
else
t(td, "NULL");
printf("%s == %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
static int event_handler(struct mg_connection *conn, enum mg_event ev)
{
if (ev == MG_AUTH)
{
return MG_TRUE; // Authorize all requests
}
else if (ev == MG_REQUEST)
{
if (!strcmp(conn->uri, "/hello"))
{
serve_hello_page(conn);
return MG_TRUE; // Mark as processed
}
}
return MG_FALSE; // Rest of the events are not processed
}
int main(void)
{
struct mg_server *server = mg_create_server(NULL, event_handler);
//mg_set_option(server, "document_root", "."); //prevent dir listing and auto file serving
//TODO can I allow file listing without dir listing in a specified directory?
mg_set_option(server, "listening_port", "8080");
rc = sqlite3_open("db.sqlite3", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return(1);
}
printf("Server is running on port 8080!\n");
for (;;)
{
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);
sqlite3_close(db);
return 0;
}
/*
* useful stuff:
* mg_send_file(struct mg_connection *, const char *path); - serve the file at *path*/
Cuối cùng, biên dịch!
Hãy biên dịch. cd
vào thư mục chính của bạn và thực hiện những điều này:
gcc -c main.c
gcc -c mongoose.c
gcc -c sqlite3.c
gcc -o server.out main.o mongoose.o sqlite3.o -ldl -lpthread -lmxml -L .
Bây giờ, thực thi server.out với /server.out
và điều hướng đếnlocalhost:8080/hello
Làm xong :)