辅助脚本:
#!/bin/bash
mkdir -p $(pwd)/nginxhome
# 生成 Makefile,--prefix need a absolute path --with-stream表示要包括stream模块
auto/configure --prefix=$(pwd)/nginxhome --with-stream
# lsof -i tcp:10086 && fuser -k 10086/tcp ||true
# 定义 Makefile 的路径
MAKEFILE="Makefile"
OBJS_MAKEFILE="objs/Makefile"# 检查 Makefile 是否存在
if [ ! -f "$MAKEFILE" ]; thenecho "Makefile not found!"exit 1
fi# 检查 objs/Makefile 是否存在
if [ ! -f "$OBJS_MAKEFILE" ]; thenecho "objs/Makefile not found!"exit 1
fi# 创建一个临时文件来存储修改后的内容
TEMP_FILE=$(mktemp)# 替换 objs/Makefile 中的 CFLAGS
while IFS= read -r line; doif [[ "$line" == CFLAGS* ]]; thenecho "CFLAGS = -pipe -O0 -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g" >> "$TEMP_FILE"elseecho "$line" >> "$TEMP_FILE"fi
done < "$OBJS_MAKEFILE"# 替换原 objs/Makefile
mv "$TEMP_FILE" "$OBJS_MAKEFILE"# 创建一个临时文件来存储修改后的内容
TEMP_FILE=$(mktemp)# 读取 Makefile,找到并删除原有的 clean 命令
while IFS= read -r line; doif [[ "$line" == "clean:" ]]; then# 跳过原来的 clean 规则read -rcontinuefi# 替换 $(MAKE)line=${line/\$(MAKE)/\$(MAKE) CFLAGS=\"\$(CFLAGS)\"}# 保留其他行echo "$line" >> "$TEMP_FILE"
done < "$MAKEFILE"# 插入新的 clean 规则和 all 目标
{echo ""echo "CFLAGS = -O0 -g"echo ""echo "clean:"echo -e "\tfind objs -type f \( -name '*.o' -o -name '*.d' -o -name '*.so' -o -name '*.a' \) -exec rm -f {} +"echo -e "\trm -rf nginxhome/*"echo ""echo -e "all: build install\n\ndefault:\tall"echo -e "nginx:\tbuild install"
} >> "$TEMP_FILE"# 替换原 Makefile
mv "$TEMP_FILE" "$MAKEFILE"sync
echo "Makefile updated successfully."
make all CFLAGS="-O0 -g"
sudo fuser -k 10086/tcp #杀掉占用10086端口的进程
最终makefile:
default: build.PHONY: default cleanbuild:lsof -i tcp:10086 && fuser -k 10086/tcp ||true #这是自己加的 $(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile #这是自己修改的的 install:$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile installmodules:$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile modulesupgrade:/home/cser/work/code/nginx/nginxhome/sbin/nginx -tkill -USR2 `cat /home/cser/work/code/nginx/nginxhome/logs/nginx.pid`sleep 1test -f /home/cser/work/code/nginx/nginxhome/logs/nginx.pid.oldbinkill -QUIT `cat /home/cser/work/code/nginx/nginxhome/logs/nginx.pid.oldbin`.PHONY: build install modules upgradeCFLAGS = -O0 -gclean: find objs -type f \( -name '*.o' -o -name '*.d' -o -name '*.so' -o -name '*.a' \) -exec rm -f {} +rm -rf nginxhome/*all: build install #这是自己加的 default: all #这是自己加的
nginx: build install #这是自己加的
1:配置
#!!!一定要用绝对路径,这里设置安装路径,makefile中会用到
auto/configure --prefix=/home/cser/work/code/nginx/nginxhome
2:修改makefile:1-修改clean;2:添加all;3:修改default;4:修改$(MAKE),因为默认的build/install目标是没有用到CFLAGS,所以命令行传递给makefile脚本的CFLAGS会不起作用
笔记:all需要包括build 和install,clion要求makefile含有all,并且会调用clean删除文件,而默认的则是删除所有文件,不好,所以需要我们手动修改clean目标
clean:#find objs -type f \( -name '*.o' -o -name '*.a' -o -name '*.so' -o -name '*.lo' -o -name '*.la' -o -name '*.out' \) -exec rm -rf {} +find objs -type f -name '*.o' -exec rm -f {} +
.PHONY: default clean
.PHONY: build install modules upgrade
all: build install
default: all#修改$(MAKE)
旧:
build:$(MAKE) -f objs/Makefile
新:
build:$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile #!!!CFLAGS="xx",记得加双引号
3:make并关闭优化
make CFLAGS="-O0" CXXFLAGS="-O0"
4:然后重新load makefile project !!!这一步非常重要。可以点击图标或者tool->makefile->relaod makefile project
5:修改配置。
笔记:通过下面三项设置之后我们修改代码,clion可以自动重新编译;
笔记:启动以后可以 curl localhost:10086 检测nginx是否启动成功
5-1:要修改target为makefile中的target,这样每次构建的时候就会make 这个target,我们需要all,即我们需要build和install。
5-2:构建完之后我们还需要指定一个可运行程序,这个程序是我们–prefix指定的目录下的nginx程序,不能是objs目录下的nginx
5-3:修改端口,默认是80端口,因为1024以下的端口需要特权,root模式启动clion需要重新激活很麻烦,所以改成10086
(笔记:如果printf没有立即输出,可以用fflush(stdout);强制刷新)
vim /path/to/config
#这个path可以随便,因为我们会通过命令行参数-c指定配置文件
#listen 80; 改成 listen 10086;
完整配置如下:
#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;
}http { #!!!!nginx是根据这个模块名来调用不同的模块来解析这个配置,如果是http_abc,nginx就会调用http_abc模块来解析这个块include mime.types;default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] "$request" '# '$status $body_bytes_sent "$http_referer" '# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;sendfile on;#tcp_nopush on;#keepalive_timeout 0;keepalive_timeout 65;#gzip on;server {listen 10086;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}location /hello {alias html;index index.html index.htm;}#error_page 404 /404.html;# redirect server error pages to the static page /50x.html#error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {# proxy_pass http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {# root html;# fastcgi_pass 127.0.0.1:9000;# fastcgi_index index.php;# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;# include fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {# deny all;#}}# another virtual host using mix of IP-, name-, and port-based configuration##server {# listen 8000;# listen somename:8080;# server_name somename alias another.alias;# location / {# root html;# index index.html index.htm;# }#}# HTTPS server##server {# listen 443 ssl;# server_name localhost;# ssl_certificate cert.pem;# ssl_certificate_key cert.key;# ssl_session_cache shared:SSL:1m;# ssl_session_timeout 5m;# ssl_ciphers HIGH:!aNULL:!MD5;# ssl_prefer_server_ciphers on;# location / {# root html;# index index.html index.htm;# }#}}
stream {# 转发到后端服务server {listen 10087; # 监听 10087 端口proxy_pass 127.0.0.1:10088; # 转发到 10088 端口,我们写了一个程序监听10088端口}
}
5-4:指定命令行参数,包括两个:1:关闭后台运行,不关则nginx会以守
护进程方式运行,无法用cliondebug;2:设置配置文件路径。总的启动参数如下:
-c /home/cser/work/code/nginx/conf/nginx.conf -g "daemon off;"
6:clion调试多进程。如果不设置,那么就无法用clion debug子进程而对请求的处理都在子进程中
1.在程序的开始处设置一个断点(注意是在父进程中)
2.转到clion的调试器控制台(带有gdb的选项卡)
在fork出子进程前,依次输入set follow-fork-mode child、set detach-on-fork off
echo “hello” | nc localhost 10088 、 ./tcp_server
备注:最好不要在gdb里用方向键上翻下翻,因为此时虚拟机特别容易莫名其妙卡死,可能是vmware的问题也可能是我电脑的问题
accept=7 client=3 upstream=11
tcp_server程序:作为stream的backend
收到什么就返回:msg is :x
1:发送stream请求命令
sudo apt install netcat
lsof -i tcp:10088 && fuser -k 10088/tcp
echo "hello" | nc localhost 10088
gcc tcp_server.c -o tcp_server
./tcp_server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define DEFAULT_PORT 10088
#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {int port = DEFAULT_PORT;if (argc > 1) {port = atoi(argv[1]);}int server_fd, client_fd;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);char buffer[BUFFER_SIZE];// 创建 socketif ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket creation failed");exit(EXIT_FAILURE);}// 设置地址和端口server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(port);// 绑定 socketif (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Bind failed");close(server_fd);exit(EXIT_FAILURE);}// 开始监听if (listen(server_fd, 3) < 0) {perror("Listen failed");close(server_fd);exit(EXIT_FAILURE);}printf("Server listening on port %d\n", port);// 接受客户端连接while (1) {client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);if (client_fd < 0) {perror("Accept failed");continue;}printf("accept a connection\n");fflush(stdout);// 读取数据ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE);if (bytes_read > 0) {buffer[bytes_read] = '\0'; // 确保字符串结束// 构造响应char response[BUFFER_SIZE];snprintf(response, sizeof(response), "msg is : %s", buffer);printf("msg is : %s\n", buffer);fflush(stdout);// 等待用户输入printf("Press Enter to continue...\n");getchar(); // 等待用户输入// 发送响应write(client_fd, response, strlen(response));printf("send ok\n------------------------------------\n");}close(client_fd);}close(server_fd);return 0;
}
20241021 22:59 遗留问题:在哪里写回响应,是不是写回响应也是一个handler,而不是单独的函数?今天下班了。已解决,就是在handler中处理
nginx.conf
worker_processes 2; #开启多个子进程,单worker和多worker有些流程不一样
events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;#keepalive_timeout 0;keepalive_timeout 65;server {listen 10086;server_name localhost;location / {root html;index index.html index.htm; #curl localhost:10086可以debug http流程limit_except GET POST { #主要是用来debug ngx_http_request_handlerdeny all;}}location /hello {alias html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}}
stream { #echo "hello world" | nc localhost 10087可以dubug stream流程# 转发到后端服务server {listen 10087; # 监听 10087 端口proxy_pass 127.0.0.1:10088; # 转发到 10088 端口}
}
tcp_client_post.c:
用来debug:把请求行、请求头、请求体分开发送,从而可以更有效地debug http请求流程 ngxin在读取完header之后就调用ngx_http_process_request,然后当请求体数据到来时,ngxin是如何处理的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 10086void wait_for_input(int expected) {int input;while (1) {printf("请输入 %d 继续:", expected);scanf("%d", &input);if (input == expected) break;}
}int main() {int sockfd;struct sockaddr_in server_addr;const char *request_line = "POST / HTTP/1.1\r\n";const char *request_headers = "Host: localhost\r\nContent-Length: 10\r\n\r\n";const char *request_body = "helloworld";while (1) {// 等待输入 0 开始新的连接wait_for_input(0);// 创建 socketsockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("Socket 创建失败");exit(EXIT_FAILURE);}// 配置服务器地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_ADDR, &server_addr.sin_addr);// 连接服务器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("连接服务器失败");close(sockfd);continue;}printf("连接已建立。\n");// 等待输入 1 并发送请求行wait_for_input(1);send(sockfd, request_line, strlen(request_line), 0);printf("已发送请求行:\n%s", request_line);// 等待输入 2 并发送请求头wait_for_input(2);send(sockfd, request_headers, strlen(request_headers), 0);printf("已发送请求头:\n%s", request_headers);// 等待输入 3 并发送请求体wait_for_input(3);send(sockfd, request_body, strlen(request_body), 0);printf("已发送请求体:\n%s", request_body);// 接收并打印服务器响应printf("等待服务器响应...\n");char buffer[1024];int received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (received > 0) {buffer[received] = '\0';printf("服务器响应:\n%s\n", buffer);} else {printf("未收到服务器响应或连接已关闭。\n");}// 关闭连接close(sockfd);printf("------------------\n");}return 0;
}