一、概要设计

  登录对话框(继承自QDialog类)进行用户登录查询数据库用户是否存在,注册插入数据到用户表。用户表字段:

(chatid int primary key, passwd varchar(30), name varchar(30), email varchar(30), history int)

显示好友列表(继承自QWidget类),窗体间数据传递,显示登录用户头像及昵称。轮询数据库用户表显示好友列表。点击好友跳出聊天窗口(继承自MainWindow类),窗体间数据传递,显示好友昵称,实现多用户聊天,支持中文通信,设置快捷键,保存聊天记录。

关键字:数据库、窗体传值、中文通信、udp协议、快捷键、文件操作。

二、详细设计

  1.登录对话框:

  数据库操作:

  (1)创建数据库和数据表

// 设置参数
QString select_table = "select tbl_name name from sqlite_master where type = 'table'";
QString create_sql = "create table user (chatid int primary key, passwd varchar(30), name varchar(30), email varchar(30), history int)";
QString select_max_sql = "select max(chatid) from user";
QString insert_sql = "insert into user values (?, ?, ?, ? ?)";
//QString update_sql = "update user set name = :name where chatid = :chatid";
QString select_sql = "select name from user";
//QString select_all_sql = "select * from user";
//QString delete_sql = "delete from user where chatid = ?";
//QString clear_sql = "delete from user"; QString select_nameInfo = "selcet * from user where name="; database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName("database.db"); //打开数据库
if(!database.open())
{
qDebug()<<database.lastError();
qFatal("failed to connect.") ;
}
else
{
qDebug()<<"open success";
QSqlQuery sql_query; //变量必须在成功打开数据库后定义才有效
sql_query.prepare(select_table);
if(!sql_query.exec())
{
qDebug()<<sql_query.lastError();
}
else
{
QString tableName;
while(sql_query.next())
{
tableName = sql_query.value(0).toString();
qDebug()<<tableName;
if(tableName.compare("user"))
{
tableFlag=false;
qDebug()<<"table is not exist";
}
else
{
tableFlag=true;
qDebug()<<"table is exist";
}
}
}
// 创建数据表
if(tableFlag==false)
{
sql_query.prepare(create_sql);
if(!sql_query.exec())
{
qDebug()<<sql_query.lastError();
}
else
{
qDebug()<<"table created!";
}
} //database.close();
}

  (2)注册验证及插入数据

if(ui->passwd1LineEdit->text()==""||ui->passwd2LineEdit->text()=="")
{
passwdFlag=false;
}
else if(ui->passwd1LineEdit->text()==ui->passwd2LineEdit->text()) //两次密码相同
{
//newpasswd=ui->passwd1LineEdit->text();
passwdFlag=true;
}
else
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("密码输入不一致!"));
qDebug()<<"passwd err";
passwdFlag=false;
//return;
} //以下为数据库的操作
QSqlQuery sql_query; //查询最大id
max_id = 0;
sql_query.prepare(select_max_sql);
if(!sql_query.exec())
{
qDebug()<<sql_query.lastError();
}
else
{
while(sql_query.next())
{
max_id = sql_query.value(0).toInt();
qDebug()<<QString("max chatid:%1").arg(max_id);
}
} //查询部分数据(name)
if(!sql_query.exec(select_sql))
{
qDebug()<<sql_query.lastError();
}
else
{
while(1)
{
if(sql_query.next()) //name有数据
{
QString name = sql_query.value("name").toString();
qDebug()<<QString("name=%1").arg(name); if(ui->nameLineEdit->text()==name) //用户名已经存在
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("用户名已存在!"));
qDebug()<<"name existed";
nameFlag=false;
break;
}
else
{
//newname=ui->nameLineEdit->text();
nameFlag=true;
}
}
else
{ //name列为空
nameFlag=true;
break;
}
}
} newchatid=max_id+1;
if(nameFlag==true) newname=ui->nameLineEdit->text();
else return;
if(passwdFlag==true) newpasswd=ui->passwd1LineEdit->text();
else return; //插入数据
sql_query.prepare(insert_sql);
sql_query.addBindValue(newchatid); //chatid
sql_query.addBindValue(newpasswd); //passwd
sql_query.addBindValue(newname); //name
sql_query.addBindValue(newemail); //email
sql_query.addBindValue(0); //history
if(!sql_query.exec())
{
qDebug()<<sql_query.lastError();
}
else
{
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("注册成功!"));
qDebug()<<"inserted!";
}

  登录界面:

  (1)登录验证

