一、概要设计

  登录对话框(继承自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. 快速提取windows备份

    windows7的备份功能,还是有很多可取之处的. 其功能的本质是将电脑的分区做成VHD影像文件. 所以,如果你熟悉VHD,可以直接挂载VHD影像,提取文件或恢复系统,或者进入winpe下操作更加简单 ...

  2. HDU 4983 Goffi and GCD(数论)

    HDU 4983 Goffi and GCD 思路:数论题.假设k为2和n为1.那么仅仅可能1种.其它的k > 2就是0种,那么事实上仅仅要考虑k = 1的情况了.k = 1的时候,枚举n的因子 ...

  3. Codeforces Round #271 (Div. 2) D. Flowers (递推 预处理)

    We saw the little game Marmot made for Mole's lunch. Now it's Marmot's dinner time and, as we all kn ...

  4. Myeclipse的优化方法

    近期在实习,公司给分配了新的电脑,可是不知道怎么弄得,总是弄得非常卡,没办法仅仅有自己好好整理一下电脑了,另外.为了提高编程的效率.顺便也把Myeclipse也优化了一下. 第一步: 取消自己主动va ...

  5. busybox的使用

    1 将busybox设置为静态链接,放在文件系统中使用 make menuconfig的时候,Busybox Settings --> Build Options --> Build Bu ...

  6. C中 数组和指针的异同

    数组在很多情况下是和指针等价的,数组的下标运算和指针的解引用也有等价形式:arr[i] == *(arr + 1):但是也有一些情况下数组和指针是不一样的:extern int arr[]; exte ...

  7. ios32---线程的状态

    // // ViewController.m // 04-了解-线程的状态 // // 创建线程是处于新建状态,start是就绪状态,会放入到可调度线程池里面(cpu看线程是否可以调用,是看线程是否在 ...

  8. 2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) S Surf

    SurfNow that you've come to Florida and taken up surng, you love it! Of course, you've realized that ...

  9. SQLALchemy之ORM操作

    1.仍然要创建引擎 2.创建session会话 (1)方式一 engine =create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s ...

  10. 阿里Java开发规约笔记

    借助阿里开发规约,回顾一下Java开发编码基础方面的知识,结合自己使用中遇到的问题,记录一下规约中以前翻过的错.有共鸣的问题. 1.覆写方法时要加上@Override注解.重写一个类型T的equals ...