TCP并发服务器
端口号快速复用函数
通过
getsockopt和setsockopt函数,管理套接字的端口号复用设置。具体操作如下:
getsockopt函数
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
功能:获取套接字的某些选项的属性。
参数:
sockfd: 套接字描述符。level: 获取的层级(例如SOL_SOCKET)。optname: 要获取的操作名称(如SO_REUSEADDR)。optval: 获取的值(0表示禁用,非0表示启用)。optlen: 参数4的大小。
返回值: 成功返回0,失败返回-1。
setsockopt函数
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
功能:设置套接字的某些选项的属性。
参数:
sockfd: 套接字描述符。level: 设置的层级(例如SOL_SOCKET)。optname: 要设置的操作名称(如SO_REUSEADDR)。optval: 设置的值(0表示禁用,非0表示启用)。optlen: 参数4的大小。
返回值: 成功返回0,失败返回-1。
示例代码:端口号快速复用
// 获取当前端口号是否能快速复用 int n; int len = sizeof(n); getsockopt(oldfd, SOL_SOCKET, SO_REUSEADDR, &n, &len); if (n == 0) {printf("端口号快速复用未启动\n"); } else {printf("端口号快速复用已经启动\n"); }// 设置当前套接字端口号快速复用 n = 999; setsockopt(oldfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

