Linux应用实战之网络服务器(五) 登录服务器初步调试

0、前言

准备做一个Linux网络服务器应用实战,通过网页和运行在Linux下的服务器程序通信,这是第五篇,编写服务器程序,与编写好的登录界面进行初步调试。

1、服务器编程

1.1 TCP服务器编程

在之前的登录界面中,我们指定了登录服务器的IP和端口号,其中IP即为服务器的IP地址,端口号即为服务器监听的端口,服务器编程即为TCP编程,具体流程可参考之前的文章Linux应用 TCP网络编程,HTTP每次与服务器通信都会创建一个TCP连接,初步编写代码如下,接收客户端发送的数据进行打印:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>


#define PORT 8081
#define MAX_SIZE 1024 * 10

int main() 
{
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // 创建TCP套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定套接字到指定端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听端口
    if (listen(server_fd, 3) < 0) {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d\n", PORT);

    while(1)
    {
        printf("waiting......\n");
        // 接受连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) 
        {
            perror("accept failed");
            exit(EXIT_FAILURE);
        }
        char rcvbuffer[MAX_SIZE] = {0};
        
        bzero((char *)rcvbuffer, sizeof(rcvbuffer));
        int bytesReceived = read(new_socket, rcvbuffer, MAX_SIZE);
        printf("bytesReceived = %d\n", bytesReceived);
        if (bytesReceived > 0) 
        {
            printf("Received request: \n%s\n", rcvbuffer);
        
        } 
        
        close(new_socket);
    }

    close(server_fd);

    return 0;
}

在虚拟机上运行该服务程序,注意网页中的IP地址和端口号要与虚拟机地址还有服务器监听的端口号一致,测试结果如下:

1.2 OPTIONS处理

在登录界面连接的过程中服务器并没有按期望收到POST请求,而是收到了OPTIONS,查阅了相关资料:OPTIONS请求在CORS(跨源资源共享)机制中是一个预检请求(preflight request)。当浏览器遇到一个非简单请求(non-simple request)时,它会在实际请求之前自动发送一个OPTIONS请求到服务器,以检查服务器是否允许这个跨域请求。

因此,服务器首先需要处理OPTIONS请求然后才能收到正常的POST,简单添加一下处理代码:

if (bytesReceived > 0) 
{
    printf("Received request: \n%s\n", rcvbuffer);

    if(strstr(rcvbuffer, "OPTIONS") != NULL)
    {
        // 构造CORS响应头部  
        const char* headers = "HTTP/1.1 200 OK\r\n"
                           "Access-Control-Allow-Origin: *\r\n"  
                           "Access-Control-Allow-Methods: POST, OPTIONS\r\n"  
                           "Access-Control-Allow-Headers: Content-Type\r\n"  
                           "Access-Control-Max-Age: 86400\r\n"  
                           "X-Content-Type-Options: nosniff\r\n"
                           "Cache-Control: no-cache, no-store, must-revalidate\r\n"
                           "Content-Length: 0\r\n"  
                           "Connection: close\r\n"  
                           "\r\n"; 
    
        printf("send:\n%s", headers);
        send(new_socket, headers, strlen(headers), 0);
    }
} 

继续测试,可以收到携带账号密码的POST请求:

1.3  登录请求处理

收到登录请求后可以获取到POST中携带的用户名和密码,服务器验证通过后回复成功,登录侧就可以显示成功,先简单添加一下登录处理,检测到登录后直接回复成功,修改代码如下:

if (bytesReceived > 0) 
{
    printf("Received request: \n%s\n", rcvbuffer);

    if(strstr(rcvbuffer, "OPTIONS") != NULL)
    {
        // 构造CORS响应头部  
        const char* headers = "HTTP/1.1 200 OK\r\n"
                           "Access-Control-Allow-Origin: *\r\n"  
                           "Access-Control-Allow-Methods: POST, OPTIONS\r\n"  
                           "Access-Control-Allow-Headers: Content-Type\r\n"  
                           "Access-Control-Max-Age: 86400\r\n"  
                           "X-Content-Type-Options: nosniff\r\n"
                           "Cache-Control: no-cache, no-store, must-revalidate\r\n"
                           "Content-Length: 0\r\n"  
                           "Connection: close\r\n"  
                           "\r\n"; 
    
        printf("send:\n%s", headers);
        send(new_socket, headers, strlen(headers), 0);
    }
    else if(strstr(rcvbuffer, "username") != NULL)
    { 
        const char* json_response = "{\"status\":\"ok\"}";  
        int json_response_length = strlen(json_response);  
          
        // 构造HTTP响应头部  
        const char* http_version = "HTTP/1.1";  
        const char* status_code = "200";  
        const char* status_message = "OK";  
        const char* access_control_allow_origin = "Access-Control-Allow-Origin: *\r\n";
        const char* content_type = "Content-Type: application/json\r\n";  
        const char* content_length = "Content-Length: ";  
        char content_length_header[32];  
        snprintf(content_length_header, sizeof(content_length_header), "%d", json_response_length);  
          
        // 构造完整的HTTP响应  
        char response[1024] = {0}; // 假设响应不会超过1024字节  
        snprintf(response, sizeof(response),  
                 "%s %s %s\r\n" 
                 "%s"
                 "%s"  
                 "%s%s\r\n"  
                 "\r\n"  
                 "%s",  
                 http_version, status_code, status_message, access_control_allow_origin, 
                 content_type, content_length_header, "\r\n",  
                 json_response);  
    
        printf("send:\n%s\n", response);
        send(new_socket, response, strlen(response), 0);
    } 
} 

再进行登录测试发现可以显示登录成功:

1.4 查询请求

修改查询界面HTML代码,添加按键点击后向服务器发送不同的请求命令:

<script type="text/javascript">
    function queryData(query) {
        var url = 'http://172.21.247.112:8081/query?q=' + query;

        fetch(url)
            .then(response => response.text())
            .then(data => {
                document.getElementById("result").value = data;
            })
            .catch(error => {
                console.error('请求出错:', error);
				alert("error!");
            });
    }
</script>

在服务器端添加简单的匹配字符串处理:


else if(strstr(rcvbuffer, "query1") != NULL)
{
	char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query1";
	printf("send:\n%s\n", response);
	send(new_socket, response, strlen(response), 0);
}
else if(strstr(rcvbuffer, "query2") != NULL)
{
	char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query2";
	printf("send:\n%s\n", response);
	send(new_socket, response, strlen(response), 0);
}
else if(strstr(rcvbuffer, "query3") != NULL)
{
	char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query3";
	printf("send:\n%s\n", response);
	send(new_socket, response, strlen(response), 0);
}
else if(strstr(rcvbuffer, "query4") != NULL)
{
	char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query4";
	printf("send:\n%s\n", response);
	send(new_socket, response, strlen(response), 0);
}

最终实现如下效果,点击不同的按键收到服务器不同的回复:

2、优化

我们在服务器测编写文件发送接口,将HTML文件发送至客户端,客户端只需要输入服务器的地址端口等信息就可以从服务器获取登录界面,登录界面登录成功后向服务器发送请求界面,然后在请求界面进行数据请求。

基于上述分析,在服务器测添加相关代码,最终代码整合如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>


#define PORT 8081
#define MAX_SIZE 1024 * 5

// 发送html文件给客户端
void vSendHtmlToCllient(const char *filepath,int new_socket)
{
    FILE *file;
    char *response_header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\rAccess-Control-Allow-Origin: *\r\nCache-Control: no-cache, no-store, must-revalidate\r\nX-Content-Type-Options: nosniff\r\n\r\n";
    
    // 发送响应头部
    send(new_socket, response_header, strlen(response_header), 0);
    printf("send:\n%s\n", response_header);
    
    // 读取文件内容并发送
    char buffer[1024] = {0};
    size_t bytes_read;

    file = fopen(filepath, "r");
    if (file == NULL) 
    {
        perror("fopen");
        exit(EXIT_FAILURE);
    }
    
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) 
    {
        send(new_socket, buffer, bytes_read, 0);
        printf("%s", buffer);
        bzero(buffer, sizeof(buffer));
    }
}

