C++ 文件传输和多人聊天室

两个主要的功能:

1.文件传输
2.多人聊天室

要用的技术点:epoll模型

出现的bug总结:

解决1个bug:每次客户端挂掉以后,就会报9:Bad file descriptormain.
解决办法:在if (len < 0)条件里面加入break就可以了

bug2:每次只能传回一次数据,数据同步的问题?
解决办法:把readLine函数写成readAll,问题解决,但是没找到原因是什么???

bug3:只能有一个人登录聊天室
解决了只能一人登录的问题,现在可以多人登录,但是消息还不是共享的
解决了,通过引入cli_map<int,Client>,去掉消息中的while(true)循环解决掉了。

bug4:无法在dubug文件夹中双击QQ_client_server.exe运行,通过http://t.csdnimg.cn/b1yiN博客看明白了

这时确定了要用缓冲区开头写一个字符C或者字符F的形式确定是文本输入还是文件输入
bug5:如何进行修改,可以区分开文本输入还是文件输入?
思路:要在QT客户端进行修改了,加了一个name_flag标志位
在修改了标志位name_flag以后,只有第一次输入名字的时候,前面有C,后面没有\n,后面就都有了
所以第一次需要提醒一下(在文本框中写上,请输出姓名)

bug6:将writeText函数抽象化的时候,遇到几个问题
1.访问Client的成员变量
传入了map<int,Client>指针,如果要访问Client的成员变量的时候,要这样操作:
(*client_map)[fd].name,
2.遍历map<int,Client>结构体数组:
还有想要遍历map<int,Client>结构体数组的时候,使用迭代器:for(auto it = (*client_map).begin(); it != (*client_map).end(); ++it),这里it的成员变量类型为:map<int,Client>::iterate
3.写入数据的时候使用了write(it->first, buffer, sizeof(buffer));
这里的sizeof(buffer) = 8,是一个指针的大小,所以,要把变量替换为原先的1024

bug7:传输文件功能实现:
需要记录文件名,文件大小等文件的基本信息,于是有两种思路:
思路1:在客户端将文件头信息和文件内容信息分开发送
思路2:在服务端对收到的buffer进行文件解析。对于小文件来说,需要用&&&&作为分隔符,接收到的buffer进行分割的代码是:

char file_name[512] = "";
char file_size[64];
const char *d = "&&&&";
char *p;
//文件格式:F&&&&文件名&&&&文件大小&&&&文件内容
//第一次:F 不管
p = strtok(buffer,d);
//第二次 文件名
strcpy(file_name,strtok(NULL,d));
printf("second time:%s:%s\n",__FUNCTION__,file_name);
FILE *fp = fopen(file_name,"w");
if(fp == NULL){printf("无法写文件%s\n",file_name);exit(1);
}
//第三次 文件大小
strcpy(file_size,strtok(NULL,d));
int len = atoi(file_size);
printf("third time:%s:%d\n",__FUNCTION__,len);
//第四次 文件内容
strcpy(buffer,strtok(NULL,d));//buffer没有问题
printf("forth time:%s:%s\n",__FUNCTION__,buffer);

常用的一些转换方法

string a转换为char b[]的方法:strcpy(b,a);
QString转换为各种格式的数据的方法(要注意)

用printf(“%s”)打印string的方法:
string a;
printf(“%s”,a.c_str());
否则就会打印乱码

完整的代码:

QT客户端:

.pro文件中,要加

QT       += core gui sql network widgets

