【一】【socket聊天室】-多线程,socket编程

news2025/4/19 18:24:51

本文主要实现基于socket编程的聊天室,主要分为下面三个步骤:

(1)多用户聊天:一个服务器多个客户端,客户端信息显示在公共的服务端窗口,利用多线程实现;

         ——客户端双线程:一个接受线程一个发送线程(主线程);

         ——服务器单线程:接收线程;

(2)多用户广播界面:将信息显示到所有用户界面和服务器界面,同时服务器也能发言,利用多线程实现;

         ——客户端双线程:一个接受线程一个发送线程(主线程)

         ——服务器单线程:一个接收线程(主线程)一个发送线程;其中接受线程为每个连接开了单独的线程;

目录

一、基础流程

        服务器端

        客户端

二、多用户聊天

三、多用户广播聊天

客户端

服务端

效果展示

四、参考


一、基础流程

基于socket实现聊天室的流程如下:

对于socket编程,一般流程都为:搭建socket环境,创建套接字,进行连接后,开始通信。

服务器端:

(1)创建套接字:socket()函数;

(2)指定本机地址:bind()函数将本机地址和端口号与套接字连起来

(3)监听:listen()函数;监听连接请求,客户端发送连接请求;

(4)接受连接:accept()函数;

(5)发送接受消息:send()和recv()函数;

客户端:

(1)创建套接字:socket()函数;

(2)发送连接请求:connect()函数

         ——将套接字与主机地址和端口号连接起来:sockaddr_in addr;

         ——发送连接请求,等待服务器accept建立连接;

(3)发送接受消息:send()和recv()函数;

二、多用户聊天

首先,我们实现多个用户基于服务器聊天,所有信息都在服务器聊天框出现。

代码如下所示:

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include "afxres.h"
#include <winsock2.h> // winsock2的头文件
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

map<SOCKET, string> client; // 存储socket和昵称对应关系

