辅助脚本:
#!/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" ]; then
echo "Makefile not found!"
exit 1
fi
# 检查 objs/Makefile 是否存在
if [ ! -f "$OBJS_MAKEFILE" ]; then
echo "objs/Makefile not found!"
exit 1
fi
# 创建一个临时文件来存储修改后的内容
TEMP_FILE=$(mktemp)
# 替换 objs/Makefile 中的 CFLAGS
while IFS= read -r line; do
if [[ "$line" == CFLAGS* ]]; then
echo "CFLAGS = -pipe -O0 -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g" >> "$TEMP_FILE"
else
echo "$line" >> "$TEMP_FILE"
fi
done < "$OBJS_MAKEFILE"
# 替换原 objs/Makefile
mv "$TEMP_FILE" "$OBJS_MAKEFILE"
# 创建一个临时文件来存储修改后的内容
TEMP_FILE=$(mktemp)
# 读取 Makefile,找到并删除原有的 clean 命令
while IFS= read -r line; do
if [[ "$line" == "clean:" ]]; then
# 跳过原来的 clean 规则
read -r
continue
fi
# 替换 $(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 clean
build:
lsof -i tcp:10086 && fuser -k 10086/tcp ||true #这是自己加的
$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile #这是自己修改的的
install:
$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile install
modules:
$(MAKE) CFLAGS="$(CFLAGS)" -f objs/Makefile modules
upgrade:
/home/cser/work/code/nginx/nginxhome/sbin/nginx -t
kill -USR2 `cat /home/cser/work/code/nginx/nginxhome/logs/nginx.pid`
sleep 1
test -f /home/cser/work/code/nginx/nginxhome/logs/nginx.pid.oldbin
kill -QUIT `cat /home/cser/work/code/nginx/nginxhome/logs/nginx.pid.oldbin`
.PHONY: build install modules upgrade
CFLAGS = -O0 -g
clean:
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 1024
int 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];
// 创建 socket
if ((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);
// 绑定 socket
if (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_handler
deny 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 10086
void 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);
// 创建 socket
sockfd = 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;
}