mainwindows.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"//用来on_btnSend_clicked发送消息的时的标志位,保证发送姓名的时候不带有\n,同时保证开启连接时发送的消息开头带有C
int name_flag = 0;//用来传输文件名
int fileName_flag = 0;MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//设置状态栏格式statusBar()->setMinimumHeight(25);statusBar()->setStyleSheet(QString("QStatusBar::item{border: 1px}")); // 不显示边框//    m_loadSize = 4*1024; // 每次发送的文件数据大小
//    m_totalBytes = 0;
//    m_bytesWritten = 0;
//    m_bytesToWrite = 0;m_tcpTextClient = new QTcpSocket(this); //创建socket变量m_tcpFileClient = new QTcpSocket(this);m_labSocketState = new QLabel("Socket状态:");//状态栏标签m_labSocketState->setMinimumWidth(250);statusBar()->addWidget(m_labSocketState);QString localIP = getLocalIP();//本机IPthis->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);ui->comboServer->addItem(localIP);//在下拉框里面可以看到追加的ip,即localIP//显示连接状态connect(m_tcpTextClient,SIGNAL(connected()),this,SLOT(onConnected()));connect(m_tcpTextClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));//状态改变时要更新connect(m_tcpTextClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));//读取从客户端传回的数据,connect(m_tcpTextClient,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));}MainWindow::~MainWindow()
{delete ui;
}//获得本地ip
QString MainWindow::getLocalIP()
{//获取本机IPv4地址QString hostName = QHostInfo::localHostName();//本机主机名QHostInfo hostInfo = QHostInfo::fromName(hostName);QString localIP="";QList<QHostAddress> addList = hostInfo.addresses();if(!addList.isEmpty()){for(int i = 0;i<addList.count();i++){QHostAddress aHost = addList.at(i);if(QAbstractSocket::IPv4Protocol==aHost.protocol()){localIP = aHost.toString();break;}}}return localIP;
}//-------------------------------------------------------------------------
//这里是客户端的连接部分void MainWindow::on_connectBt_clicked()
{//连接到服务器动作QString addr = ui->comboServer->currentText();quint16 port = ui->spinPort->value();m_tcpTextClient->connectToHost(addr, port);//    m_bytesWritten = 0; // 初始化已发送字节为0
//    m_tcpFileClient->connectToHost(addr, port);}void MainWindow::on_disconnectBt_clicked()
{//断开与服务器的连接动作if (m_tcpTextClient->state()==QAbstractSocket::ConnectedState)m_tcpTextClient->disconnectFromHost();name_flag = 0;
}void MainWindow::on_btnSend_clicked()
{//发送数据QString  msg=ui->editMsg->toPlainText();m_tcpTextClient->write("C");
//    qDebug() << msg;if (msg.isEmpty()){QMessageBox::information(this, "提示", "发送的消息不能为空!");return;}ui->plainTextEdit->appendPlainText("[out] "+msg);ui->editMsg->clear();ui->editMsg->setFocus();QByteArray  str=msg.toUtf8();if(name_flag != 0){str.append('\n');}
//    m_tcpTextClient->write(str.constData(),1024);name_flag = 1;qint64 flag = m_tcpTextClient->write(str);if(flag > 0){qDebug() << "success write,flag = " << flag;}else{qDebug() << "write error occurred";}}//显示连接的状态
void MainWindow::onConnected()
{ //connected()信号槽函数ui->plainTextEdit->appendPlainText("**已连接到服务器");ui->plainTextEdit->appendPlainText("**peer address:"+m_tcpTextClient->peerAddress().toString());ui->plainTextEdit->appendPlainText("**peer port:"+QString::number(m_tcpTextClient->peerPort()));ui->connectBt->setEnabled(false);ui->disconnectBt->setEnabled(true);
}//显示断开连接的状态
void MainWindow::onDisconnected()
{//disConnected()信号槽函数ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");ui->connectBt->setEnabled(true);ui->disconnectBt->setEnabled(false);
}//文本读服务器的消息
void MainWindow::onSocketReadyRead()
{   //readyRead()信号槽函数while(m_tcpTextClient->canReadLine()){ui->plainTextEdit->appendPlainText("[in] "+ QString::fromUtf8(m_tcpTextClient->readAll()));
//        printf("readLine()buffer:%d\n",sizeof(m_tcpTextClient->readLine(1024)));}
}//清空文本框消息
void MainWindow::on_clearBtn_clicked()
{ui->plainTextEdit->clear();
}//发送文件
void MainWindow::on_btnSendFile_clicked()
{//为了和发送消息进行区分qint64 flag = m_tcpTextClient->write("F");if(flag > 0){qDebug() << "success write,flag = " << flag;}else{qDebug() << "write error occurred";}startTransfer();
}void MainWindow::startTransfer()  //实现文件大小等信息的发送
{//这里的m_fileName是在selectFile中找到的,已经赋值了QString currentFileName = m_fileName.right(m_fileName.size() - m_fileName.lastIndexOf('/')-1);//最开始传输,写一下文件名if(fileName_flag == 0){//注意这里write的参数是QByteArray类型或者是const char*类型,//即要将QString转化为QByteArray用.toUtf8()//将QByteArray转化为const char*,用.data()m_tcpTextClient->write("&&&&");int flag = m_tcpTextClient->write(currentFileName.toUtf8());if(flag < 0){//ui->plainTextEdit->appendPlainText(QString("传送文件 %1 成功").arg(m_fileName));ui->plainTextEdit->appendPlainText(QString("传送文件 %1 失败").arg(m_fileName));exit(1);}ui->plainTextEdit->appendPlainText(QString("正在传送 %1 文件...").arg(m_fileName));}//文件名和文件内容需要分两次发送,看看别人的解决思路//github中的解决思路是://发送端:读文件,进入循环发送文件,接收success信息,显示发送文件成功//接收端:F字母判断是接收文件,fopen打开文件的同时命名文件,进入循环,循环逻辑:recv以后fwrite文件,文件接收结束。给发送端发送success信息//接收两次,一次是文件名,一次是内容QFile file(m_fileName);bool isok = file.open(QIODevice::ReadWrite);qDebug() << "isok:" <<isok;//char buffer[1024];QFileInfo info(m_fileName);qDebug()<<"文件名字:"<< info.fileName();qDebug()<<"文件后缀:"<< info.suffix();qDebug()<<"文件大小:"<< info.size();qDebug()<<"文件创建时间:"<< info.created().toString("yyyy-MM-dd hh:mm:ss"); //yyyy代表年 MMd//发送文件大小m_tcpTextClient->write("&&&&");m_tcpTextClient->write(QByteArray::number(info.size()));m_tcpTextClient->waitForBytesWritten();if(isok){//用来存储文件内的内容QByteArray array;while(file.atEnd() == false){array += file.readLine();qDebug() << "readAll loop";}qDebug()<<"文件内容:"<< array;//输入分隔符,文件发送的格式为F&&&&文件名&&&&文件大小&&&&文件内容m_tcpTextClient->write("&&&&");//传输文件内容m_tcpTextClient->write(array);}}void MainWindow::on_btnSelectFile_clicked()
{m_fileName = QFileDialog::getOpenFileName(this, "选择文件");if (!m_fileName.isEmpty()){ui->plainTextEdit->appendPlainText(QString("打开文件 %1 成功!").arg(m_fileName));ui->btnSendFile->setEnabled(true);}}void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{//stateChange()信号槽函数switch(socketState){case QAbstractSocket::UnconnectedState:m_labSocketState->setText("scoket状态:UnconnectedState");break;case QAbstractSocket::HostLookupState:m_labSocketState->setText("scoket状态:HostLookupState");break;case QAbstractSocket::ConnectingState:m_labSocketState->setText("scoket状态:ConnectingState");break;case QAbstractSocket::ConnectedState:m_labSocketState->setText("scoket状态:ConnectedState");break;case QAbstractSocket::BoundState:m_labSocketState->setText("scoket状态:BoundState");break;case QAbstractSocket::ClosingState:m_labSocketState->setText("scoket状态:ClosingState");break;case QAbstractSocket::ListeningState:m_labSocketState->setText("scoket状态:ListeningState");}
}//下面是两个没有用到的槽函数(弃用)
void MainWindow::displayError(QAbstractSocket::SocketError) //显示错误
{ui->plainTextEdit->appendPlainText(m_tcpFileClient->errorString());m_tcpFileClient->close();ui->progressBar->reset();
}
// 更新进度条,实现文件的传送(弃用)
void MainWindow::updateClientProgress(qint64 numBytes)
{// 已经发送数据的大小m_bytesWritten += (int)numBytes;if(m_bytesToWrite > 0) // 剩余数据大小{// 从文件中取出数据到发送缓冲区,每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,就发送剩余数据的大小m_outBlock = m_localFile->read(qMin(m_bytesToWrite, m_loadSize));// 从发送缓冲区发送数据,计算发送完一次数据后还剩余数据的大小m_bytesToWrite -= (int)m_tcpFileClient->write(m_outBlock);// 清空发送缓冲区m_outBlock.resize(0);} else{m_localFile->close(); // 没有数据待发送,则关闭文件}//更新进度条ui->progressBar->setMaximum(m_totalBytes);ui->progressBar->setValue(m_bytesWritten);if(m_bytesWritten == m_totalBytes) //发送完毕{ui->plainTextEdit->appendPlainText(QString("传送文件 %1 成功").arg(m_fileName));m_localFile->close();}
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
//sql 要在.pro文件中加入sql
#include <QFile>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>//server 要在.pro文件中加入network
#include <QTcpServer>
#include <QHostInfo>
#include <QTcpSocket>#include <QSignalMapper>#include<QDebug>
#include<QMessageBox>//选择文件
#include <QFileDialog>//时间
#include <QDateTime>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();protected slots:void onNewConnection();  //有新的请求会调用//void onSocketStateChange(QAbstractSocket::SocketState socketState);//显示连接状态 暂不需要,可通过stateChanged信号调用void onClientConnected(int);	//连接成功调用void onClientDisconnected(int);	//连接断开调用void onSocketReadyRead(int);	//有消息调用QSqlDatabase connectSql();private slots://文本读服务器的消息void onSocketReadyRead();//用来显示连接状态的槽函数void onSocketStateChange(QAbstractSocket::SocketState socketState);void onConnected();void onDisconnected();//连接动作void on_connectBt_clicked();void on_disconnectBt_clicked();//发送消息按钮,清空文本框按钮,选择文件按钮和发送文件按钮void on_btnSend_clicked();void on_clearBtn_clicked();void on_btnSelectFile_clicked();void on_btnSendFile_clicked();//发送文件大小等信息void startTransfer();//更新进度条,实现文件的传送(暂时没用到)void updateClientProgress(qint64 numBytes);void displayError(QAbstractSocket::SocketError);
private:Ui::MainWindow *ui;QSqlDatabase db; //数据库连接QTcpServer *tcpServer; //server连接QLabel* LabListenInfo; //状态栏显示正在连接QList <QTcpSocket *> tcpSocket;//TCP通信的Socket,消息格式为ip 端口号QList <bool> isfrist;       //判断是否第一条消息QString getLocalIP(); //得到本机ip//项目里的QTcpSocket  *m_tcpTextClient;  //文本消息socketQLabel  *m_labSocketState;  //状态栏显示标签QString m_fileName;  //保存文件路径//状态栏QLabel* labelStsInfo;               // 状态栏提示信息QLabel* labelStsIP;            // IP信息//发送文件需要的参数QFile *m_localFile;  //要发送的文件qint64 m_totalBytes;  //数据总大小qint64 m_bytesWritten;  //已经发送数据大小qint64 m_bytesToWrite;   //剩余数据大小qint64 m_loadSize;   //每次发送数据的大小QByteArray m_outBlock;  //数据缓冲区,即存放每次要发送的数据QTcpSocket  *m_tcpFileClient;  //文件消息socket};
#endif // MAINWINDOW_H

服务端的cpp文件:

#include <stdio.h>
#include<iostream>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <error.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>#include<map>
#include<fstream> using namespace std;
#define BUFFER_SIZE 1024struct Client{int sockfd;string name;string fileName;
};void writeText(map<int,Client> *client_map,char* buffer,int fd){cout << "unconvert buffer = " << buffer << endl;if((*client_map)[fd].name == ""){(*client_map)[fd].name = buffer+1;printf("(%d):%s,name:%s\n",__LINE__,__FUNCTION__,(*client_map)[fd].name.c_str());}else{//为了去除每次buffer前面的C字符string msg = buffer+1;string realmsg = '[' + (*client_map)[fd].name + ']' + msg;memset(buffer, 0, sizeof(buffer));strcpy(buffer, realmsg.c_str());cout << "realmsg:" <<realmsg << endl;cout << "buffer:" << buffer << endl;//转发给所有人,代码写到这儿了,还没有做好转发给所有人的功能for(auto it = (*client_map).begin(); it != (*client_map).end(); ++it){cout << "client_map.begin()->first=" << it->first << endl;// cout << "client_map.end()->first=" << (*client_map).end()->first << endl;printf("%s(%d):%s: server write buffer:%s\n", __FILE__, __LINE__, __FUNCTION__,buffer);printf("%s(%d):%s:sizeof(buffer):%d\n", __FILE__, __LINE__, __FUNCTION__,sizeof(*buffer));printf("%s(%d):%s:it->first:%d\n", __FILE__, __LINE__, __FUNCTION__,it->first);//这里的buffer是8个字节ssize_t write_result = write(it->first, buffer, BUFFER_SIZE);printf("(%d)write_result:%d \n", __LINE__ , write_result);if (write_result != BUFFER_SIZE) {printf("%s:server端写入失败\n",__FUNCTION__);}}}
}void writeFile(map<int,Client> *client_map,char buffer[],int fd){printf("%s(%d):buffer:%s\n",__FUNCTION__,__LINE__,buffer);char file_name[512] = "";char file_size[64];const char *d = "&&&&";char *p;//第一次:F 不管p = strtok(buffer,d);//第二次 文件名strcpy(file_name,strtok(NULL,d));printf("second time:%s:%s\n",__FUNCTION__,file_name);FILE *fp = fopen(file_name,"w");if(fp == NULL){printf("无法写文件%s\n",file_name);exit(1);}//第三次 文件大小strcpy(file_size,strtok(NULL,d));int len = atoi(file_size);printf("third time:%s:%d\n",__FUNCTION__,len);//第四次 文件内容strcpy(buffer,strtok(NULL,d));//buffer没有问题printf("forth time:%s:%s\n",__FUNCTION__,buffer);//写文件//fseek(fp,0,SEEK_SET);	int flag = fwrite(buffer, sizeof(char), len, fp);if(flag < 0){printf("写入文件%s失败\n", file_name);}	fclose(fp);
}void server103() {//设置参数int serv_sock, fileServ_sock ,cli_sock;struct sockaddr_in serv_addr, fileServ_addr ,cli_addr;socklen_t cliaddr_len = sizeof(cli_addr);//初始化serv_sock = socket(PF_INET, SOCK_STREAM, 0);serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("0.0.0.0");serv_addr.sin_port = htons(9527);//bindif (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {printf("bind failed!\n");}//listenlisten(serv_sock, 5);//使用epollepoll_event event;int enfd, event_cnt;enfd = epoll_create(100);if (enfd == -1) {printf("epoll create failed! \n%d:%s", errno, strerror(errno));close(serv_sock);return;}//需要多个epoll_event,搞100个吧epoll_event* all_event = new epoll_event[100];//对epoll做初始化,将服务端用epoll添加进来event.events = EPOLLIN;//printf("EPOLLIN:%d\n", EPOLLIN);//结果为EPOLLIN:1event.data.fd = serv_sock;epoll_ctl(enfd, EPOLL_CTL_ADD, serv_sock, &event);char buffer[BUFFER_SIZE] = "";  //消息传输的buffer大小printf("开始进入while循环 \n");//保存客户端的消息map<int, Client> client_map;//对客户端传入后的epoll处理while (true) {event_cnt = epoll_wait(enfd, all_event, 100, 1000);//这里的event_cnt就是事件发生的数量,连接算一个,发送数据算一个printf("event_cnt: %d\n", event_cnt); //结果为0 0 0 1(有操作的时候) 0 0 0 1(有操作时)//sleep(1);if (event_cnt == -1) {printf("epoll wait failed! \n%d:%s", errno, strerror(errno));close(serv_sock);return;}if (event_cnt == 0) continue;for (int i = 0; i < event_cnt; i++) {int fd = all_event[i].data.fd;cout << "fd=" << fd << endl;if ( fd == serv_sock) {cli_sock = accept(serv_sock, (struct sockaddr*)&cli_addr, &cliaddr_len);if(cli_sock < 0){printf("accept error\n");continue;}//将客户端的socket加入epollstruct epoll_event ev_client;ev_client.events = EPOLLIN;ev_client.data.fd = cli_sock;int ret = epoll_ctl(enfd, EPOLL_CTL_ADD, cli_sock, &ev_client);if(ret < 0){printf("epoll_ctl error\n");break;}//printf("%s正在连接",cli_addr.sin_addr.s_addr);//保存该客户端的信息struct Client client;client.sockfd = cli_sock;client.name = "";client_map[cli_sock] = client;printf("已连接用户: %s:%d\n all_event[i].data.fd = %d\n", inet_ntoa(cli_addr.sin_addr),htons(cli_addr.sin_port),client_map[cli_sock].sockfd);}//这里写的逻辑不对,但还不知道怎么改:有头绪了,用C和F区分开消息和文件else{//这里read中的socket要写成all_event[i].data.fd,不是serv_sock		memset(buffer, 0, sizeof(buffer));ssize_t len = read(fd, buffer, sizeof(buffer));if (len <= 0) {//客户端断开连接了(或程序发生错误了),关掉客户端// printf("client closed,len < 0(%d)!\n", __LINE__);client_map.erase(fd);	epoll_ctl(enfd, EPOLL_CTL_DEL, fd, NULL);close(fd);printf("client is closed! fd = %d \n", fd);break;}else{printf("%s(%d):%s read buffer:%s\n", __FILE__, __LINE__, __FUNCTION__, buffer);//输出的时候带上自己的名字,三个参数:client_map,fd,bufferif(buffer[0] == 'C'){writeText(&client_map,buffer,fd);}else if(buffer[0] == 'F'){writeFile(&client_map,buffer,fd);}}}}}delete[]all_event;close(serv_sock);}int main(int argc,char* argv[])
{server103();return 0;
}

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

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

相关文章

模拟实现string【C++】

文章目录 全部的实现代码放在了文章末尾准备工作包含头文件定义命名空间和类类的成员变量 构造函数默认构造拷贝构造 重载赋值拷贝函数析构函数迭代器和获取迭代器迭代器获取迭代器 resize【调整size】图解 reserve【调整capacity】empty【判断串是否为空】operator[]appendpus…

高中数学:复数-基础概念及运算法则

一、定义 规定 复数集与实数集之间的关系 二、复数的几何意义 第一种几何意义 第二种几何意义 复数向量的模 共轭复数 三、四则运算 加法 复向量加法 减法 两复数的距离 乘法 除法 四、总结 复数的所有运算法则和实数相同。 向量运算和实数向量运算相同。 怎么简便记忆了&a…

TTS 语音合成技术学习

TTS 语音合成技术 TTS&#xff08;Text-to-Speech&#xff0c;文字转语音&#xff09;技术是一种能够将文字内容转换为自然语音的技术。通过 TTS&#xff0c;机器可以“说话”&#xff0c;这大大增强了人与机器之间的互动能力。无论是在语音助手、导航系统还是电子书朗读器中&…

【iPaaS ESB】论企业在数据集成的抉择

随着信息化时代的到来&#xff0c;企业在发展过程中引入了众多且不协同的应用、系统和软件&#xff0c;每个系统都有着独立的信息&#xff0c;渐渐地出现数据信息不协同、数据集成异构的现象。因此企业对于数据的处理和分析需求也越来越多元及个性化。 在这样的背景下&#xf…

Java SE入门及基础(58) 并发 进程与线程概念

目录 并发 进程和线程 1. 进程和线程 2. 进程 3.线程 总结 并发 并发(Concurrency) Computer users take it for granted that their systems can do more than one thing at a time. They assume that they can continue to work in a word processor, while other app…

大模型火了一年半,AI还在「钻木取火」?

伴随着AI大模型的新一轮进化&#xff0c;这个夏天&#xff0c;人工智能正在引领一波新的热潮。 美国当地时间6月18日&#xff0c;AI大模型的主要显卡芯片供应商英伟达收涨3.51%&#xff0c;市值升至3.34万亿美元&#xff0c;一度超越微软和苹果等科技巨头&#xff0c;成为全球…

ElasticSearch中的BM25算法实现原理及应用分析

文章目录 一、引言二、BM25算法实现原理BM25算法的实现原理1. 词频&#xff08;TF&#xff09;&#xff1a;2. 逆文档频率&#xff08;IDF&#xff09;&#xff1a;3. 长度归一化&#xff1a;4. BM25评分公式&#xff1a; BM25算法示例 三、BM25算法在ElasticSearch中的应用分析…

在 Java 中的使用Selenium 测试框架

Selenium 测试框架&#xff1a;在 Java 中的使用 Selenium 测试框架就是这样一个强大的工具&#xff0c;它为 Web 应用的自动化测试提供了全面且高效的解决方案。 一、Selenium 简介 Selenium 是一个开源的自动化测试工具集&#xff0c;专门用于测试 Web 应用程序。它支持多…

「树莓派入门」树莓派基础05-文件传输及桌面环境配置

一、文件传输的配置方法 使用U盘传输文件 将文件存储到U盘中。将U盘插入树莓派的USB端口。挂载U盘&#xff08;通常自动挂载&#xff09;。使用 cp 命令或图形界面将文件从U盘复制到树莓派。 使用VNC进行文件传输 开启树莓派的VNC服务&#xff1a; sudo raspi-config在 rasp…

数据结构:队列详解 c++信息学奥赛基础知识讲解

目录 一、队列概念 二、队列容器 三、队列操作 四、代码实操 五、队列遍历 六、案例实操 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 详细代码&#xff1a; 一、队列概念 队列是一种特殊的线性…

【单片机毕业设计选题24032】-基于STM32的电瓶车电池检测系统

系统功能: 系统上电后显示“欢迎使用电池检测系统请稍后”后两秒后正常显示界面 第一页面第一行显示“系统状态信息” 第二行显示获取到的电压值 第三行显示获取到的电流值 第四行显示获取到的温度和剩余电量值 短按B4按键可切换到第二页面 第二页面第一行显示“温度阈值…

2020年全国大学生数学建模竞赛C题中小微企业信贷决策(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码&#xff08;一&#xff09;数据处理代码&#xff08;二&#xff09;熵权法与TOPSIS代码&#xff08;三&#xff09;最小二乘法代码&#xff08;四&#xff09;粒子群代码 四、完整word版论文和源代码&#xff08;两种获取方式…

Nest 的 IoC 机制

后端系统中&#xff0c;会有很多对象&#xff1a; Controller 对象&#xff1a;接收 http 请求&#xff0c;调用 Service&#xff0c;返回响应 Service 对象&#xff1a;实现业务逻辑 Repository 对象&#xff1a;实现对数据库的增删改查 此外&#xff0c;还有数据库链接对…

Python 希尔排序

希尔排序&#xff08;Shell Sort&#xff09;是一种基于插入排序的算法&#xff0c;它通过引入增量序列来改进插入排序的性能。希尔排序的基本思想是将原始数据分成多个子序列&#xff0c;每个子序列的元素之间相隔某个增量d&#xff0c;然后对每个子序列进行插入排序。随着增量…

内外网文件流转场景日益复杂,看麒麟信安如何构筑安全防线?

随着信息化快速发展&#xff0c;数据已成为企业核心资产&#xff0c;根据信息安全分级保护和等级保护的相关要求&#xff0c;诸多单位都采取了内外网隔离措施以确保信息安全。但在管理内外部数据流通时&#xff0c;用户单位在集中加密存储、文件流转管理机制、外带文件审批管理…

超详细的Linux Conda环境安装教程

前言 在现代开发过程中&#xff0c;环境管理是确保项目顺利进行的关键之一。不同的项目可能需要不同的库和依赖版本&#xff0c;而直接在系统级别安装这些依赖往往会导致冲突和混乱。为了解决这个问题&#xff0c;Conda 应运而生。 Conda 是一个开源的软件包管理系统和环境管理…

AI原力觉醒:华硕NUC组团出道,快来Pick属于你的NUC

NUC 家族组团出道&#xff0c;全新的计算体验&#xff0c;重新定义桌面设备。AI加持下&#xff0c;谁最适合你&#xff1f; 颜值担当 NUC 14 Pro 居家必备单品 适用于广大消费者的NUC 14 Pro&#xff0c;不仅颜值在线&#xff0c;更多方位考虑您的日常所需&#xff0c;工作娱…

2024/6/28 英语每日一段

The Supreme Court on Thursday rejected a challenge to an obscure provision of President Donald Trump’s 2017 tax package, ending a lawsuit that many experts feared could destabilize the nation’s tax system. In a divided decision, the court upheld a one-ti…

基于SpringBoot养老院管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

Java网络编程(JavaWeb的基础)

Java网络编程&#xff08;JavaWeb的基础&#xff09; 文章目录 Java网络编程&#xff08;JavaWeb的基础&#xff09;前言一、网络编程概述1.1 软件架构&网络基础1.2 网络通信要素:IP/端口/通信协议1.3 传输层协议:tcp/udp 二、网络编程API2.1 InetAddress类2.2 Socket类&am…