int main()
{

	system("chcp 65001"); // 设置中文
	// 加载winsock环境
	WSAData wd;
	if(WSAStartup(MAKEWORD(2,2), &wd) != 0){
		cout << "加载网络环境失败" << endl;
		return 0;
	}
	else
		cout << "加载网络环境成功" << endl;

	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET){
		cout << "创建套接字失败" << endl;
		WSACleanup();
	}
	else
		cout << "创建套接字成功" << endl;

	// 给套接字绑定ip地址和端口:bind函数
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int len = sizeof(sockaddr_in);
	if(bind(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){
		cout << "服务器绑定端口和ip失败" <<endl;
		WSACleanup();
	}
	else
		cout << "server绑定端口和Ip成功" << endl;

	// 监听端口
	if(listen(s, 5) != 0){
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else
		cout << "设置监听状态成功!" << endl;

	cout<< "服务器监听连接中,请稍等......" << endl;

    // 循环接受:客户端发来的连接
	while(true){
		sockaddr_in addrClient;
		len = sizeof(sockaddr_in);
		SOCKET c = accept(s, (sockaddr*)&addrClient, &len);
		if( c == INVALID_SOCKET ){ // 一个失败我们就撤退,也可以去掉clean和return
			cout << "与客户端连接失败" << endl;
			WSACleanup();
			return 0;
		}
		
        //连接成功,开始发送消息
        char bufrecv[100] = {0}; //用来接受和发送数据
        int ret;
        ret = recv(c, bufrecv, 100, 0);
        client[c] = string(bufrecv);
        cout << "欢迎[" << client[c] << "]加入聊天室" << endl;
        string bufsend;
        bufsend = "欢迎[" + client[c] + "]加入聊天室";
        send(c, bufsend.data(), 100, 0);

        for(auto i : client){
            if(i.first ==  c)
                continue;
            send(i.first, bufsend.data(), 100, 0);
        }
        
        ret = 0;
        do{
            char buf[100] = {0};
            ret = recv(c, buf, 100, 0);
            cout << "[" << client[c] << "]: " << buf << endl;
        }while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error

        cout << "[" << client[c] << "]离开聊天室!" << endl;

	}
	
	// 关闭连接,释放资源
	closesocket(s);
	WSACleanup();
	return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
//#include "stdafx.h"
#include "afxres.h"
#include <winsock2.h> // winsock2的头文件
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

map<SOCKET, string> client;

int main()
{

	system("chcp 65001");
	// 加载winsock环境
	WSAData wd;
	if(WSAStartup(MAKEWORD(2,2), &wd) != 0){
		cout << "加载网络环境失败" << endl;
		return 0;
	}
	else
		cout << "加载网络环境成功" << endl;

	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET){
		cout << "创建套接字失败" << endl;
		WSACleanup();
	}
	else
		cout << "创建套接字成功" << endl;

	// 给套接字绑定ip地址和端口:bind函数
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int len = sizeof(sockaddr_in);
	if(connect(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){
		cout << "客户端连接失败" <<endl;
		WSACleanup();
        return 0;
	}
	else
		cout << "客户端连接成功" << endl;

    // 发送和接受数据即可
    string name;
    char bufrecv[100] = {0}; 
    cout << "请输入你的昵称:";
    getline(cin, name); // 读入一整行,可以有空格
    send(s, name.data(), 100, 0);
	int ret;
	ret = recv(s, bufrecv, 100, 0); // 接受欢迎信息
	cout << bufrecv << endl;
    
    // while循环发送数据
	ret = 0;
	do{
        cout << "Enter the word: ";
		char bufrecv[100] = {0};
        cin.getline(bufrecv, 100); 
		ret = send(s, bufrecv, 100, 0);
	}while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error
    
	// 关闭连接,释放资源
	closesocket(s);
	WSACleanup();
	return 0;
}

 实现结果如上图所示,可以发现,成功建立连接,并且可以显示出信息。

但我们发现,当我们开启多个客户端,不能同时显示信息,只有关掉前面的客户端,后面的信息才能接着显示?

 经过分析我们发现,服务端每接受一个连接,就开始陷入该连接的while里面,不断接受该连接的信息,而没有跳出while,以得到其他的连接。

我们采用多线程来解决这个问题

对于每次建立的连接,我们将该连接开启一个线程用来处理服务器与该客户的信息接受,主线程一直处于监听和接受连接状态,从线程处于信息沟通状态。一旦建立一个连接,就给该连接开启一个线程用于发送和接受信息,从而实现同步。

实现的结果如下:

 此时,通过多线程我们成功实现了多用户同时通信。

修改代码如下:

服务端:

#include <stdio.h>
#include <stdlib.h>
//#include "stdafx.h"
#include "afxres.h"
#include <winsock2.h> // winsock2的头文件
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

map<SOCKET, string> client; // 存储socket和昵称对应关系
DWORD WINAPI Threadfun(LPVOID lpParameter);

int main()
{

	system("chcp 65001"); // 设置中文
	// 加载winsock环境
	WSAData wd;
	if(WSAStartup(MAKEWORD(2,2), &wd) != 0){
		cout << "加载网络环境失败" << endl;
		return 0;
	}
	else
		cout << "加载网络环境成功" << endl;

	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET){
		cout << "创建套接字失败" << endl;
		WSACleanup();
	}
	else
		cout << "创建套接字成功" << endl;

	// 给套接字绑定ip地址和端口:bind函数
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int len = sizeof(sockaddr_in);
	if(bind(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){
		cout << "服务器绑定端口和ip失败" <<endl;
		WSACleanup();
	}
	else
		cout << "server绑定端口和Ip成功" << endl;

	// 监听端口
	if(listen(s, 5) != 0){
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else
		cout << "设置监听状态成功!" << endl;

	cout<< "服务器监听连接中,请稍等......" << endl;

	// 循环接受:客户端发来的连接
	while(true){
		sockaddr_in addrClient;
		len = sizeof(sockaddr_in);
		SOCKET c = accept(s, (sockaddr*)&addrClient, &len);
		if( c == INVALID_SOCKET ){ // 一个失败我们就撤退,也可以去掉clean和return
			cout << "与客户端连接失败" << endl;
			WSACleanup();
			return 0;
		}
		HANDLE hthread = CreateThread(NULL, 0, Threadfun, (LPVOID)c, 0, NULL);
		CloseHandle(hthread); // 关闭句柄,没用
	}
	
	// 关闭连接,释放资源
	closesocket(s);
	WSACleanup();

	return 0;
}

DWORD WINAPI Threadfun(LPVOID lpParameter){
	SOCKET c = (SOCKET)lpParameter;

	//连接成功,开始发送消息
    char bufrecv[100] = {0}; //用来接受和发送数据
    int ret;
    ret = recv(c, bufrecv, 100, 0);
    client[c] = string(bufrecv);
    cout << "欢迎[" << client[c] << "]加入聊天室" << endl;
    string bufsend;
    bufsend = "欢迎[" + client[c] + "]加入聊天室";
    send(c, bufsend.data(), 100, 0);
    
    ret = 0;
    do{
        char buf[100] = {0};
        ret = recv(c, buf, 100, 0);
        cout << "[" << client[c] << "]: " << buf << endl;
    }while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error

    cout << "[" << client[c] << "]离开聊天室!" << endl;
    return 0;
}

客户端代码不变。

三、多用户广播聊天

从上边我们可以发现,服务端只有接受信息功能,客户端只有发送信息功能,这就导致每个人只能通过服务端界面看消息,并且我们也不能实现一些小功能如:

(1)@某用户的消息提醒;

(2)用户退出提醒:某个用户退出,我们应该广播给每个用户界面,告诉该用户退出;

(3)服务器端作为管理员,也应该有说话的功能;

为了解决上面三个问题,我们进行如下探索。

客户端:

对于客户端,其建立连接后,一直处于while(true)的发送信息循环里,直到退出:

 那么我们可以考虑创建两个线程:接受信息线程和发送信息线程,从而使客户端既能接受信息,也能同步发送消息

我们将主线程用来发送数据(因为客户端主任务是发送),创建另一个线程用来接受数据:

 服务端:

服务端主线程是监听和接受连接,每接受一个连接就创建该连接的线程。那么为了能使服务端也作为发言方发送数据,我们创建副线程用来发送数据。同时,对于接受数据的线程,为了显示到别的用户端界面上,我们将收到的信息广播出去,让所有用户都能看见,同时广播实时用户状态,修改代码如下:

 创建的发送线程如下:

 那么至此,我们就修改好了两端的代码,进行尝试。

效果展示如下:

 可以看到,实现了客户端和用户端都是读写双线程,既能接受数据也能发送数据,同时如果用户离开也会广播信息。

修改后的代码如下:

服务端:

#include <stdio.h>
#include <stdlib.h>
//#include "stdafx.h"
#include "afxres.h"
#include <winsock2.h> // winsock2的头文件
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

map<SOCKET, string> client; // 存储socket和昵称对应关系
DWORD WINAPI Threadfun(LPVOID lpParameter);
DWORD WINAPI ThreadSend(LPVOID lpParameter);

int main()
{

	system("chcp 65001"); // 设置中文
	// 加载winsock环境
	WSAData wd;
	if(WSAStartup(MAKEWORD(2,2), &wd) != 0){
		cout << "加载网络环境失败" << endl;
		return 0;
	}
	else
		cout << "加载网络环境成功" << endl;

	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET){
		cout << "创建套接字失败" << endl;
		WSACleanup();
	}
	else
		cout << "创建套接字成功" << endl;

	// 给套接字绑定ip地址和端口:bind函数
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int len = sizeof(sockaddr_in);
	if(bind(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){
		cout << "服务器绑定端口和ip失败" <<endl;
		WSACleanup();
	}
	else
		cout << "server绑定端口和Ip成功" << endl;

	// 监听端口
	if(listen(s, 5) != 0){
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else
		cout << "设置监听状态成功!" << endl;

	cout<< "服务器监听连接中,请稍等......" << endl;

    // 发送消息线程
    CloseHandle(CreateThread(NULL, 0, ThreadSend, (LPVOID)s, 0, NULL));
    
	// 循环接受:客户端发来的连接
	while(true){
		sockaddr_in addrClient;
		len = sizeof(sockaddr_in);
		SOCKET c = accept(s, (sockaddr*)&addrClient, &len);
		if( c == INVALID_SOCKET ){ // 一个失败我们就撤退,也可以去掉clean和return
			cout << "与客户端连接失败" << endl;
			WSACleanup();
			return 0;
		}

		HANDLE hthread = CreateThread(NULL, 0, Threadfun, (LPVOID)c, 0, NULL);
		CloseHandle(hthread); // 关闭句柄,没用
	}
	
	// 关闭连接,释放资源
	closesocket(s);
	WSACleanup();
	return 0;
}

DWORD WINAPI ThreadSend(LPVOID lpParameter){
	SOCKET c = (SOCKET)lpParameter;

    int ret = 0;
	do{
		char bufsend[100] = {0};
        cin.getline(bufsend, 100); 
        
        // 发送给所有用户端
        string str = "[Server]: " + string(bufsend);
        for(auto i : client)
		    ret = send(i.first, str.data(), 100, 0);
	}while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error

    return 0;
}
DWORD WINAPI Threadfun(LPVOID lpParameter){
	SOCKET c = (SOCKET)lpParameter;

	//连接成功,开始发送消息
    char bufrecv[100] = {0}; //用来接受和发送数据
    int ret;
    ret = recv(c, bufrecv, 100, 0);
    client[c] = string(bufrecv);

    string bufsend;
    bufsend = "欢迎[" + client[c] + "]加入聊天室";
    cout << bufsend << endl;

    for(auto i : client)
        send(i.first, bufsend.data(), 100, 0);
    
    ret = 0;
    do{
        char buf[100] = {0};
        ret = recv(c, buf, 100, 0);
        cout << "[" << client[c] << "]: " << buf << endl << endl;

        // 将接受到的信息广播
        string str1 = "[" + client[c] + "]: " + string(buf);
        for(auto i : client)
            send(i.first, str1.data(), 100, 0);
    }while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error

    string str2 = "[" + client[c] + "]离开聊天室!";
    cout << str2 << endl;
    for(auto i : client)
        send(i.first, str2.data(), 100, 0);
    
    return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
//#include "stdafx.h"
#include "afxres.h"
#include <winsock2.h> // winsock2的头文件
#include <iostream>
#include <map>
#pragma comment(lib, "ws2_32.lib")
using namespace std;

map<SOCKET, string> client;
DWORD WINAPI Threadfun(LPVOID lpParameter);

int main()
{
	system("chcp 65001");

	// 加载winsock环境
	WSAData wd;
	if(WSAStartup(MAKEWORD(2,2), &wd) != 0){
		cout << "加载网络环境失败" << endl;
		return 0;
	}
	else
		cout << "加载网络环境成功" << endl;

	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == INVALID_SOCKET){
		cout << "创建套接字失败" << endl;
		WSACleanup();
	}
	else
		cout << "创建套接字成功" << endl;

	// 给套接字绑定ip地址和端口:bind函数
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int len = sizeof(sockaddr_in);
	if(connect(s, (SOCKADDR*)&addr, len) == SOCKET_ERROR){
		cout << "客户端连接失败" <<endl;
		WSACleanup();
        return 0;
	}
	else
		cout << "客户端连接成功" << endl;

    // 发送和接受数据即可
    string name;
    char bufrecv[100] = {0}; 
    cout << "请输入你的昵称:";
    getline(cin, name); // 读入一整行,可以有空格
    send(s, name.data(), 100, 0);
	int ret;
    
    // 建立连接后,创建线程用于接受数据,主线程用来发送数据
    CloseHandle(CreateThread(NULL, 0, Threadfun, (LPVOID)s, 0, NULL));
    
    // while循环发送数据
	ret = 0;
	do{
		char bufrecv[100] = {0};
        cin.getline(bufrecv, 100); 
		ret = send(s, bufrecv, 100, 0);
	}while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error
    
	// 关闭连接,释放资源
	closesocket(s);
	WSACleanup();
	return 0;
}

DWORD WINAPI Threadfun(LPVOID lpParameter){
	SOCKET c = (SOCKET)lpParameter;
    int ret = 0;
    do{
        char buf[100] = {0};
        ret = recv(c, buf, 100, 0);
        cout << buf << endl << endl;
    }while(ret!=SOCKET_ERROR && ret!=0); //如果连接被关闭,返回0,否则返回socket_error

    return 0;
}

四、参考

优秀博文:

(465条消息) Socket 多人聊天室的实现 (含前后端源码讲解)(一)_socket的聊天程序代码及理解_宾有为的博客-CSDN博客

各个接口函数的解释:

socket技术详解(看清socket编程) - 枫飞飞 - 博客园 (cnblogs.com)

视频:

​​​​​​C/C++多线程实战教程:多线程客户端聊天室的实现!腾讯QQ的核心技术,老马就靠这个技术一战成名!_哔哩哔哩_bilibili

写代码部分(服务端)_哔哩哔哩_bilibili

C++windows网络编程学习笔记【简易聊天室】(二)_哔哩哔哩_bilibili

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV基础(一)

1.认识图像&#xff08;彩色图中每一个像素点都包含三个颜色通道RGB&#xff0c;数值范围为0~255&#xff0c;0代表黑色&#xff0c;255代表白色&#xff09; import cv2 #opencv 读取的格式为BGRimg cv2.imread(cat.png) #读取图像 cv2.imshow(cat, img) #显示图像img&#x…

Matlab实现FFT变换

Matlab实现FFT变换 文章目录Matlab实现FFT变换原理实现手算验证简单fft变换和频谱求取功率谱结论在信号处理中&#xff0c;快速傅里叶变换&#xff08;FFT&#xff09;是一种非常常见的频域分析方法。本文将介绍如何使用Matlab实现FFT变换&#xff0c;并通过Matlab代码演示实际…

SAP ABAP 深度解析Smartform打印特殊符号等功能

ABAP 开发人员可以在 Smartform 输出上显示 SAP 图标或 SAP 符号。例如,需要在 SAP Smart Forms 文档上显示复选框形状的输出。SAP Smartform 文档上可以轻松显示空复选框、标记复选框以及 SAP 图标等特殊符号。 在 SAP Smartform 文档中添加一个新的文本节点。 1. 单击“更…

开发一款系统软件的流程步骤是什么

在如今的数字化时代&#xff0c;软件开发成为了一个重要的行业。无论是大型企业还是小型创业公司&#xff0c;软件开发都是不可或缺的一环。在本文中&#xff0c;我将介绍一些网上常见的软件开发步骤&#xff0c;以便开发者能够更好地理解和实践。1、需求分析需求分析是开发系统…

基于transformer的多帧自监督深度估计 Multi-Frame Self-Supervised Depth with Transformers

Multi-Frame Self-Supervised Depth with Transformers基于transformer的多帧自监督深度估计0 Abstract 多帧深度估计除了学习基于外观的特征外&#xff0c;也通过特征匹配利用图像之间的几何关系来改善单帧估计。我们采用深度离散的核极抽样来选择匹配像素&#xff0c;并通过一…

基于Jeecgboot前后端分离的ERP系统开发代码生成(六)

商品信息原先生成的不符合要求&#xff0c;重新生成&#xff0c;包括一个附表商品价格信息表 一、采用TAB主题一对多的模式 因为主键&#xff0c;在online表单配置是灰的&#xff0c;所以不能进行外键管理&#xff0c;只能通过下面数据库进行关联录入&#xff0c;否则online界面…

案例19-遇见问题的临时解决方案和最终解决方案

目录1、背景介绍2、两种解决方案的概念1、临时解决方案&#xff1a;2、最终解决方案&#xff1a;3、排查问题过程4、总结站在用户的角度思考作为软件开发者5、升华1、背景介绍 首先说明这是系统很早之前的时候的一个功能&#xff0c;当时和学习通还有很强的耦合关系。在学习通…

研究链表空间销毁问题

&#x1f4af;&#x1f4af;&#x1f4af; 1.研究链表空间销毁问题 当链表使用完后&#xff0c;需要将链表销毁&#xff0c;那么该如何销毁呢&#xff1f; void SLTDestroy(SLTNode* phead)//销毁单链表 {SLTNode* cur phead;while(cur){free(cur);cur cur->next;} }你…

Linux下Nginx安装使用

一、下载解压nginx # 进入要放安装包的目录 cd /opt/software # 下载安装包 wget https://nginx.org/download/nginx-1.20.2.tar.gz # 解压缩 tar -zxvf nginx-1.20.2.tar.gz -C /opt/modules # 进入解压后的目录 cd /opt/modules/nginx-1.20.2/二、安装nginx 1、安装编译器 …

剑指 Offer II 021. 删除链表的倒数第 n 个结点

题目链接 剑指 Offer II 021. 删除链表的倒数第 n 个结点 mid 题目描述 给定一个链表&#xff0c;删除链表的倒数第 n个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; …

MySQL的多表操作

多表关系 介绍 实际开发中&#xff0c;一个项目通常需要很多张表才能完成。例如&#xff1a;一个商城项目就需要分类表(category)、商品表(products)、 订单表(orders)等多张表。且这些表的数据之间存在一定的关系&#xff0c;接下来我们将在单表的基础上&#xff0c;一起学习…

DolphinDB 机器学习在物联网行业的应用:实时数据异常率预警

数据异常率预警在工业安全生产中是一项重要工作&#xff0c;对于监控生产过程的稳定性&#xff0c;保障生产数据的有效性&#xff0c;维护生产设备的可靠性具有重要意义。随着大数据技术在生产领域的深入应用&#xff0c;基于机器学习的智能预警已经成为各大生产企业进行生产数…

logback无法删除太久远的日志文件?logback删除日志文件源码分析

logback无法删除太久远的日志文件&#xff1f;logback删除日志文件源码分析 最近发现logback配置滚动日志&#xff0c;但是本地日志文件甚至还有2年前的日志文件&#xff0c;服务器是却是正常的&#xff01; 网上搜索了一波没有发现&#xff0c;只找到说不能删除太久远的旧日志…

Leetcode. 21 合并两个有序列表

尾插 核心思路&#xff1a;依次比较 &#xff0c;取经过比较后较小值进行尾插 cur1 指向list1 ,cur 2指向list2 ,当cur1走完list1 或者cur2 走完list2 后停止 如果cur1走完list1 ,可以将cur2 整个拿下来尾插 如果cur2走完list2 ,可以将cur1 整个拿下来尾插 特殊情况 &#xff1…

c# 32位程序突破2G内存限制

起因 在开发过程中&#xff0c;由于某些COM组件只能在32位程序下运行&#xff0c;程序不得不在X86平台下生成。而X86的32位程序默认内存大小被限制在2G。由于程序中可能存在大数量处理&#xff0c;期间对象若没有及时释放或则回收&#xff0c;内存占用达到了1.2G左右&#xff…

瀑布开发与敏捷开发的区别,以及从瀑布转型敏捷项目管理的5大注意事项

事实证明&#xff0c;瀑布开发管理模式并不适合所有的软件项目&#xff0c;但敏捷项目管理却对大多数项目有效。那么当团队选择转型敏捷的时候有哪些因素必须注意&#xff1f;敏捷开发最早使用者大多是小型、独立的团队&#xff0c;他们通常致力于小型、独立的项目。正是他们的…

Keepalive+LVS群集部署

KeepaliveLVS群集部署一、Keepalive概述1、什么是Keepalive2、Keepalive工作原理3、Keepalive主要模块及作用4、Keepalived 服务重要功能&#xff08;1&#xff09;管理 LVS 负载均衡软件&#xff08;2&#xff09;支持故障自动切换&#xff08;3&#xff09;实现 LVS 负载调度…

SpringBoot下的Spring框架学习(tedu)——day03——Spring DI、SpringAOP JDK动态代理

SpringBoot下的Spring框架学习&#xff08;tedu&#xff09;——day03——Spring DI、SpringAOP JDK动态代理 目录SpringBoot下的Spring框架学习&#xff08;tedu&#xff09;——day03——Spring DI、SpringAOP JDK动态代理1. Spring的依赖注入1.1 依赖注入案例1.1.1 定义接口…

Python的运算符

Python支持多种运算符&#xff0c;下表大致按照优先级从高到低的顺序列出了所有的运算符&#xff0c;运算符的优先级指的是多个运算符同时出现时&#xff0c;先做什么运算然后再做什么运算。除了我们之前已经用过的赋值运算符和算术运算符&#xff0c;我们稍后会陆续讲到其他运…

解决launch:program .exe does not exist

二. 程序的运行和调试 1.launch.json 复制下列代码至launch.json&#xff0c;并根据指导做出相对/绝对路径修改 用 IntelliSense 了解相关属性。 {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: https://go.micros…