《Linux 網絡架構:基于 TCP 協議的多人聊天系統搭建詳解》

一、系統概述

????????本系統是一個基于 TCP 協議的多人聊天系統,由一個服務器和多個客戶端組成。客戶端可以連接到服務器,向服務器發送消息,服務器接收到消息后將其轉發給其他客戶端,實現多人之間的實時聊天。系統使用 C 語言編寫,利用了 Unix 系統的網絡編程接口和多線程、I/O 多路復用等技術。

二、文件結構

  • server.c:服務器端程序,負責監聽客戶端連接、接收客戶端消息并將消息轉發給其他客戶端。
  • client1.c:客戶端程序,使用?poll?函數實現 I/O 多路復用,同時處理用戶輸入和服務器消息。
  • client2.c:客戶端程序,使用多線程技術,一個線程負責接收服務器消息,另一個線程負責處理用戶輸入。

三、代碼詳細分析

1. 數據包結構體

????????在三個文件中都定義了相同的數據包結構體?Packet,用于在客戶端和服務器之間傳輸數據。

typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;
  • type:數據包類型,0 表示消息,1 表示斷開連接。
  • data:數據包攜帶的數據,最大長度為?BUFFER_SIZE

2. 服務器端程序(server.c)

2.1 主要變量

  • server_fd:服務器套接字文件描述符。
  • client_fd:客戶端套接字文件描述符。
  • max_fd:記錄最大的文件描述符,用于?select?函數。
  • activity:記錄?select?函數返回的活動文件描述符數量。
  • valread:記錄從客戶端讀取的數據長度。
  • server_addr:存儲服務器的地址信息。
  • client_addr:存儲客戶端的地址信息。
  • client_sockets:數組用于存儲所有客戶端的套接字文件描述符。
  • readfds:文件描述符集合,用于?select?函數監聽可讀事件。

2.2 主要步驟

  1. 創建套接字:使用?socket?函數創建一個 TCP 套接字。
  2. 綁定地址:使用?bind?函數將套接字綁定到指定的地址和端口。
  3. 監聽連接:使用?listen?函數開始監聽客戶端連接。
  4. 循環處理:使用?select?函數監聽服務器套接字和客戶端套接字的可讀事件。
    • 若服務器套接字有可讀事件,說明有新的客戶端連接請求,使用?accept?函數接受連接。
    • 若客戶端套接字有可讀事件,從客戶端讀取數據包,根據數據包類型進行相應處理。
      • 若數據包類型為消息,將消息轉發給其他客戶端。
      • 若數據包類型為斷開連接或讀取到的數據長度為 0,說明客戶端斷開連接,關閉客戶端套接字。

3. 客戶端程序(client1.c)

3.1 主要變量

  • client_fd:客戶端套接字文件描述符。
  • server_addr:存儲服務器的地址信息。
  • packet:用于存儲要發送或接收的數據包。
  • fds:數組用于存儲要監聽的文件描述符及其事件。

3.2 主要步驟

  1. 創建套接字:使用?socket?函數創建一個 TCP 套接字。
  2. 連接服務器:使用?connect?函數連接到服務器。
  3. 初始化?poll?結構體:監聽標準輸入和客戶端套接字的可讀事件。
  4. 循環處理:使用?poll?函數監聽文件描述符集合中的可讀事件。
    • 若標準輸入有可讀事件,從標準輸入讀取數據,設置數據包類型為消息,發送給服務器。
    • 若客戶端套接字有可讀事件,從服務器讀取數據包,根據數據包類型進行相應處理。
      • 若數據包類型為消息,輸出接收到的消息。
      • 若數據包類型為斷開連接或讀取數據失敗,說明服務器斷開連接,關閉客戶端套接字,跳出循環。

4. 客戶端程序(client2.c)

4.1 主要變量

  • client_fd:客戶端套接字文件描述符。
  • server_addr:存儲服務器的地址信息。
  • packet:用于存儲要發送或接收的數據包。
  • thread_id:存儲線程的標識符。

4.2 主要步驟

  1. 創建套接字:使用?socket?函數創建一個 TCP 套接字。
  2. 連接服務器:使用?connect?函數連接到服務器。
  3. 創建線程:創建一個線程來接收服務器發送的消息。
  4. 循環處理:在主線程中,從標準輸入讀取數據,設置數據包類型為消息,發送給服務器。
  5. 線程函數:在子線程中,持續接收服務器消息,根據數據包類型進行相應處理。
    • 若數據包類型為消息,輸出接收到的消息。
    • 若數據包類型為斷開連接或讀取數據失敗,說明服務器斷開連接,關閉客戶端套接字,退出程序。