1. 循环服务器模型
描述
循环服务器模型是一次只处理一个客户端请求的传统方式,处理完一个客户端请求后,才会继续处理下一个客户端请求。由于其效率较低,每次只能执行单个任务,因此在高并发场景下表现不佳。
在循环服务器模型中,服务器端采用了一个循环来不断接收客户端请求。每当一个客户端连接成功后,服务器创建一个新的套接字用于通信,并处理客户端的请求。每处理完一个客户端请求后,服务器继续等待下一个客户端的连接。
主要步骤:
- 创建套接字:使用
socket()函数创建套接字。- 绑定:将套接字与服务器的 IP 地址和端口号绑定。
- 监听:开始监听客户端连接请求。
- 循环接收客户端请求:
- 每次
accept()成功后,创建一个新的套接字用于通信。- 循环收发信息,直到客户端断开连接。
- 关闭连接:每次客户端断开连接后,关闭相应的套接字。
关闭监听套接字
循环服务器代码实现
代码示例
#include <myhead.h>
#define IP "192.168.60.45"
#define PORT 6666
#define BACKLOG 20int main(int argc, const char *argv[]) {// 1. 创建套接字int oldfd = socket(AF_INET, SOCK_STREAM, 0); // IPV4, TCP协议if (oldfd == -1) {perror("socket");return -1;}// 2. 获取端口号属性,查看是否启用端口号快速复用int n;int len = sizeof(n);if (getsockopt(oldfd, SOL_SOCKET, SO_REUSEADDR, &n, &len) == -1) {perror("getsockopt");return -1;}if (n) {printf("端口号快速复用已经启动\n");} else {printf("端口号快速复用未启动\n");}// 设置端口号快速复用n = 999;if (setsockopt(oldfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {perror("setsockopt");return -1;}printf("端口号快速复用成功\n");// 3. 绑定IP和端口号struct sockaddr_in server = {.sin_family = AF_INET, // IPV4.sin_port = htons(PORT), // 端口号转换为网络字节序.sin_addr.s_addr = inet_addr(IP), // IP地址};if (bind(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1) {perror("bind");return -1;}// 4. 监听if (listen(oldfd, BACKLOG) == -1) {perror("listen");return -1;}// 5. 接受客户端连接请求并创建新的描述符用于通信struct sockaddr_in client;socklen_t client_len = sizeof(client);int newfd;while (1) {newfd = accept(oldfd, (struct sockaddr *)&client, &client_len);if (newfd == -1) {perror("accept");return -1;}printf("%s发来连接请求\n", inet_ntoa(client.sin_addr));// 6. 循环收发信息char buff[1024];while (1) {memset(buff, 0, sizeof(buff));int len = recv(newfd, buff, sizeof(buff), 0); // 接收客户端信息if (len == 0) { // 客户端断开连接printf("客户端下线\n");break;}printf("%s\n", buff);strcat(buff, "5201314"); // 添加回复信息send(newfd, buff, sizeof(buff), 0); // 发送信息}}// 关闭套接字close(oldfd);close(newfd);return 0;
}
代码流程:
- 创建套接字:
socket()函数创建一个 TCP 套接字。 - 设置端口号复用:通过
setsockopt()设置端口复用,允许在同一端口上建立多个连接。 - 绑定:使用
bind()将套接字与 IP 地址和端口绑定。 - 监听:调用
listen()使套接字进入监听状态,准备接受客户端连接。 - 接受连接:通过
accept()接受客户端连接,返回一个新的套接字描述符用于与客户端通信。 - 收发消息:在循环内通过
recv()接收客户端消息,然后用send()发送回应消息。直到客户端断开连接。 - 关闭套接字:处理完成后关闭套接字。
缺点:
- 同步阻塞:当前的设计是同步阻塞的,服务器在处理一个客户端请求时不能同时处理其他客户端的请求。因此,如果有多个客户端连接,必须等待前一个客户端的请求处理完成后才能继续。
- 客户端阻塞:新客户端要通信时,必须等到旧客户端退出。服务器只能在当前连接断开后接受新的客户端请求。
- 新客户端要通信时,必须等待旧客户端退出。
改进:
为了解决这个问题,可以采用 多线程 或 多进程 模型来并发处理客户端请求,从而避免客户端之间的阻塞。
2. 基于TCP的并发服务器
2.1 多进程并发服务器
多进程并发服务器的模型通常由一个父进程负责监听客户端的连接请求,每当接收到一个连接请求时,父进程创建一个新的子进程来处理与客户端的通信。父进程和子进程之间通过文件描述符传递数据。父进程负责监听和接收客户端连接,子进程则处理数据收发。
多进程服务器模型的关键问题:
1. 子进程在哪创建: 父进程在接收到客户端的连接请求时会创建一个子进程来处理该请求。
示例代码:
pid_t pid = fork(); if (pid > 0) {// 父进程:继续监听新的连接请求,关闭新文件描述符close(newfd); } else if (pid == 0) {// 子进程:关闭旧的监听套接字,并处理客户端通信close(server_fd);// 与客户端通信...close(newfd); // 处理完成后关闭与客户端的连接exit(0); // 子进程处理完请求后退出 } else {perror("fork");return -1; }
2. 子进程怎么回收:子进程处理完任务后会退出,因此需要确保父进程能够回收已经退出的子进程,防止僵尸进程的产生。
常见的方式:
- 阻塞回收:父进程使用
wait()或waitpid()函数在子进程退出时阻塞等待并回收它们。- 非阻塞回收:如果不想让父进程被
wait()阻塞,可以使用信号处理机制,通过捕捉SIGCHLD信号并在信号处理函数中回收子进程。示例代码(非阻塞回收):
// 信号处理函数,用于回收僵尸进程 void handle_sigchld(int sig) {while (waitpid(-1, NULL, WNOHANG) > 0); // 回收已退出的子进程 }// 在主程序中捕捉 SIGCHLD 信号 signal(SIGCHLD, handle_sigchld);
3. 文件描述符: 由于系统对打开的文件描述符有限制,因此需要确保在父子进程中正确管理文件描述符。
在操作系统中,文件描述符是有限的,每个进程可以打开的文件(包括套接字)的数量是有限的。如果服务器并发连接数较多,且每个连接都创建一个新的子进程,就可能遇到文件描述符耗尽的问题。可以通过以下方式解决或避免这一问题:
解决方案:
- 增加文件描述符限制:可以通过
ulimit -n命令临时增加系统的文件描述符限制,或者在/etc/security/limits.conf文件中永久增加限制。- 使用线程池或连接池:而不是为每个客户端创建一个新的子进程,可以通过线程池或连接池来管理客户端连接。多线程或线程池模型可以减少系统开销,因为线程的创建和销毁比进程更加轻量。
- 复用文件描述符:通过一些技术手段(例如
select()、poll()或epoll())来管理多个连接,不需要为每个客户端创建一个独立的进程或线程,从而避免耗尽文件描述符。
示例:修改文件描述符限制
- 临时修改文件描述符的限制:
ulimit -n 65535
- 永久修改文件描述符的限制: 在
/etc/security/limits.conf文件中添加:* soft nofile 65535 * hard nofile 65535
总结
- 子进程创建:父进程在接收到客户端连接请求后,使用
fork()创建子进程来处理该客户端的通信。- 子进程回收:父进程可以通过
wait()或waitpid()回收子进程,也可以通过信号处理函数来非阻塞回收已退出的子进程。- 文件描述符限制:多进程模型可能会受到系统文件描述符限制的影响,解决方法包括增加文件描述符限制、使用线程池或连接池等技术来减少文件描述符的占用。
多进程并发服务器执行模型:
定义信号处理函数,非阻塞回收僵尸进程。
绑定子进程退出时的信号。
- 创建套接字
- 绑定
- 监听
- 循环接收客户端连接
- 让父进程接收客户端请求并关闭新文件描述符,子进程关闭旧的描述符只负责数据收发。
多进程服务器代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <wait.h>#define IP "192.168.60.45" #define PORT 6666 #define BACKLOG 100// 信号处理函数,用于回收僵尸进程 void fun(int sss) {if (sss == SIGCHLD){while (waitpid(-1, NULL, 0) > 0); // 循环回收子进程} }int main(int argc, const char *argv[]) {// 1. 捕获子进程退出时的信号if (signal(SIGCHLD, fun) == SIG_ERR){perror("signal");return -1;}// 2. 创建TCP类型的套接字int oldfd = socket(AF_INET, SOCK_STREAM, 0);if (oldfd == -1){perror("socket");return -1;}// 3. 设置端口号快速复用int n = 1;if (setsockopt(oldfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1){perror("setsockopt");return -1;}printf("端口号快速复用成功\n");// 4. 绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP),};if (bind(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1){perror("bind");return -1;}// 5. 监听if (listen(oldfd, BACKLOG) == -1){perror("listen");return -1;}struct sockaddr_in client;socklen_t client_len = sizeof(client);char buff[1024];// 6. 主循环接收客户端连接while (1){int newfd = accept(oldfd, (struct sockaddr *)&client, &client_len); // 接收客户端连接printf("%s发来连接请求\n", inet_ntoa(client.sin_addr));pid_t pid = fork(); // 创建子进程if (pid > 0) // 父进程监听,并关闭新的描述符{close(newfd);}else if (pid == 0) // 子进程处理数据收发{close(oldfd); // 子进程关闭旧的描述符while (1){int len = recv(newfd, buff, sizeof(buff), 0);if (len == 0){printf("客户端退出\n");break;}printf("客户端%s发来消息:%s\n", inet_ntoa(client.sin_addr), buff);strcat(buff, inet_ntoa(client.sin_addr)); // 回去时加上客户端IPsend(newfd, buff, sizeof(buff), 0);}close(newfd);exit(0); // 子进程退出}else{perror("fork");return -1;}}return 0; }
客户端代码:
客户端通过连接到服务器来发送数据并接收服务器的响应。该程序创建一个TCP套接字,连接到指定的服务器IP和端口,并与服务器进行数据交互。
#include <myhead.h>#define IP "192.168.60.45" #define PORT 6666int main(int argc, const char *argv[]) {// 1. 创建套接字int oldfd = socket(AF_INET, SOCK_STREAM, 0);if (oldfd == -1){perror("socket");return -1;}// 2. 连接服务器struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP),};if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1){perror("connect");return -1;}// 3. 数据收发char buff[1024];while (1){fgets(buff, sizeof(buff), stdin); // 键盘输入字符串buff[strlen(buff) - 1] = '\0'; // 去除换行符// 发送数据到服务器send(oldfd, buff, sizeof(buff), 0);// 接收服务器数据int len = recv(oldfd, buff, sizeof(buff), 0);if (len == 0){printf("服务器意外退出\n");break;}printf("接收服务器消息:%s\n", buff);}// 关闭套接字close(oldfd);return 0; }
总结
- 多进程并发模型: 通过父进程监听和创建子进程来处理每个客户端的请求,使用
fork创建子进程,在子进程中负责数据收发,父进程关闭新文件描述符,子进程关闭旧文件描述符。通过信号处理函数回收子进程,避免僵尸进程。- TCP客户端: 连接到服务器后,通过套接字发送数据并接收响应,支持实时数据交互。
这种模型适用于客户端和服务器之间的实时通信,特别是在需要处理多个客户端请求的场景中。
2.2 多线程并发服务器
1. 多线程的优势
- 资源开销小:线程的资源开销比进程小,创建和销毁线程的速度比进程快。
- 响应速度快:如果客户端连接较多,可以通过线程池提前创建线程来分配给客户端,减少响应延迟。
- 线程销毁开销小:线程占用资源较少,销毁线程的资源开销也较小。
2. 服务器模型
- 主线程:负责监听客户端连接。
- 子线程:负责接收和发送数据,与客户端通信。
3. 服务器的工作流程
- 创建原始套接字:使用
socket()创建一个 TCP 类型的套接字。- 绑定 IP 地址和端口号:使用
bind()绑定服务器的 IP 地址和端口。- 监听客户端连接:使用
listen()开始监听客户端的连接请求。- 接收客户端连接:使用
accept()接收客户端的连接请求,并为每个客户端创建一个新线程。- 线程处理客户端请求:每个线程接收到客户端消息后进行处理并发送响应。线程执行完后退出。
4. 线程池
- 为了处理大量并发请求,线程池的机制可以预先创建一定数量的线程,接收到客户端请求时,从线程池中取出一个线程来处理该请求。
5. 代码实现
#include <myhead.h>// 定义服务器的 IP 地址、端口号和最大连接数 #define IP "192.168.60.45" #define PORT 9999 #define BACKLOG 10// 定义结构体,用于传递客户端信息和新文件描述符 typedef struct {struct sockaddr_in client; // 客户端信息int newfd; // 新的文件描述符 } ZYJ;// 线程体函数,处理与客户端的通信 void *fun(void *sss) {// 获取新文件描述符和客户端信息int newfd = ((ZYJ *)sss)->newfd;struct sockaddr_in client = ((ZYJ *)sss)->client;// 打印客户端的 IP 地址printf("%s发来信息\n", inet_ntoa(client.sin_addr));char buff[1024];while (1) {// 接收客户端发来的消息int len = recv(newfd, buff, sizeof(buff), 0);if (len == 0) { // 客户端退出printf("客户端退出\n");break;}// 打印收到的消息printf("收到消息:%s\n", buff);// 在消息末尾加上 "1973"strcat(buff, "1973");// 将处理后的消息发送回客户端send(newfd, buff, sizeof(buff), 0);}// 线程结束时退出pthread_exit(NULL); }int main(int argc, const char *argv[]) {// 1. 创建套接字int oldfd = socket(AF_INET, SOCK_STREAM, 0);if (oldfd == -1) {perror("socket");return -1;}// 2. 绑定 IP 地址和端口号struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP),};if (bind(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1) {perror("bind");return -1;}// 3. 监听客户端的连接请求if (listen(oldfd, BACKLOG) == -1) {perror("listen");return -1;}struct sockaddr_in client;socklen_t client_len = sizeof(client);// 4. 主线程循环接收客户端连接while (1) {int newfd = accept(oldfd, (struct sockaddr *)&client, &client_len); // 接收客户端连接请求ZYJ sb;sb.newfd = newfd; // 保存新的文件描述符sb.client = client; // 保存客户端信息pthread_t tid;// 5. 创建子线程处理客户端请求tid = pthread_create(&tid, NULL, fun, &sb);if (tid == -1) {perror("pthread_create");return -1;}// 6. 将线程设为分离状态,系统会自动回收线程资源pthread_detach(tid);}return 0; }
代码功能
- 创建套接字:通过
socket()创建一个 TCP 套接字。- 绑定 IP 地址和端口:通过
bind()将套接字绑定到指定的 IP 地址和端口号。- 监听连接:调用
listen()开始监听来自客户端的连接请求,最多允许BACKLOG个连接排队。- 接受连接:主线程通过
accept()接收客户端的连接请求,并返回一个新的文件描述符newfd用于与客户端进行通信。- 创建子线程:每当接受到新的连接,主线程会通过
pthread_create()创建一个子线程处理客户端的消息传递和接收。每个子线程的处理逻辑通过fun()函数完成。- 线程回收:通过
pthread_detach()将线程设置为分离状态,子线程在完成后会自动回收资源,避免主线程等待子线程退出。
关键函数
socket():创建一个套接字。bind():将套接字与本地的 IP 地址和端口号绑定。listen():让套接字进入监听状态,准备接受客户端连接。accept():阻塞等待客户端连接,并返回一个新的套接字用于与客户端通信。pthread_create():创建新的线程来处理客户端请求。pthread_detach():将线程设为分离状态,线程结束后自动回收资源。recv():接收客户端发送的消息。send():向客户端发送消息。
适用场景
这个多线程模型适用于客户端连接量较大或较为频繁的场景。每个客户端连接都会分配一个线程进行处理,因此可以在短时间内处理多个客户端的请求。
线程池优化
虽然该程序为每个连接创建了一个独立的线程,但在实际应用中,为了提高资源利用率和性能,可以引入线程池。线程池事先创建好一组线程,客户端请求到来时从线程池中取出一个线程来处理,而不是每次都创建一个新的线程。这样可以减少线程的创建和销毁开销。
注意事项
- 多线程同步问题:虽然本例中没有涉及多线程之间的共享资源,但在复杂应用中可能需要考虑线程同步机制(如互斥锁、条件变量等)。
- 线程回收:线程被设为分离状态(
pthread_detach()),这样可以在线程结束后由系统自动回收资源,而不需要调用pthread_join()。
总结
该程序展示了一个典型的多线程并发服务器模型,使用线程处理每个客户端请求,减少了主线程的负担,提高了处理效率。通过
pthread_detach()来自动回收资源,避免了线程泄露问题。
6. 客户端代码示例
#include <myhead.h>#define IP "192.168.60.45" #define PORT 9999int main(int argc, const char *argv[]) {// 1、创建套接字int oldfd = socket(AF_INET, SOCK_STREAM, 0);if (oldfd == -1){perror("socket");return -1;}// 2、连接服务器struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP),};if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1){perror("connect");return -1;}// 3、数据收发char buff[1024];while (1){fgets(buff, sizeof(buff), stdin); // 键盘输入字符串buff[strlen(buff) - 1] = '\0'; // 去除换行符// 发送数据到服务器send(oldfd, buff, sizeof(buff), 0);// 接收服务器数据int len = recv(oldfd, buff, sizeof(buff), 0);if (len == 0){printf("服务器意外退出\n");break;}printf("接收服务器消息:%s\n", buff);}// 关闭套接字close(oldfd);return 0; }
相关文章:
TCP并发服务器
端口号快速复用函数 通过getsockopt和setsockopt函数,管理套接字的端口号复用设置。具体操作如下: getsockopt函数 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);功能:获取套接字的某些选项的属性。…...
Debug-031-近期功能实现小结
由于时间原因,没办法对每个小的功能点进行比较细致的总结,这里统一去记录一下最近的实现了的功能,算是存档备份,为今后开发带来便利和参考。 一、ACEeditor ACEeditor使用手册(一)_ace editor-CSDN博客 AC…...
Consumer Group
不,kafka-consumer-groups.sh 脚本本身并不用于创建 Consumer Group。它主要用于管理和查看 Consumer Group 的状态和详情,比如列出所有的 Consumer Group、查看特定 Consumer Group 的详情、删除 Consumer Group 等。 Consumer Group 是由 Kafka 消费者…...
.NET架构师学习大纲
目录 微服务 Consul Ocelot Polly Skywalking Exceptionless Apollo Jenkins Docker Kubernetes DDD领域驱动设计 DevOps CDN Nginx 应用服务器集群 数据库高可用 异步化架构 Azure前沿技术 工具排查 O/RM-EFCore IOC&AOP Core WebApi WebServer 数…...
【代码随想录】贪心
455. 分发饼干 题目 随想录 本质: 对于每个孩子,使用可以满足该孩子的最小的饼干。所以对孩子胃口和饼干进行sort排序,依次将大的饼干满足给孩子。 贪心策略: 想一下局部最优,想一下全局最优,如果局部最优…...
Harmony鸿蒙类似与Android中broadcast广播的api使用及释义
EventHub模块提供了事件中心,提供订阅、取消订阅、触发事件的能力。 这里需要注意,该模块接口仅可在Stage模型下使用。且Api>9 EventHub.on on(event: string, callback: Function): void; 订阅指定事件。(接收广播) 参…...
openGauss 6.0.0主备部署(企业版)
openGauss 6.0.0主备部署(企业版) 文章目录 openGauss 6.0.0主备部署(企业版)一、环境准备1.操作系统环境2.修改主机名3.设置字符集编码4.修改openEuler默认yum源5.安装所需工具6.同步网络时间7.关闭防火墙 二、安装openGauss数据…...
【机器学习】聚类算法原理详解
聚类算法 性能度量: 外部指标 jaccard系数(简称JC)FM指数(简称FMI)Rand指数(简称RI) 内部指标 DB指数(简称DBI)Dunn指数(简称DI) 距离计算&am…...
Ubuntu20.04从零安装IsaacSim/IsaacLab
Ubuntu20.04从零安装IsaacSim/IsaacLab 电脑硬件配置:安装Isaac sim方案一:pip安装方案二:预构建二进制文件安装1、安装ominiverse2、在ominiverse中安装isaac sim,下载最新的4.2版本 安装Isaac Lab1、IsaacLab环境克隆2、创建con…...
基于Java Springboot大学校园旧物捐赠网站
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…...
【Java 集合】Collections 空列表细节处理
问题 如下代码,虽然定义为非空 NonNull,但依然会返回空对象,导致调用侧被检测为空引用。 实际上不是Collections的问题是三目运算符返回了null对象。 import java.util.Collections;NonNullprivate List<String> getInfo() {IccReco…...
大数据实验4-HBase
一、实验目的 阐述HBase在Hadoop体系结构中的角色;能够掌握HBase的安装和配置方法熟练使用HBase操作常用的Shell命令; 二、实验要求 学习HBase的安装步骤,并掌握HBase的基本操作命令的使用; 三、实验平台 操作系统࿱…...
deepin系统下载pnpm cnpm等报错
deepin系统下载pnpm cnpm等报错 npm ERR! request to https://registry.npm.taobao.org/pnpm failed, reason: certificate has expired 报错提示证书过期,执行以下命令 npm config set registry https://registry.npmmirror.com下载pnpm npm install pnpm -g查…...
#Js篇:JSON.stringify 和 JSON.parse用法和传参
JSON.stringify 和 JSON.parse 1. JSON.stringify JSON.stringify 方法将一个 JavaScript 对象或数组转换为 JSON 字符串。 基本用法 const obj { name: "Alice", age: 25 }; const jsonString JSON.stringify(obj); console.log(jsonString); // 输出: {"…...
c#通过网上AI大模型实现对话功能
目录 基础使用给大模型额外提供函数能力用Microsoft.Extensions.AI库实现用json格式回答 基础使用 https://siliconflow.cn/网站有些免费的大模型可以使用,去注册个账户,拿到apikey 引用 nuget Microsoft.Extensions.AI.OpenAI using Microsoft.Extensi…...
pymysql模块
1.pymysql基本使用 打开数据库连接,使用cursor()方法获取操作游标执行SQL语句 获取命令执行的查询结果 1.1 打开数据库连接 # 打开数据库连接 db pymysql.connect(host127.0.0.1,userroot,port3306,password"123",databasedb5) 1.2 使用cursor()方法获取操作游…...
WPF-模板和样式
在 WPF(Windows Presentation Foundation)中,模板是一种强大的机制,用于定义控件的外观。它允许你将控件的逻辑(功能)和外观(UI)分离开来。例如,一个按钮控件,…...
网络编程 day1.2~day2——TCP和UDP的通信基础(TCP)
笔记脑图 作业: 1、将虚拟机调整到桥接模式联网。 2、TCP客户端服务器实现一遍。 服务器 #include <stdio.h> #include <string.h> #include <myhead.h> #define IP "192.168.60.44" #define PORT 6666 #define BACKLOG 20 int mai…...
element ui table 每行不同状态
table 每行定义值 tableData: [ { name: ,type:,location:, ziduan:,createtype:,ziduanvalue:,checkAll:true,checkedCities: [空, null, str随机, int随机],isIndeterminate: true,table_id:single,downloaddisabled:true,deldisabled:true} ], table c…...
力扣--LRC 142.训练计划IV
题目 给定两个以 有序链表 形式记录的训练计划 l1、l2,分别记录了两套核心肌群训练项目编号,请合并这两个训练计划,按训练项目编号 升序 记录于链表并返回。 注意:新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&am…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