if(matchFlag==false)
{
//用户名错误
QMessageBox::warning(this, tr("警告"), tr("用户不存在!"), QMessageBox::Yes);
this->ui->et_username->clear();
this->ui->et_pwd->clear();
this->ui->et_username->setFocus();
}
else
{
if(usr_passwd!=ui->et_pwd->text())
{
//密码错误
QMessageBox::warning(this, tr("警告"), tr("用户不存在!"), QMessageBox::Yes);
this->ui->et_username->clear();
this->ui->et_pwd->clear();
this->ui->et_username->setFocus();
}
else
{
//用户名和密码均正确
// ChatWindow cw(this);
// this->hide();
// cw.show();
// cw.exec();
// this->close();
LoginDialog::NICKNAME = usr_name;
accept();
}

  (2)用户头像

QSqlQuery sql_query;        //变量必须在成功打开数据库后定义才有效

    //查询部分数据(name)

    QString tempstring="select * from user where name='"+name+"'";
qDebug()<<tempstring;
if(!sql_query.exec(tempstring))
{
qDebug()<<sql_query.lastError();
matchFlag=false;
}
else
{
while(sql_query.next())
{
usr_id = sql_query.value(0).toInt();
usr_passwd = sql_query.value(1).toString();
usr_name = sql_query.value(2).toString();
usr_email = sql_query.value(3).toString();
usr_history = sql_query.value(4).toInt(); qDebug()<<QString("chatid=%1 passwd=%2 name=%3 email=%4 history=%5").arg(usr_id).arg(usr_passwd).arg(usr_name).arg(usr_email).arg(usr_history);
}
if(usr_name==name) matchFlag=true;
else matchFlag=false;
} qDebug()<<matchFlag;
if(matchFlag==true)
{
QString path=":/images/facex.jpg";
QString diff="face"+QString::number(usr_id);
path.replace("facex",diff);
qDebug()<<path; QImage img;
img.load(path);
QPixmap pic=QPixmap::fromImage(img.scaled(ui->userPic->width(),ui->userPic->height()));
ui->userPic->setPixmap(pic);
}
else
{ QPixmap pic;
ui->userPic->setPixmap(pic);
}

  2.好友列表:

  数据库查询及列表显示:

database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName("database.db"); QSqlQuery sql_query; //改变量必须在成功打开数据库后定义才有效 //打开数据库
if(!database.open())
{
qDebug()<<database.lastError();
qFatal("failed to connect.") ;
}
else
{ QSqlQuery query; //改变量必须在成功打开数据库后定义才有效
QString execstring="select * from user"; ctoolTip = new CToolTip();
// 定义全局的ToolTip,方便使用
g_toolTip = ctoolTip; // 本行代码主要针对ListWidgetItem右键点击时才生效的
ui->listWidget->setMouseTracking(true); if(!query.exec(execstring))
{
qDebug()<<QString("chatid");
qDebug()<<query.lastError();
// matchFlag=false;
}
else
{
// 添加测试数据
while (query.next()) {
usr_id = query.value(0).toInt();
usr_passwd = query.value(1).toString();
usr_name = query.value(2).toString();
usr_email = query.value(3).toString();
usr_history = query.value(4).toInt(); qDebug()<<QString("chatid=%1 passwd=%2 name=%3 email=%4 history=%5").arg(usr_id).arg(usr_passwd).arg(usr_name).arg(usr_email).arg(usr_history); ItemWidget *itemWidget = new ItemWidget(ui->listWidget);
itemWidget->setText(QPixmap(QString(":/images/face%1").arg(usr_id)).scaled(80, 80),
QString("%1").arg(usr_name), QString("127.0.0.1:800%1").arg(usr_id));
QListWidgetItem *listItem = new QListWidgetItem(ui->listWidget);
// 此处的size如果不设置,界面被压缩了看不出ItemWidget的效果,高度一定要设置
listItem->setSizeHint(QSize(200, 70));
ui->listWidget->setItemWidget(listItem, itemWidget); }
}
}
QObject::connect(ui->listWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(conChat(QListWidgetItem*)));
qDebug()<<"LoginWidget:"+LoginDialog::NICKNAME;
ui->nickname->setText(LoginDialog::NICKNAME);
QString tempstring="select * from user where name='"+LoginDialog::NICKNAME+"'";
qDebug()<<tempstring;
if(!sql_query.exec(tempstring))
{
qDebug()<<sql_query.lastError();
matchFlag=false;
}
else
{
while(sql_query.next())
{
usr_id = sql_query.value(0).toInt();
usr_passwd = sql_query.value(1).toString();
usr_name = sql_query.value(2).toString();
usr_email = sql_query.value(3).toString();
usr_history = sql_query.value(4).toInt(); qDebug()<<QString("chatid=%1 passwd=%2 name=%3 email=%4 history=%5").arg(usr_id).arg(usr_passwd).arg(usr_name).arg(usr_email).arg(usr_history); }
if(usr_name==LoginDialog::NICKNAME) matchFlag=true;
else matchFlag=false;

  登录用户信息设置:

QString path=":/images/facex.jpg";
QString diff="face"+QString::number(usr_id);
path.replace("facex",diff);
qDebug()<<path; QImage img;
img.load(path);
QPixmap pic=QPixmap::fromImage(img.scaled(ui->userPic->width(),ui->userPic->height()));
ui->userPic->setPixmap(pic);
ui->ipaddress->setText(QString("127.0.0.1:800%1").arg(usr_id));

  列表项:

    labelIcon = new QLabel(this);
labelName = new QLabel(this);
labelName->setStyleSheet("QLabel{color: green; font: 23pt bold;}");
labelInfo = new QLabel(this);
labelInfo->setStyleSheet("QLabel{color: gray;}"); verlayout = new QVBoxLayout();
verlayout->setContentsMargins(0, 0, 0, 0);
verlayout->addWidget(labelName);
verlayout->addWidget(labelInfo); horLayout = new QHBoxLayout(this);
horLayout->setContentsMargins(2, 2, 2, 2);
horLayout->addWidget(labelIcon, 1, Qt::AlignTop);
horLayout->addLayout(verlayout, 4);

  悬浮窗口:

    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
this->resize(200, 100); ; this->setObjectName("CToolTip");
this->setStyleSheet("QWidget#CToolTip {border: 2px solid green; background-color: skyblue;}"); groupBox = new QGroupBox(this);
groupBox->setGeometry(10, 10, 180, 80);
groupBox->setTitle("用户信息"); labelIcon = new QLabel(groupBox);
labelName = new QLabel(groupBox);
labelInfo = new QLabel(groupBox); verlayout = new QVBoxLayout();
verlayout->setContentsMargins(0, 0, 0, 0);
verlayout->addWidget(labelName);
verlayout->addWidget(labelInfo); horLayout = new QHBoxLayout(groupBox);
horLayout->setContentsMargins(10, 10, 10, 10);
horLayout->addWidget(labelIcon, 1, Qt::AlignTop);
horLayout->addLayout(verlayout, 4);

  选择列表项:

QObject::connect(ui->listWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(conChat(QListWidgetItem*)));

void LoginWidget::conChat(QListWidgetItem*)
{
qDebug()<<QString("onChat:%1").arg(usr_id);
LoginWidget::ID = ui->listWidget->currentRow()+1;
chatWindow=new ChatWindow;
chatWindow->show();
}

  3.聊天窗口:

  数据库查询、信息设置及绑定端口号:

database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName("database.db"); QSqlQuery chat_query; //变量必须在成功打开数据库后定义才有效 //打开数据库
if(!database.open())
{
qDebug()<<database.lastError();
qFatal("failed to connect.") ;
}
else
{
QString tempstring="select * from user where name='"+LoginDialog::NICKNAME+"'";
qDebug()<<tempstring;
if(!chat_query.exec(tempstring))
{
qDebug()<<chat_query.lastError();
matchFlag=false;
}
else
{
while(chat_query.next())
{
usr_id = chat_query.value(0).toInt();
usr_passwd = chat_query.value(1).toString();
usr_name = chat_query.value(2).toString();
usr_email = chat_query.value(3).toString();
usr_history = chat_query.value(4).toInt(); qDebug()<<QString("chatid=%1 passwd=%2 name=%3 email=%4 history=%5").arg(usr_id).arg(usr_passwd).arg(usr_name).arg(usr_email).arg(usr_history);
port = 8000+usr_id;
}
}
QString receiverId="select * from user where chatid="+QString("%1").arg(LoginWidget::ID)+"";
qDebug()<<receiverId;
if(!chat_query.exec(receiverId))
{
qDebug()<<chat_query.lastError();
matchFlag=false;
}
else
{
while(chat_query.next())
{
usr_id = chat_query.value(0).toInt();
usr_passwd = chat_query.value(1).toString();
usr_name = chat_query.value(2).toString();
usr_email = chat_query.value(3).toString();
usr_history = chat_query.value(4).toInt(); qDebug()<<QString("chatid=%1 passwd=%2 name=%3 email=%4 history=%5").arg(usr_id).arg(usr_passwd).arg(usr_name).arg(usr_email).arg(usr_history);
ui->name->setText(usr_name);
}
}

  UDP接收端:

senderAno = new QUdpSocket(this);
receiver = new QUdpSocket(this);
receiver->bind(port, QUdpSocket::ShareAddress);
connect(receiver, &QUdpSocket::readyRead, this, &ChatWindow::processPendingDatagram); void ChatWindow::processPendingDatagram()
{
//中文支持
// QTextCodec *codec = QTextCodec::codecForName("GBK");
// 拥有等待的数据报
while(receiver->hasPendingDatagrams())
{
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
QString str = time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式
QByteArray datagram; // 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
datagram.resize(receiver->pendingDatagramSize()); // 接收数据报,将其存放到datagram中
receiver->readDatagram(datagram.data(), datagram.size());
//ui->label->setText(datagram);
ui->listWidget->addItem(str);
chat += str+"\n";
qDebug()<<datagram<<QString::fromLocal8Bit(datagram);
ui->listWidget->addItem(usr_name+":"+QString::fromLocal8Bit(datagram));
chat += usr_name+":"+datagram+"\n";
}
}

  UDP发送端:

//中文支持
// QTextCodec *codec = QTextCodec::codecForName("GBK");
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
QString str = time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式
int port = LoginWidget::ID+8000;
qDebug()<<port;
QByteArray datagram = ui->textEdit->toPlainText().toUtf8(); senderAno->writeDatagram(datagram.data(), datagram.size(),
QHostAddress("127.0.0.1"), port);
ui->listWidget->addItem(str);
chat += str+"\n";
qDebug()<<ui->textEdit->toPlainText()<<QString::fromLocal8Bit(ui->textEdit->toPlainText().toUtf8());
ui->listWidget->addItem("me:"+ui->textEdit->toPlainText());
chat += "me:"+ui->textEdit->toPlainText()+"\n";
this->ui->textEdit->clear();
this->ui->textEdit->setFocus();

  快捷键设置:

ui->pushButton->setShortcut(tr("ctrl+return"));
ui->pushButton_3->setShortcut(tr("alt+c"));
ui->pushButton_8->setShortcut(tr("ctrl+s"));

  文件操作:

/*
* 通过QFile实现数据操作
*/
qDebug()<<tr("Save File");
qDebug()<<chat;
QFile file("/Users/apple/Documents/QT/ChatLog.txt");
// 以只写方式打开,如果文件不存在,那么会创建该文件
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
qDebug() << file.errorString();
file.write(chat);
file.close();
QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("聊天记录保存成功!"));

  4.窗体传值:

  使用QT中的Signal&Slot机制进行传值:

  QT中的Signal&Slot机制相比于MFC中的消息机制简单了许多,它保证了任何对象之间均可以通过这种方式进行通信,甚至可以得到消息的sender。这里就拿一个简单的窗体间传值作为例子。

首先看一下主窗体MainWindow:

在设计器中拖拽一个Label和一个TextEdit控件到界面上,TextEdit用于显示传递过来的数据。

  创建一个右下有两个按键的对话框,放置一个Label和一个LineEdit。

  下面就是编码的操作了,我们需要在Dialog中声明一个信号,当用户点击OK时传递LineEdit中的内容到mainWindow中,具体的dialog.h代码为:

    #ifndef DIALOG_H
#define DIALOG_H #include <QDialog> namespace Ui {
class Dialog;
} class Dialog : public QDialog
{
Q_OBJECT public:
explicit Dialog(QWidget *parent = 0);
~Dialog(); private:
Ui::Dialog *ui;
signals:
void sendData(QString);
private slots:
void on_buttonBox_accepted();
}; #endif // DIALOG_H

  其中的signals:void sendData(QString)便是我们需要的信号函数,同时声明了一个槽函数

void on_buttonBox_accepted();用于相应确定按钮的click事件。下面就是需要在该函数中产生一个信号。代码如下:

    void Dialog::on_buttonBox_accepted()
{
emit sendData(ui->lineEdit->text());
}

  代码异乎寻常的简单,只需要用emit的方式调用sendData函数,将需要的参数传递进去即可。而MainWindow中则需要声明接收的槽函数,注意槽函数参数只能与信号函数少或相等,而不能多于信号函数参数个数。在MainWindow的头文件中声明槽函数:

    private slots:
void receiveData(QString data);

  为了便于测试,我只在MainWindow的构造函数中创建了一个Dialog对象,并连接了信号和槽,具体为:

    MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//信号槽方式下父子窗体传值的测试
Dialog *dlg = new Dialog;
//关联信号和槽函数
connect(dlg,SIGNAL(sendData(QString)),this,SLOT(receiveData(QString)));
// dlg->setModal(true); 不论是模态或者非模态都可以正常传值
dlg->show();
}

  这里,我没有将父窗口的指针传递到Dialog中,如new Dialog(this),这种方式下,实际上可以归结到第三类传值方式中去。因为此时,可以使用MainWindow中的父窗口的函数进行数据的赋值和操作。

