当前位置: 首页 > wzjs >正文

有什么网站可以叫人做图今日头条热点新闻

有什么网站可以叫人做图,今日头条热点新闻,wordpress 公网访问,野望王绩翻译本文使用cgi库编写cgi服务,再使用nginx进行转发。两者结合,实现简单的get请求和post请求的web服务。 问题提出 近年笔者实现的web服务,基本是用golang、nodejs语言。C/C的没有真正做过,为补充技术栈,一直想做。想了几…

本文使用cgi库编写cgi服务,再使用nginx进行转发。两者结合,实现简单的get请求和post请求的web服务。

问题提出

近年笔者实现的web服务,基本是用golang、nodejs语言。C/C++的没有真正做过,为补充技术栈,一直想做。想了几年,现在因某些缘故,不能只想了,于是抽出时间研究一下。

设计思路

1、用cgi库实现简单的后端业务服务,提供get和post请求。端口端口为9000。

2、用spawn-fcgi启动服务。

3、使用nginx做转发,nginx为容器方式运行,对外端口为87,内部端口为9000。

4、上述服务运行在虚拟机中,在本地物理机使用curl测试。

实践

依赖库下载编译

本业务程序需使用spawn-fcgi和fcgi-2.4.1这2个库(工具)。

spawn-fcgi下载地址:http://download.lighttpd.net/spawn-fcgi/releases-1.6.x/spawn-fcgi-1.6.4.tar.gz

fcgi-2.4.1 下载地址:https://github.com/FastCGI-Archives/fcgi2/releases,具体为:https://github.com/FastCGI-Archives/FastCGI.com/blob/master/original_snapshot/fcgi-2.4.1-SNAP-0910052249.tar.gz

编译简要步骤:

tar xf spawn-fcgi-1.6.4.tar.gz
cd spawn-fcgi-1.6.4
./configure --prefix=/home/latelee/tools/cgi
make
make installtar xf fcgi-2.4.1-SNAP-0910052249.tar.gz
cd fcgi-2.4.1-SNAP-0910052249
./configure --prefix=/home/latelee/tools/cgi
make
make install

注1:在fcgi-2.4.1-SNAP-0910052249/libfcgi/fcgio.cpp文件中添加#include <stdio.h>,否则编译失败,提示fcgio.cpp:70:72: error: 'EOF' was not declared in this scope

注2:如果系统上没有g++编译,则不会生成libfcgi++.so.0。库位于fcgi-2.4.1-SNAP-0910052249/libfcgi/.libs/

安装后得到文件:

$pwd
/home/latelee/tools/cgi$ tree -L 2
.
├── bin
│   ├── cgi-fcgi
│   └── spawn-fcgi
├── include
│   ├── fastcgi.h
│   ├── fcgiapp.h
│   ├── fcgi_config.h
│   ├── fcgimisc.h
│   ├── fcgio.h
│   ├── fcgios.h
│   └── fcgi_stdio.h
├── lib
│   ├── libfcgi.a
│   ├── libfcgi++.a
│   ├── libfcgi.la
│   ├── libfcgi++.la
│   ├── libfcgi.so -> libfcgi.so.0.0.0
│   ├── libfcgi++.so -> libfcgi++.so.0.0.0
│   ├── libfcgi.so.0 -> libfcgi.so.0.0.0
│   ├── libfcgi++.so.0 -> libfcgi++.so.0.0.0
│   ├── libfcgi.so.0.0.0
│   └── libfcgi++.so.0.0.0
└── share└── man	

在Makefile中直接使用上述库路径,由于要使用spawn-fcgi,因此将其拷贝到系统PATH目录中。

业务程序代码

核心C++代码如下,由于是测试用,因此代码不做过多的设计。