int main() 
{
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char rcvbuffer[MAX_SIZE] = {0};

    // 创建TCP套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定套接字到指定端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听端口
    if (listen(server_fd, 3) < 0) {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d\n", PORT);

    while(1)
    {
        printf("waiting......\n");
        // 接受连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) 
        {
            perror("accept failed");
            exit(EXIT_FAILURE);
        }
        
        bzero((char *)rcvbuffer, sizeof(rcvbuffer));
        int bytesReceived = read(new_socket, rcvbuffer, MAX_SIZE);
        printf("bytesReceived = %d\n", bytesReceived);
        if (bytesReceived > 0) 
        {
            printf("Received request: \n%s\n", rcvbuffer);

            // 只做简单处理
            if(strstr(rcvbuffer, "OPTIONS") != NULL)
            {
                // 构造CORS响应头部  
                const char* headers = "HTTP/1.1 200 OK\r\n"
                                   "Access-Control-Allow-Origin: *\r\n"  
                                   "Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate"
                                   "Access-Control-Allow-Methods: POST, OPTIONS\r\n"  
                                   "Access-Control-Allow-Headers: Content-Type\r\n"  
                                   "Access-Control-Max-Age: 86400\r\n"  
                                   "X-Content-Type-Options: nosniff\r\n"
                                   "Cache-Control: no-cache, no-store, must-revalidate\r\n"
                                   "Content-Length: 0\r\n"  
                                   //"Connection: close\r\n"  
                                   "\r\n"; 
            
                printf("send:\n%s", headers);
                send(new_socket, headers, strlen(headers), 0);
            }
            else if(strstr(rcvbuffer, "username") != NULL)
            { 
                const char* json_response = "{\"status\":\"ok\"}";  
                int json_response_length = strlen(json_response);  
                  
                // 构造HTTP响应头部  
                const char* http_version = "HTTP/1.1";  
                const char* status_code = "200";  
                const char* status_message = "OK";  
                const char* access_control_allow_origin = "Access-Control-Allow-Origin: *\r\n";
                const char* access_control_allow_Cache = "Cache-Control: no-store, no-cache, must-revalidate\r\n";
                const char* content_type = "Content-Type: application/json\r\n";  
                const char* content_length = "Content-Length: ";  
                char content_length_header[32];  
                snprintf(content_length_header, sizeof(content_length_header), "%d", json_response_length);  
                  
                // 构造完整的HTTP响应  
                char response[1024] = {0}; // 假设响应不会超过1024字节  
                snprintf(response, sizeof(response),  
                         "%s %s %s\r\n" 
                         "%s"
                         "%s"  
                         "%s"
                         "%s%s\r\n"  
                         "\r\n"  
                         "%s",  
                         http_version, status_code, status_message, access_control_allow_origin, access_control_allow_Cache,
                         content_type, content_length_header, "\r\n",  
                         json_response);  
            
                printf("send:\n%s\n", response);
                send(new_socket, response, strlen(response), 0);
            }
            else if(strstr(rcvbuffer, "query1") != NULL)
            {
                char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query1";
                printf("send:\n%s\n", response);
                send(new_socket, response, strlen(response), 0);
            }
            else if(strstr(rcvbuffer, "query2") != NULL)
            {
                char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query2";
                printf("send:\n%s\n", response);
                send(new_socket, response, strlen(response), 0);
            }
            else if(strstr(rcvbuffer, "query3") != NULL)
            {
                char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query3";
                printf("send:\n%s\n", response);
                send(new_socket, response, strlen(response), 0);
            }
            else if(strstr(rcvbuffer, "query4") != NULL)
            {
                char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n\r\nHello from server Reply to query4";
                printf("send:\n%s\n", response);
                send(new_socket, response, strlen(response), 0);
            }
            else if(strstr(rcvbuffer, "gethtml") != NULL)
            {
                vSendHtmlToCllient("./Require.html",new_socket);
            }
            else if(strstr(rcvbuffer, "Login") != NULL)
            {
                vSendHtmlToCllient("./Login.html",new_socket);
            }
        } 

        close(new_socket);
    }

    close(server_fd);
    return 0;
}

登录界面也需要添加登录成功后获取请求界面的相关代码,最终代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            text-align: center;
            padding: 20px;
        }
 
        h2 {
            color: #333;
        }
 
        form {
            max-width: 300px;
            margin: 0 auto;
            background-color: #fff;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
 
        label {
            display: block;
            text-align: left;
            margin-bottom: 5px;
        }
 
        input {
            width: calc(100% - 10px);
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 3px;
            display: inline-block;
        }
 
        button {
            padding: 8px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
 
        button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <h2>Login</h2>
    <form>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username">
        <label for="password">Password:</label>
		<input type="password" id="password" name="password"><br><br>
        <button type="button" onclick="submitForm()">Login</button>
    </form>
</body>
<script>
	function submitForm() {  
    const username = document.getElementById("username").value;  
    const password = document.getElementById("password").value;  
  
    const url = "http://172.21.247.112:8081/login"; // 服务器地址  
  
    // 第一个 fetch 请求  
    fetch(url, {  
        method: "POST",  
        mode: 'cors',  
        headers: {  
            "Content-Type": "application/json"  
        },  
        body: JSON.stringify({ username: username, password: password })  
    })  
    .then(response => {  
        console.log("第一个请求的响应 json!");  
        return response.json();  
    })  
    .then(data => {  
        console.log("第一个请求接收到的数据:", data);  
        if (data.status === 'ok') {  
            console.log("第一个请求登录成功!");  
			alert("登录成功!"); 
			fetch("http://172.21.247.112:8081/gethtml")
			.then(response => {
				if (!response.ok) {
					throw new Error('Network response was not ok');
				}
				return response.text();
			})
			.then(html => {
				console.log(html);

				// 打开文档以进行写入  
				document.open();  
				  
				// 写入从服务器接收到的 HTML 内容  
				document.write(html);  
				  
				// 关闭文档  
				document.close();				
			})
			.catch(error => {
				console.error('There was a problem with your fetch operation:', error);
				alert("There was a problem with your fetch operation: " + error);
			});
        } else {   
            alert("登录失败!");  
        }  
    })  
    .catch(error => {   
        alert("登录错误!" + error);  
    }); 
}
</script>
</html>

请求界面最终代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Query Interface</title>
<style>
  body {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    height: 100vh;
    margin: 0;
  }

  h2 {
    text-align: center;
  }

  div {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
  }

  button {
    padding: 10px 20px;
    margin: 5px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
  }

  button:hover {
    background-color: #0056b3;
  }

  textarea {
    margin-top: 10px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
  }
</style>
</head>
<body>
<h2>Query Interface</h2>
<div>
  <button onclick="queryData('query1')">Query 1</button>
  <button onclick="queryData('query2')">Query 2</button>
  <button onclick="queryData('query3')">Query 3</button>
  <button onclick="queryData('query4')">Query 4</button>
</div>
<textarea id="result" rows="10" cols="50" readonly></textarea>
</body>
<script type="text/javascript">
    function queryData(query) {
        var url = 'http://172.21.247.112:8081/query?q=' + query;

        fetch(url)
            .then(response => response.text())
            .then(data => {
                document.getElementById("result").value = data;
            })
            .catch(error => {
                console.error('请求出错:', error);
				alert("error!");
            });
    }
</script>
</html>

登录界面保存文件名称为Login.html,请求界面保存文件名称为Require.html,两个文件和服务器程序放在同一目录下,测试流程大致如下:

测试结果如下:

3、总结

本文是 Linux网络服务器应用实战的服务器和客户端初步调试篇,实现了客户端和服务器的初步通信,通过Web客户端可以和服务器进行简单通信,实现数据的简单收发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/532866.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Docker部署开源项目FreeGPT35来免费调用ChatGPT3.5 API

Vercel部署FreeGPT35有严重限制&#xff0c;玩玩就好&#xff0c;真用还是得docker。 限制原因: Vercel的流式响应并不是一开始写流&#xff0c;客户端就能立刻收到响应流&#xff0c;而是先写到一个缓冲区&#xff0c;当流关闭才一股脑的流式响应回来(不是实时流) 因此导致: …

创意解决方案:如何将作品集视频集中于一个二维码或链接中?

引言&#xff1a;随着面试环节的进一步数字化&#xff0c;展示自己的作品集成为了求职过程中的重要一环。但除了使用传统的方式&#xff0c;如百度网盘或直接发送多个视频链接&#xff0c;有没有更便捷的方法将作品集的多个视频放在一个链接中呢? 本文将介绍一种创意解决方案…

系统监控-硬件资源-内存篇01-整体思路-性能指标-性能工具概览-Buffer/Cache

参考来源&#xff1a;性能优化实战 内存的功能主要用来存储系统和应用程序的指令、数据、缓存等。 内存性能分析整体思路 当你看到系统的剩余内存很低时&#xff0c;是不是就说明&#xff0c;进程一定不能申请分配新内存了呢&#xff1f;当然不是&#xff0c;因为进程可以使…

5.2 配置静态路由

5.2.1 实验1&#xff1a;配置IPv4静态路由 1、实验目的 通过本实验可以掌握&#xff1a; 配置带下一跳地址的IPv4静态路由的方法。配置带送出接口的IPv4静态路由的方法。配置总结IPv4静态路由的方法。配置浮动IPv4静态路由的方法。代理 ARP的作用。路由表的含义。扩展ping命…

数据仓库与数据挖掘(第三版)陈文伟思维导图1-5章作业

第一章 概述 8.基于数据仓库的决策支持系统与传统决策支持系统有哪些区别&#xff1f; 决策支持系统经历了4个阶段。 1.基本决策支持系统 是在运筹学单模型辅助决策的基础上发展起来的&#xff0c;以模型库系统为核心&#xff0c;以多模型和数据库的组合形成方案辅助决策。 它…

hal库实现串口通信——阻塞式 API

1STM32CobeMX设置 设置时钟源 rcc设置为外部时钟High Speed Clock (HSE)//设置为如图 再将其设置为72MHz 设置串口引脚为异步通信 设置波特率等 设置波特率范围提示点击波特率再点击图中的 我的设置 再打开中断 即可生成代码//省略项目设置 2代码设置 函数 HAL_UART_Trans…

探索数据中台的力量:企业数据资产管理的未来_光点科技

随着数字化时代的到来&#xff0c;"数据中台"这一概念逐渐为人所知&#xff0c;并迅速成为推动企业数据驱动转型的重要基石。数据中台不仅是集数据接入、管理、分析于一身的综合平台&#xff0c;更是企业实现数据资源集中管理、分析决策和业务创新的核心支撑。 一、数…

VRRP虚拟路由实验(华为)

思科设备参考&#xff1a;VRRP虚拟路由实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;是一种网络协议&#xff0c;用于实现路由器冗余&#xff0c;提高网络可靠性和容错能力。VRRP允许多台路由器…

如何提高旋转花键运行稳定性?

现代化精密仪器设备中&#xff0c;精密仪器的稳定工作性能对于生产效率和产品质量至关重要&#xff0c;运行效率和精度是常见问题。旋转花键作为机械传动系统中的重要组成部分&#xff0c;其稳定性也是直接影响到机械装配的质量和使用寿命&#xff0c;那么我们应该如何提升旋转…

uniapp开发小程序,通过缓存的方式,判断页面只弹出一次弹窗通知

一、需求 在使用uniapp开发小程序时&#xff0c;在【个人中心页面】-点击【我的推广】按钮进入详情页面时&#xff0c;要求出现【会员协议通知】的弹窗&#xff0c;并且有【确认和取消】两个按钮&#xff0c; 如果点了【取消】按钮&#xff0c;直接退出该页面&#xff0c;并且…

如何在极狐GitLab 使用Docker 仓库功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何在[极狐GitLab…

Github Benefits 学生认证/学生包 新版申请指南

本教程适用于2024年之后的Github学生认证申请&#xff0c;因为现在的认证流程改变了很多&#xff0c;所以重新进行了总结这方面的指南。 目录 验证教育邮箱修改个人资料制作认证文件图片转换Base64提交验证 验证教育邮箱 进入Email settings&#xff0c;找到Add email address…

官网下载IDE插件并导入IDE

官网下载IDEA插件并导入IDEA 1. 下载插件2. 导入插件 1. 下载插件 地址&#xff1a;https://plugins.jetbrains.com/plugin/21068-codearts-snap/versions 说明&#xff1a;本次演示以IDEA软件为例 操作&#xff1a; 等待下载完成 2. 导入插件 点击File->setting->Pl…

HTML和markdown

总体情况 <p>在html的用处 在vscode中使用markdown [Markdown] 使用vscode开始Markdown写作之旅 - 知乎

STL库 —— list 的编写

目录 一、成员变量 ​编辑 二、push_back 函数 三、迭代器 iterator 3.1 iterator 结构体 3.2 begin() 与 end() 函数 3.3 iterator 运算符重载 3.4 -> 的重载 3.5 const_iterator 四、测试代码 五、修饰符成员 5.1 insert 函数 5.2 erase 函数 5.3 push 函数…

【Ubuntu】update-alternatives 命令详解

1、查看所有候选项 ​​​​​​​sudo update-alternatives --list java 2、​​​​​​​更换候选项 sudo update-alternatives --config java 3、自动选择优先级最高的作为默认项 sudo update-alternatives --auto java 4、删除候选项 sudo update-alternatives --rem…

Netty 入门应用之Http服务WebSocket

Netty实现Http服务 主要的变化是在初始化器中引入了新的编解码器 一些创建的类作用和Netty HelloWorld的小demo一样我这里就不再次重复了 1、Http服务端代码 public class HttpServer {public static void main(String[] args) {// 创建Reactor// 用来管理channel 监听事件 …

FME学习之旅---day22

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 教程&#xff1a;栅格入门 FME 支持读取和写入 70 多种栅格格式。本教程将介绍几个基本示例&#xff0c;展示如何使用 FME 读取、转换和写入栅格数据。 FME 数据检查器不应用任何对比度增强。因…

实战项目——智慧社区(一)

1、项目介绍 系统功能 登录、修改密码、登出 &#xff08;1&#xff09;首页 &#xff08;1.1&#xff09;数据统计&#xff1a;小区人员统计对比图&#xff0c;占比图 &#xff08;2&#xff09;物业管理 &#xff08;2.1&#xff09;小区管理&#xff1a;小区数据的增删改…

centos 7.9 nginx本地化安装,把镜像改成阿里云

1.把centos7.9系统切换到阿里云的镜像源 1.1.先备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2.下载新的CentOS-Base.repo配置文件 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo特别…