nginx系列--(一)--调试环境搭建

辅助脚本:

#!/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;
}
相关推荐
速盾cdn7 分钟前
速盾:CDN缓存的工作原理是什么?
网络·安全·web安全
东华果汁哥15 分钟前
【linux 免密登录】快速设置kafka01、kafka02、kafka03 三台机器免密登录
linux·运维·服务器
咖喱鱼蛋36 分钟前
Ubuntu安装Electron环境
linux·ubuntu·electron
ac.char40 分钟前
在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
linux·ubuntu·npm
肖永威1 小时前
CentOS环境上离线安装python3及相关包
linux·运维·机器学习·centos
tian2kong1 小时前
Centos 7 修改YUM镜像源地址为阿里云镜像地址
linux·阿里云·centos
布鲁格若门1 小时前
CentOS 7 桌面版安装 cuda 12.4
linux·运维·centos·cuda
网络安全-杰克1 小时前
网络安全概论
网络·web安全·php
C-cat.1 小时前
Linux|进程程序替换
linux·服务器·microsoft
dessler1 小时前
云计算&虚拟化-kvm-扩缩容cpu
linux·运维·云计算