#include <fcgi_stdio.h>
#include <fcgiapp.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>
#include <time.h>#include "log.h"#define THREAD_COUNT 4
#define VERSION "1.0.0"// 获取编译时间字符串
const char* get_build_time() {static char build_time[64] = {0};if (build_time[0] == '\0') {// 使用 __DATE__ 和 __TIME__ 宏获取编译时间snprintf(build_time, sizeof(build_time), "%s %s", __DATE__, __TIME__);}return build_time;
}char* get_request_path(FCGX_Request* request) {char* request_uri = FCGX_GetParam("REQUEST_URI", request->envp);if (!request_uri) return strdup("");// 去除查询字符串char* question_mark = strchr(request_uri, '?');if (question_mark) {return strndup(request_uri, question_mark - request_uri);} else {return strdup(request_uri);}
}void send_response(FCGX_Request* request, const char* response) {FCGX_FPrintF(request->out, "%s", response);
}void handle_get(FCGX_Request* request) {char* path = get_request_path(request);log_printf("GET request for path: %s\n", path);if (strcmp(path, "/info") == 0) {// 返回版本信息char response[512];snprintf(response, sizeof(response),"Status: 200 OK\r\n""Content-Type: application/json\r\n\r\n""{\"service\": \"FastCGI Demo\", ""\"version\": \"%s\", ""\"build_time\": \"%s\", ""\"current_time\": \"%ld\"}",VERSION, get_build_time(), time(NULL));send_response(request, response);} else {send_response(request, "Status: 404 Not Found\r\n""Content-Type: text/plain\r\n\r\n""Endpoint not found. Try /info");}free(path);
}char* php_url_decode(const char* str) {if (!str) return NULL;char* result = (char*)malloc(strlen(str) + 1);char* dst = result;const char* src = str;while (*src) {if (*src == '+') {*dst++ = ' ';src++;} else if (*src == '%' && isxdigit(src[1]) && isxdigit(src[2])) {char hex[3] = {src[1], src[2], '\0'};*dst++ = (char)strtol(hex, NULL, 16);src += 3;} else {*dst++ = *src++;}}*dst = '\0';return result;
}char* parse_form_data(const char* data, const char* key) {if (!data || !key) return NULL;char* copy = strdup(data);char* token = strtok(copy, "&");char* result = NULL;while (token) {char* eq = strchr(token, '=');if (eq) {*eq = '\0';if (strcmp(token, key) == 0) {result = strdup(eq + 1);break;}}token = strtok(NULL, "&");}free(copy);return result;
}void handle_post(FCGX_Request* request) {char* path = get_request_path(request);log_printf("POST request received,path: %s\n", path);free(path);char* content_length_str = FCGX_GetParam("CONTENT_LENGTH", request->envp);int content_length = content_length_str ? atoi(content_length_str) : 0;if (content_length <= 0) {send_response(request, "Status: 411 Length Required\r\n""Content-Type: text/plain\r\n\r\n""Content-Length required");return;}char* post_data = (char*)malloc(content_length + 1);FCGX_GetStr(post_data, content_length, request->in);post_data[content_length] = '\0';char* decoded_data = php_url_decode(post_data);const char* key = "name";char* value = parse_form_data(decoded_data, key);char response[1024];snprintf(response, sizeof(response), "Status: 200 OK\r\n""Content-Type: application/json\r\n\r\n""{\"received\": \"%s\", \"%s\": \"%s\"}",post_data, key, value ? value : "not found");send_response(request, response);free(post_data);free(decoded_data);if (value) free(value);
}void process_request(FCGX_Request* request) {char* request_method = FCGX_GetParam("REQUEST_METHOD", request->envp);log_printf("Received %s request\n", request_method ? request_method : "unknown");if (strcmp(request_method, "POST") == 0) {handle_post(request);} else if (strcmp(request_method, "GET") == 0) {handle_get(request);} else {send_response(request, "Status: 405 Method Not Allowed\r\n""Content-Type: text/plain\r\n\r\n""Only GET and POST methods are supported");}
}void* handle_requests(void* arg) {FCGX_Request request;FCGX_InitRequest(&request, 0, 0);while (FCGX_Accept_r(&request) == 0) {process_request(&request);FCGX_Finish_r(&request);}return NULL;
}int main()
{// 初始化日志系统if (log_init() != 0) {fprintf(stderr, "Failed to initialize logging system\n");return EXIT_FAILURE;}log_printf("Server started. Version: %s, Build time: %s\n", VERSION, get_build_time());FCGX_Init();pthread_t threads[THREAD_COUNT];for (int i = 0; i < THREAD_COUNT; i++) {pthread_create(&threads[i], NULL, handle_requests, NULL);}for (int i = 0; i < THREAD_COUNT; i++) {pthread_join(threads[i], NULL);}log_printf("Server shutting down\n");// 清理日志系统log_cleanup();return 0;
}

其中,log_printf等是日志模块函数,不是重点,因此不列出。要说明的是,本次代码绝大部分是使用AI经多次迭代后得到的,其风格与笔者的稍有不同,但也不修改了(以证明使用AI)。不过,像char* post_data = (char*)malloc(content_length + 1);这种代码就无法生成正确的版本,要借助编译器修正。

鉴于笔者不太喜欢动态库,因此用静态库编译,Makefile核心要点:

LIBS    += /home/latelee/tools/cgi/lib/libfcgi.a
LDFLAGS += $(LIBS) -lpthread -lrt INC = ./ ./inc /home/latelee/tools/cgi/include

运行:

spawn-fcgi -p 9000 -n ./a.out

nginx转发

本次使用nginx容器进行转发,docker-compose文件如下:

version: '3.8'services:nginx-forward:image: registry.cn-shenzhen.aliyuncs.com/hxr/nginx:1.23.tbcontainer_name: nginx-forwardhostname: nginx-forwardrestart: always#command: "sleep 10000000"volumes:- ./log/nginx:/var/log/nginx- ./config/nginx.conf:/etc/nginx/nginx.conf- ./config/conf.d:/etc/nginx/conf.denvironment:- TZ=Asia/Shanghai- AA=aaaadddaadports:- 87:9000networks:- custom-net
networks:custom-net:driver: bridge

其中nginx.conf会加载nginx.conf目录下的文件,其下有http_cgi.conf配置文件,内容如下:

server {listen       9000;listen  [::]:9000;server_name  localhost;location / {fastcgi_pass 192.168.28.11:9000;include fastcgi_params;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_param PATH_INFO $fastcgi_script_name;# 允许POST请求fastcgi_param REQUEST_METHOD $request_method;fastcgi_param CONTENT_TYPE $content_type;fastcgi_param CONTENT_LENGTH $content_length;}# redirect server error pages to the static page /50x.html#error_page   500 502 503 504  /50x.html;location = /50x.html {root   /usr/share/nginx/html;}
}

其中关键的地方是指定后端业务程序IP和端口,可以是本机,也可以是同网络其它主机,但一定要正确。

fastcgi_pass 192.168.28.11:9000;

测试

本次使用curl进行测试。

get请求示例:

$ curl http://192.168.28.11:87/info
{"service": "FastCGI Demo", "version": "1.0.0", "build_time": "Apr  4 2025 22:17:22", "current_time": "1743776337"}

post请求示例:

$ curl -X POST -d "name=latelee&age=38" http://192.168.28.11:87/api
{"received": "name=latelee&age=38", "name": "latelee"}

可以看到,已经能正确处理请求了。

扩展知识

后续考虑使用Tengine替换nginx来实验,再后续可能考虑使用商密的SSL证书。

小结

总体上,用C/C++实现感觉有些麻烦,涉及东西也多。不如用golang来得直接。也正是如此,笔者负责的和web有关的工程,都不用C/C++。即使需要和底层动态库交互,也有相应的方法实现(可参考笔者写的golang有关文章),维护起来相对简单。如果出于性能方面的考虑,的确应用稍底层的语言,不过,就笔者在生产上使用的情况看,还没有出现因语言本身导致的性能问题,如果一定要举例,那就是因为日志模块没有用异步写入,导致处理请求整体耗时较高,虽然在秒级以内,但也不能接受,后来修正,再有本地缓存加持,单次处理可在20毫秒内完成。

nginx无法转发问题

起初没留意http_cgi.conf的配置,nginx的error.log有如下错误:

*8 upstream sent unsupported FastCGI protocol version: 72 while reading response header from upstream, client: 192.168.28.11, server: localhost, request: "POST /api HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "192.168.28.11:87"

信息里提到不支持FastCGI协议版本,或说nginx和自编的cgi程序协议版本不对。后来才发现是IP写错了。因为我的nginx使用容器启动,而配置文件的IP写成了127.0.0.1:9000,而恰好容器里监听的端口也是9000,就是说nginx的端口转给自己了。将IP改为主机IP即可解决问题。但当时调试时思路不够清晰,搞了好久才发现问题。

http://www.dtcms.com/wzjs/159292.html

相关文章:

  • 公司网站界面设计做高端网站公司
  • 网站模版建设工具应用商店aso优化
  • 网站模块源码百度一级代理商
  • 邯郸有做网站的吗百度起诉seo公司
  • 网站建设的背景意义精准客户截流软件
  • 广告优化师培训企业网站seo推广方案
  • 长沙关键词优化推荐谷歌优化培训
  • b2b网站制作平台北京seo公司助力网络营销
  • 阿里云自助建站教程网站制作设计
  • 凡科做网站关键词腾讯朋友圈广告代理
  • 百度seo公司报价南京seo圈子
  • 网站后台模板怎样使用品牌营销策划包括哪些内容
  • 网站可以做多少事情天津seo推广服务
  • 有哪些网站可以做h5在线识图
  • wordpress 进销存主题迈步者seo
  • 自己想做一个网站怎么做网上教育培训机构排名
  • app网站开发河 又网站制作费用多少
  • 如何看网站是否优化沈阳seo
  • 网站建设佰首选金手指二七国际新闻最新消息十条摘抄
  • 用aspx做的网站郑州最新通告
  • 深圳市住房和建设局招聘seo营销推广多少钱
  • wordpress页面加载jsseo基础培训
  • 网站主机域名网页设计与制作书籍
  • 广东企业微信网站建设人民网今日头条
  • 石家庄网站建设团队制作网页一般多少钱
  • 做网站的公司吉林磁力猫引擎
  • 网站转微信小程序开发免费建自己的网站
  • 宁波网站建设制作的公司重庆电子商务seo
  • 建设一个电商网站的步骤东莞seo建站如何推广
  • java企业门库网站开发网站建设培训机构