这里,可能还有一个问题就是,父窗口如何给子窗口传值,一方面,仍然可以使用信号和槽的方式进行,但是,我感觉更便利的方式倒是使用这种public接口的方式进行传值。这种来的更直接和明显。当然,可以看出Signal&Signal方式进行此类的处理会更有通用性。

在receiveData(QString)的槽函数中进行接收到数据的处理,这里仅仅进行了简单的显示:

    void MainWindow::receiveData(QString data)
{
ui->textEdit->setText(data);
}

  最后看下结果:

  

  最终的结果,因为信号和槽可以是多对多的,所以,在类似多个窗体广播信息时,这种方式就很有用,当然也可以使用全局变量的形式。

   使用全局变量;

使用public形式的函数接口;

使用QT中的Event机制(这种没有把握,但是感觉应该是可以的),但是实现起来应该比前几种复杂,这里不做讨论。

   5.中文支持:

  网上搜索一下,找到的都是这种:

#include < QTextCodec >
int main(int argc, char **argv)
{
....................
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF8"));
..........................
}

  Qt5中, 取消了QTextCodec::setCodecForTr()和QTextCodec::setCodecForCString()这两个函数,而且网上很多都是不推荐这种写法。

有一下几种转换方法:

1、

QTextCodec * BianMa = QTextCodec::codecForName ( "GBK" );

    QMessageBox::information(this, "提示", BianMa->toUnicode("中文显示!"));