四、編譯和運行

4.1 編譯

gcc server.c -o server
gcc client1.c -o client1
gcc client2.c -o client2 -lpthread

4.2 運行

  1. 啟動服務器:
./server
  1. 啟動客戶端:
./client1
./client2

4.3運行結果展示

五、源碼

5.1服務器端程序(server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>// 定義服務器監聽的端口號
#define PORT 8080
// 定義數據緩沖區的大小
#define BUFFER_SIZE 1024
// 定義服務器允許的最大客戶端連接數
#define MAX_CLIENTS 10/*** 定義數據包結構體,用于在服務器和客戶端之間傳輸數據* type 數據包類型,0 表示消息,1 表示斷開連接* data 數據包攜帶的數據*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;/*** 主函數,服務器程序的入口點* @return 程序的退出狀態碼,0 表示正常退出*/
int main() {// server_fd 為服務器套接字文件描述符,client_fd 為客戶端套接字文件描述符// max_fd 記錄最大的文件描述符,用于 select 函數// activity 記錄 select 函數返回的活動文件描述符數量// valread 記錄從客戶端讀取的數據長度int server_fd, client_fd, max_fd, activity, valread;// server_addr 存儲服務器的地址信息,client_addr 存儲客戶端的地址信息struct sockaddr_in server_addr, client_addr;// client_len 存儲客戶端地址結構體的長度socklen_t client_len = sizeof(client_addr);// packet 用于存儲從客戶端接收的數據包Packet packet;// client_sockets 數組用于存儲所有客戶端的套接字文件描述符int client_sockets[MAX_CLIENTS] = {0};// readfds 是一個文件描述符集合,用于 select 函數監聽可讀事件fd_set readfds;// 創建服務器套接字,使用 IPv4 地址族和 TCP 協議if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {// 若套接字創建失敗,輸出錯誤信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 設置服務器地址信息server_addr.sin_family = AF_INET;// 監聽所有可用的網絡接口server_addr.sin_addr.s_addr = INADDR_ANY;// 將端口號從主機字節序轉換為網絡字節序server_addr.sin_port = htons(PORT);// 將服務器套接字綁定到指定的地址和端口if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若綁定失敗,輸出錯誤信息,關閉套接字并退出程序perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 開始監聽客戶端連接,允許的最大連接請求隊列長度為 3if (listen(server_fd, 3) < 0) {// 若監聽失敗,輸出錯誤信息,關閉套接字并退出程序perror("listen");close(server_fd);exit(EXIT_FAILURE);}// 輸出服務器啟動信息,顯示監聽的端口號printf("Server started on port %d\n", PORT);// 進入無限循環,持續處理客戶端連接和數據while (1) {// 清空文件描述符集合FD_ZERO(&readfds);// 將服務器套接字添加到文件描述符集合中,監聽其可讀事件FD_SET(server_fd, &readfds);// 初始化最大文件描述符為服務器套接字文件描述符max_fd = server_fd;// 遍歷客戶端套接字數組for (int i = 0; i < MAX_CLIENTS; i++) {// 獲取當前客戶端的套接字文件描述符int sd = client_sockets[i];if (sd > 0) {// 若該客戶端套接字有效,將其添加到文件描述符集合中FD_SET(sd, &readfds);}if (sd > max_fd) {// 更新最大文件描述符max_fd = sd;}}// 調用 select 函數監聽文件描述符集合中的可讀事件,無超時時間activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);if ((activity < 0) && (errno != EINTR)) {// 若 select 函數調用失敗且不是被信號中斷,輸出錯誤信息perror("select error");}if (FD_ISSET(server_fd, &readfds)) {// 若服務器套接字有可讀事件,說明有新的客戶端連接請求if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) {// 若接受連接失敗,輸出錯誤信息并繼續循環perror("accept");continue;}// 輸出新客戶端連接的信息,包括套接字文件描述符、IP 地址和端口號printf("New connection, socket fd is %d, ip is : %s, port : %d\n",client_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 遍歷客戶端套接字數組,找到一個空閑位置存儲新客戶端的套接字文件描述符for (int i = 0; i < MAX_CLIENTS; i++) {if (client_sockets[i] == 0) {client_sockets[i] = client_fd;break;}}}// 遍歷客戶端套接字數組,檢查每個客戶端是否有可讀事件for (int i = 0; i < MAX_CLIENTS; i++) {int sd = client_sockets[i];if (FD_ISSET(sd, &readfds)) {// 從客戶端讀取數據包valread = read(sd, &packet, sizeof(Packet));if (valread == 0) {// 若讀取到的數據長度為 0,說明客戶端斷開連接getpeername(sd, (struct sockaddr*)&client_addr, &client_len);// 輸出客戶端斷開連接的信息,包括 IP 地址和端口號printf("Host disconnected, ip %s, port %d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 關閉客戶端套接字close(sd);// 將該位置的客戶端套接字文件描述符置為 0,表示空閑client_sockets[i] = 0;} else {if (packet.type == 0) {// 若數據包類型為消息,輸出接收到的消息printf("Received from client %d: %s", sd, packet.data);// 將消息轉發給其他客戶端for (int j = 0; j < MAX_CLIENTS; j++) {if (client_sockets[j] != sd && client_sockets[j] != 0) {// 發送數據包給其他客戶端send(client_sockets[j], &packet, sizeof(Packet), 0);}}} else if (packet.type == 1) {// 若數據包類型為斷開連接,輸出客戶端斷開連接的信息printf("Client %d disconnected\n", sd);// 關閉客戶端套接字close(sd);// 將該位置的客戶端套接字文件描述符置為 0,表示空閑client_sockets[i] = 0;}}}}}return 0;
}

5.2客戶端程序(client1.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>// 定義服務器監聽的端口號
#define PORT 8080
// 定義數據緩沖區的大小
#define BUFFER_SIZE 1024/*** 定義數據包結構體,用于在客戶端和服務器之間傳輸數據* type 數據包類型,0 表示消息,1 表示斷開連接* data 數據包攜帶的數據*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;/*** 主函數,客戶端程序的入口點* @return 程序的退出狀態碼,0 表示正常退出*/
int main() {// client_fd 為客戶端套接字文件描述符int client_fd;// server_addr 存儲服務器的地址信息struct sockaddr_in server_addr;// packet 用于存儲要發送或接收的數據包Packet packet;// fds 數組用于存儲要監聽的文件描述符及其事件struct pollfd fds[2];// 創建客戶端套接字,使用 IPv4 地址族和 TCP 協議if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 若套接字創建失敗,輸出錯誤信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 設置服務器地址信息server_addr.sin_family = AF_INET;// 將端口號從主機字節序轉換為網絡字節序server_addr.sin_port = htons(PORT);// 將服務器的 IP 地址轉換為網絡字節序server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// 連接到服務器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若連接失敗,輸出錯誤信息,關閉套接字并退出程序perror("connect");close(client_fd);exit(EXIT_FAILURE);}// 輸出連接成功的信息printf("Connected to server\n");// 初始化 poll 結構體// 監聽標準輸入的可讀事件fds[0].fd = STDIN_FILENO;fds[0].events = POLLIN;// 監聽客戶端套接字的可讀事件fds[1].fd = client_fd;fds[1].events = POLLIN;// 進入無限循環,持續處理輸入和服務器消息while (1) {// 調用 poll 函數監聽文件描述符集合中的可讀事件,無超時時間int activity = poll(fds, 2, -1);if ((activity < 0) && (errno != EINTR)) {// 若 poll 函數調用失敗且不是被信號中斷,輸出錯誤信息perror("poll error");}if (fds[0].revents & POLLIN) {// 若標準輸入有可讀事件// 清空數據包的數據部分memset(packet.data, 0, BUFFER_SIZE);if (fgets(packet.data, BUFFER_SIZE, stdin) != NULL) {// 若成功從標準輸入讀取數據// 設置數據包類型為消息packet.type = 0;// 發送數據包給服務器send(client_fd, &packet, sizeof(Packet), 0);}}if (fds[1].revents & POLLIN) {// 若客戶端套接字有可讀事件// 清空數據包memset(&packet, 0, sizeof(Packet));if (read(client_fd, &packet, sizeof(Packet)) > 0) {// 若成功從服務器讀取數據if (packet.type == 0) {// 若數據包類型為消息,輸出接收到的消息printf("Received from server: %s", packet.data);} else if (packet.type == 1) {// 若數據包類型為斷開連接,輸出服務器斷開連接的信息printf("Server disconnected\n");// 關閉客戶端套接字close(client_fd);// 跳出循環break;}} else {// 若讀取數據失敗,說明服務器斷開連接printf("Server disconnected\n");// 關閉客戶端套接字close(client_fd);// 跳出循環break;}}}// 關閉客戶端套接字close(client_fd);return 0;
}

5.3客戶端程序(client2.c)

// 包含標準輸入輸出庫,用于使用 printf、perror 等函數進行輸入輸出操作
#include <stdio.h>
// 包含標準庫,提供 exit 等函數用于程序退出等操作
#include <stdlib.h>
// 包含字符串處理庫,提供 memset、strlen 等字符串操作函數
#include <string.h>
// 包含 Unix 標準庫,提供 close、read、write 等系統調用函數
#include <unistd.h>
// 包含網絡地址轉換庫,提供 inet_ntoa、htons 等網絡地址轉換函數
#include <arpa/inet.h>
// 包含線程相關的頭文件,用于創建和管理線程
#include <pthread.h>// 定義服務器監聽的端口號
#define PORT 8080
// 定義數據緩沖區的大小
#define BUFFER_SIZE 1024/*** 定義數據包結構體,用于在客戶端和服務器之間傳輸數據* type 數據包類型,0 表示消息,1 表示斷開連接* data 數據包攜帶的數據*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;// 全局變量,存儲客戶端套接字文件描述符
int client_fd;/*** 線程函數,用于接收服務器發送的消息* arg 線程函數的參數,此處未使用* @return 線程返回值,此處為 NULL*/
void *receive_messages(void *arg) {// 定義數據包變量,用于存儲從服務器接收的數據包Packet packet;// 進入無限循環,持續接收服務器消息while (1) {// 清空數據包memset(&packet, 0, sizeof(Packet));if (read(client_fd, &packet, sizeof(Packet)) > 0) {// 若成功從服務器讀取數據if (packet.type == 0) {// 若數據包類型為消息,輸出接收到的消息printf("Received from server: %s", packet.data);} else if (packet.type == 1) {// 若數據包類型為斷開連接,輸出服務器斷開連接的信息printf("Server disconnected\n");// 關閉客戶端套接字close(client_fd);// 退出程序,返回失敗狀態exit(EXIT_FAILURE);}} else {// 若讀取數據失敗,說明服務器斷開連接printf("Server disconnected\n");// 關閉客戶端套接字close(client_fd);// 退出程序,返回失敗狀態exit(EXIT_FAILURE);}}return NULL;
}/*** 主函數,客戶端程序的入口點* @return 程序的退出狀態碼,0 表示正常退出*/
int main() {// server_addr 存儲服務器的地址信息struct sockaddr_in server_addr;// packet 用于存儲要發送的數據包Packet packet;// thread_id 存儲線程的標識符pthread_t thread_id;// 創建客戶端套接字,使用 IPv4 地址族和 TCP 協議if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 若套接字創建失敗,輸出錯誤信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 設置服務器地址信息server_addr.sin_family = AF_INET;// 將端口號從主機字節序轉換為網絡字節序server_addr.sin_port = htons(PORT);// 將服務器的 IP 地址轉換為網絡字節序server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// 連接到服務器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若連接失敗,輸出錯誤信息,關閉套接字并退出程序perror("connect");close(client_fd);exit(EXIT_FAILURE);}// 輸出連接成功的信息printf("Connected to server\n");// 創建線程來接收消息if (pthread_create(&thread_id, NULL, receive_messages, NULL) != 0) {// 若線程創建失敗,輸出錯誤信息,關閉套接字并退出程序perror("pthread_create");close(client_fd);exit(EXIT_FAILURE);}// 進入無限循環,持續從標準輸入讀取數據并發送給服務器while (1) {// 清空數據包的數據部分memset(packet.data, 0, BUFFER_SIZE);if (fgets(packet.data, BUFFER_SIZE, stdin) != NULL) {// 若成功從標準輸入讀取數據// 設置數據包類型為消息packet.type = 0;// 發送數據包給服務器send(client_fd, &packet, sizeof(Packet), 0);}}// 關閉客戶端套接字close(client_fd);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73031.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73031.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73031.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaIO流的使用和修飾器模式(直擊心靈版)

系列文章目錄 JavaIO流的使用和修飾器模式 文章目錄 系列文章目錄前言一、字節流&#xff1a; 1.FileInputStream(讀取文件)2.FileOutputStream(寫入文件) 二、字符流&#xff1a; 1..基礎字符流:2.處理流&#xff1a;3.對象處理流&#xff1a;4.轉換流&#xff1a; 三、修飾器…

【設計模式】SOLID 設計原則概述

SOLID 是面向對象設計中的五大原則&#xff0c;不管什么面向對象的語言&#xff0c; 這個準則都很重要&#xff0c;如果你沒聽說過&#xff0c;趕緊先學一下。它可以提高代碼的可維護性、可擴展性和可讀性&#xff0c;使代碼更加健壯、易于測試和擴展。SOLID 代表以下五個設計原…

可發1區的超級創新思路:基于注意力機制的DSD-CNN時間序列預測模型(功率預測、交通流量預測、故障檢測)

首先聲明,該模型為原創!原創!原創! 一、應用場景 該模型主要用于時間序列數據預測問題,包含功率預測、電池壽命預測、電機故障檢測等等 二、模型整體介紹(本文以光伏功率預測為例) DSD-CNN(Depthwise-Spacewise Separable CNN)結合通道注意力機制,通過以下創新提升…

wsl2配置xv6全解(包括22.04Jammy)

文章目錄 獲取xv6源代碼Ubuntu20.04 Version安裝指令成功測試參考MIT2021年官方文檔 24.04 Version安裝指令成功測試參考MIT2024年官方文檔 Ubuntu 22.04沒有官方文檔&#xff1f; 配置大體流程1. 卸載原本qemu&#xff08;如果之前安裝了&#xff09;2. clone qemu官方源代碼&…

招聘面試季--一文頓悟,Java中字節流和字符流的區別及使用場景上的差異

?一、核心區別? ?特性??字節流??字符流??數據單位?以字節&#xff08;8-bit&#xff09;為單位處理數據&#xff08;如0xA1&#xff09;以字符&#xff08;16-bit Unicode&#xff09;為單位處理數據&#xff08;如A, 你&#xff09;?基類?InputStream / OutputSt…

車載以太網網絡測試-16【傳輸層-UDP】

目錄 1 摘要2 車載以太網傳輸層概述3 車載以太網UDP協議3.1 車載以太網UDP協議的作用3.2 UDP報文幀結構3.3 UDP協議的通信過程3.3.1 通信過程3.3.2 實例示例3.3.3 代碼示例 4 總結 1 摘要 車載以太網的第五層是傳輸層&#xff0c;它在車載網絡架構中扮演著至關重要的角色。主要…

深度強化學習中的深度神經網絡優化策略:挑戰與解決方案

I. 引言 深度強化學習&#xff08;Deep Reinforcement Learning&#xff0c;DRL&#xff09;結合了強化學習&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;和深度學習&#xff08;Deep Learning&#xff09;的優點&#xff0c;使得智能體能夠在復雜的環境中學…

無人機點對點技術要點分析!

一、技術架構 1. 網絡拓撲 Ad-hoc網絡&#xff1a;無人機動態組建自組織網絡&#xff0c;節點自主協商路由&#xff0c;無需依賴地面基站。 混合架構&#xff1a;部分場景結合中心節點&#xff08;如指揮站&#xff09;與P2P網絡&#xff0c;兼顧集中調度與分布式協同。 2.…

MQ,RabbitMQ,MQ的好處,RabbitMQ的原理和核心組件,工作模式

1.MQ MQ全稱 Message Queue&#xff08;消息隊列&#xff09;&#xff0c;是在消息的傳輸過程中 保存消息的容器。它是應用程序和應用程序之間的通信方法 1.1 為什么使用MQ 在項目中&#xff0c;可將一些無需即時返回且耗時的操作提取出來&#xff0c;進行異步處理&#xff0…

django怎么配置404和500

在 Django 中&#xff0c;配置 404 和 500 錯誤頁面需要以下步驟&#xff1a; 1. 創建自定義錯誤頁面模板 首先&#xff0c;創建兩個模板文件&#xff0c;分別用于 404 和 500 錯誤頁面。假設你的模板目錄是 templates/。 404 頁面模板 創建文件 templates/404.html&#x…

各類神經網絡學習:(四)RNN 循環神經網絡(下集),pytorch 版的 RNN 代碼編寫

上一篇下一篇RNN&#xff08;中集&#xff09;待編寫 代碼詳解 pytorch 官網主要有兩個可調用的模塊&#xff0c;分別是 nn.RNNCell 和 nn.RNN &#xff0c;下面會進行詳細講解。 RNN 的同步多對多、多對一、一對多等等結構都是由這兩個模塊實現的&#xff0c;只需要將對輸入…

深度學習篇---深度學習中的范數

文章目錄 前言一、向量范數1.L0范數1.1定義1.2計算式1.3特點1.4應用場景1.4.1特征選擇1.4.2壓縮感知 2.L1范數&#xff08;曼哈頓范數&#xff09;2.1定義2.2計算式2.3特點2.4應用場景2.4.1L1正則化2.4.2魯棒回歸 3.L2范數&#xff08;歐幾里得范數&#xff09;3.1定義3.2特點3…

星越L_燈光操作使用講解

目錄 1.開啟前照燈 2左右轉向燈、遠近燈 3.auto自動燈光 4.自適應遠近燈光 5.后霧燈 6.調節大燈高度 1.開啟前照燈 2左右轉向燈、遠近燈 3.auto自動燈光 系統根據光線自動開啟燈光

Stable Diffusion lora訓練(一)

一、不同維度的LoRA訓練步數建議 2D風格訓練 數據規模&#xff1a;建議20-50張高質量圖片&#xff08;分辨率≥10241024&#xff09;&#xff0c;覆蓋多角度、多表情的平面風格。步數范圍&#xff1a;總步數控制在1000-2000步&#xff0c;公式為 總步數 Repeat Image Epoch …

AI 生成 PPT 網站介紹與優缺點分析

隨著人工智能技術不斷發展&#xff0c;利用 AI 自動生成 PPT 已成為提高演示文稿制作效率的熱門方式。本文將介紹幾款主流的 AI PPT 工具&#xff0c;重點列出免費使用機會較多的網站&#xff0c;并對各平臺的優缺點進行詳細分析&#xff0c;幫助用戶根據自身需求選擇合適的工具…

使用Systemd管理ES服務進程

Centos中的Systemd介紹 CentOS 中的 Systemd 詳細介紹 Systemd 是 Linux 系統的初始化系統和服務管理器&#xff0c;自 CentOS 7 起取代了傳統的 SysVinit&#xff0c;成為默認的初始化工具。它負責系統啟動、服務管理、日志記錄等核心功能&#xff0c;顯著提升了系統的啟動速…

【一維前綴和與二維前綴和(簡單版dp)】

1.前綴和模板 一維前綴和模板 1.暴力解法 要求哪段區間&#xff0c;我就直接遍歷那段區間求和。 時間復雜度O(n*q) 2.前綴和 ------ 快速求出數組中某一個連續區間的和。 1&#xff09;預處理一個前綴和數組 這個前綴和數組設定為dp&#xff0c;dp[i]表示&#xff1a;表示…

在Windows和Linux系統上的Docker環境中使用的鏡像是否相同

在Windows和Linux系統上的Docker環境中使用的鏡像是否相同&#xff0c;取決于具體的運行模式和目標平臺&#xff1a; 1. Linux容器模式&#xff08;默認/常見場景&#xff09; Windows系統&#xff1a; 當Windows上的Docker以Linux容器模式運行時&#xff08;默認方式&#xf…

植物來源藥用天然產物的合成生物學研究進展-文獻精讀121

植物來源藥用天然產物的合成生物學研究進展 摘要 大多數藥用天然產物在植物中含量低微&#xff0c;提取分離困難&#xff1b;而且這些化合物一般結構復雜&#xff0c;化學合成難度大&#xff0c;還容易造成環境污染。基于合成生物學技術獲得藥用天然產物具有綠色環保和可持續發…

JavaScript |(五)DOM簡介 | 尚硅谷JavaScript基礎實戰

學習來源&#xff1a;尚硅谷JavaScript基礎&實戰丨JS入門到精通全套完整版 筆記來源&#xff1a;在這位大佬的基礎上添加了一些東西&#xff0c;歡迎大家支持原創&#xff0c;大佬太棒了&#xff1a;JavaScript |&#xff08;五&#xff09;DOM簡介 | 尚硅谷JavaScript基礎…