zoukankan      html  css  js  c++  java
  • QT开发之旅-Udp聊天室编程

    一、概要设计

      登录对话框(继承自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+"
    ";
            qDebug()<<datagram<<QString::fromLocal8Bit(datagram);
            ui->listWidget->addItem(usr_name+":"+QString::fromLocal8Bit(datagram));
            chat += usr_name+":"+datagram+"
    ";
        }
    }
    

       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+"
    ";
        qDebug()<<ui->textEdit->toPlainText()<<QString::fromLocal8Bit(ui->textEdit->toPlainText().toUtf8());
        ui->listWidget->addItem("me:"+ui->textEdit->toPlainText());
        chat += "me:"+ui->textEdit->toPlainText()+"
    ";
        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类同样不以’’为结尾:如

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

    三、结果图

    登录界面:

    好友列表:

    注册界面:

    聊天窗口:

    聊天记录:

     

  • 相关阅读:
    Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
    Clustered Index
    Docker:一、开始部署第一个Asp.net应用
    数据库死锁 之 三.死锁解读
    数据库死锁 之 二.定位死锁
    数据库死锁 之 一.啥是死锁
    番外篇
    C# QQ & 163 邮件发送
    asp.net core 四 IOC&DI Autofac
    asp.net core 五 SignalR 负载均衡
  • 原文地址:https://www.cnblogs.com/sirius-swu/p/6919924.html
Copyright © 2011-2022 走看看