2、也可以通过QString定义的静态函数,先转换成Unicode类型:

QString::fromLocal8Bit("提示")

3、在Qt5中,提供了一个专门的处理宏,来支持中文常量,那就是QStringLiteral,但它只能处理常量。

QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("中文显示"));const char* info = "中文显示";
//不支持
QString strInfo = QStringLiteral(info);
//支持
QString strInfo = QString::fromLocal8Bit(info);

QByteArray QString::toLatin1() const

  Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。
  ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。

  toLatin1压缩掉了QString自动给每个英文字符加上的那些00字节.

QString与QByteArray互相转换的方法

  QString转QByteArray方法

//Qt5.3.2
QString str("hello");
QByteArray bytes = str.toUtf8(); // QString转QByteArray方法1 QString str("hello");
QByteArray bytes = str.toLatin1(); // QString转QByteArray方法2

  QByteArray转QString方法

//Qt5.3.2
QByteArray bytes("hello world");
QString string = bytes; // QByteArray转QString方法1 QByteArray bytes("hello world");
QString string;
string.prepend(bytes);// QByteArray转QString方法2 qDebug() << string;

  QByteArray类同样不以’\0’为结尾:如

QByteArray bytes;
bytes.resize(5);
bytes[0] = '1';
bytes[1] = '2';
bytes[2] = '3';
bytes[3] = '\0';
bytes[4] = 'a'; cout << bytes << endl;

三、结果图

登录界面:

好友列表:

注册界面:

聊天窗口:

聊天记录:

QT开发之旅-Udp聊天室编程的更多相关文章

  1. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  2. XMPPFrameWork IOS 开发(六)聊天室

    原始地址:XMPPFrameWork IOS 开发(六)聊天室 聊天室 //初始化聊天室 XMPPJID *roomJID = [XMPPJID jidWithString:ROOM_JID]; xm ...

  3. 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室

    原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  4. Java Socket聊天室编程(二)之利用socket实现单聊聊天室

    这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...

  5. Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...

  6. Qt NetWork即时通讯网络聊天室(基于TCP)

    本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能: 1.基于TCP的可靠连接(QTcpServer.QTcpSocket) 2.一个服务器,多个客户端 3.服务器接收到某个客户端的请求 ...

  7. QT开发之旅一DS7400主机调试工具

    接触QT三年有余,期间因为工作需要断断续续学习过,2010年开始接触,当时好像是4.7版本,现在都已经到5.2版本了,更新真快,前阵子安装了下5.2版本,还是有很多变化的,不过感觉好像编译速度慢了很多 ...

  8. 基于Websocket开发的仿微信聊天室

    一.运行环境及涉及技术:----------------------------------* Visual Studio 2019* SQL SERVER 2008 R2* .Net FrameWo ...

  9. java 开发 websocket 网页端聊天室

    博客地址:https://ainyi.com/67 WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端. ...

随机推荐

  1. Google Chrome Developer Tools

    原文:https://www.oschina.net/p/chromedevtools Google发布了Google Chrome Developer Tools,这是一系列面向Chrome开发者的 ...

  2. dstat用法;利用awk求dstat所有列每列的和;linux系统监控

    安装:yum install -y dstat dstat命令是一个用来替换vmstat.iostat.netstat.nfsstat和ifstat这些命令的工具,是一个全能系统信息统计工具.与sys ...

  3. 中国第二代身份证验证js代码

    以下这部分代码截取自盛大的某个网页.详细我就不给url了.以下是相应的js代码: iW = new Array(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1); iSum ...

  4. The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory的解决方法

    An error occurred at line: [31] in the generated java file: [/data/tmisnt/work/Catalina/localhost/_/ ...

  5. hdu 5361 2015多校联合训练赛#6 最短路

    In Touch Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total ...

  6. 2016/1/22 3,将id为005的对象从集合中移除

    package shuzu; public class Emp { private String id; private String name; public Emp(String id, Stri ...

  7. the largest value you actually can transmit between the client and server is determined by the amount of available memory and the size of the communications buffers.

    the largest value you actually can transmit between the client and server is determined by the amoun ...

  8. Open Source Computer Vision Library

    https://opencv.org/ OpenCV (Open Source Computer Vision Library) is released under a BSD license and ...

  9. redis集群在window下安装

    1.下载安装单机版:  https://github.com/MSOpenTech/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.msi ...

  10. mysql/sql server和java之间的数据类型对应关系

    Mysql************************************当前列 ClassName ColumnType DisplaySize TypeName0: java.lang.I ...