自学之路day03---qt小项目

四、网络聊天室

原理:基于tcp协议的网络聊天室,实现服务器端和客户端之间的通信。
需求:利用tcpSocket套接字实现服务器和客户端的通信。
如何实现通讯?
服务器:
一、头文件

  1. 服务器界面(利用qtcreator画出来),如图所示:
    在这里插入图片描述
  2. 在头文件中声明套接字,服务器端口,已经保存通信套接字的容器
    QTcpServer tcpServer;//TCP服务器
    quint16 port;//服务器端口
    //列表容器:保存和客户端通信的套接字
    QList<QTcpSocket*> tcpClientList;
  3. 增加以下槽函数:
    ① 、void onNetConnect():用于响应客户端的连接请求
    ② 、void onReadyRead :用于接收聊天消息的槽函数
    ③ 、创建服务器按钮的槽函数;
    ④ 、成员函数SendMessage();

二、函数的编写

1.构造函数

建立newConnect和onNetConnect槽函数的连接—当有客户端发送连接请求时,tcpServer会发送信号newConnect

{
    ui->setupUi(this);
    //当有客户端发送连接请求时,tcpServer发送信号newConnection
    connect(&tcpServer,SIGNAL(newConnection()),
            this,SLOT(onNewConnection()));

    //每当定时器到时,将发送到时信号timeout
    connect(&timer,SIGNAL(timeout()),
            this,SLOT(onTimeout()));
}
2.槽函数的编写

①.创建服务器按钮
获取端口号、设置监听服务器IP和端口号、禁止使用

//创建服务器按钮对应的槽函数
void ServerDialog::on_pushButton_clicked()
{
    //获取配置的服务器端口
    port = ui->lineEdit->text().toShort();
    //设置监听服务器IP和端口
    if(tcpServer.listen(QHostAddress::Any,port)){
        qDebug() << "服务器创建成功!";
        //禁用创建按钮和端口输入配置
        ui->pushButton->setEnabled(false);
        ui->lineEdit->setEnabled(false);
        //开启定时器,每隔3秒时间检查一次客户端连接状态
        timer.start(3000);
    }
    else{
        qDebug() << "服务器创建失败!";
    }
}

②.响应客户端连接的槽函数
获取即将连接的客户端套接字、将套接字保存到容器、将readyRead信号连接到相应的槽函数

//响应客户端连接请求的槽函数
void ServerDialog::onNewConnection()
{
    //获取和客户端通信的套接字
    QTcpSocket* clientSocket =
            tcpServer.nextPendingConnection();
    //保存和客户端通信的套接字到容器
    tcpClientList.append(clientSocket);
    //当客户端给服务器发送消息时,发送信号readyRead
    connect(clientSocket,SIGNAL(readyRead()),
            this,SLOT(onReadyRead()));
}

注:readyRead:This signal is emitted once every time new data is available for reading from the device’s current read channel.
③.接收聊天消息的槽函数
检查连接是够还在、Buffer空间的创建、将信息发送给所有客户端

//接收聊天消息的槽函数
void ServerDialog::onReadyRead()
{
    //遍历检查哪个客户端发的消息
    for(int i=0;i<tcpClientList.size();i++){
        //at(i):获取容器中第i个套接字
        //bytesAvailable:获取当前套接字等待读取消息的
        //字节数,如果是0说明没有消息,如果>0说明消息等待接收
        if(tcpClientList.at(i)->bytesAvailable()){
            //读取消息
            QByteArray buf =
                tcpClientList.at(i)->readAll();
            //显示聊天消息
            ui->listWidget->addItem(buf);
            ui->listWidget->scrollToBottom();
            //转发聊天消息给所有客户端
            sendMessage(buf);
        }
    }
}

④. 成员函数SendMessage()
转发聊天消息的成员函数

//转发聊天消息的成员函数
void ServerDialog::sendMessage(
        const QByteArray& msg)
{
    for(int i=0;i<tcpClientList.size();i++){
        tcpClientList.at(i)->write(msg);
    }
}
//定时器检查和客户端通信的套接字连接状态的槽函数
void ServerDialog::onTimeout(void){
    //遍历检查
    //state():获取套接字连接状态
    //UnconnectedState:表示未连接状态
    //removeAt(i):将第i套接字从容器中删除
    for(int i=0;i<tcpClientList.size();i++){
        if(tcpClientList.at(i)->state() ==
            QAbstractSocket::UnconnectedState){
            tcpClientList.removeAt(i);
            i--;
        }
    }
}

客户端
聊天消息的成员函数
一、头文件

1.界面

在这里插入图片描述
完成对每个构建的不同命名,方便后续使用

2.在头文件中添加:

连接状态,服务器IP、端口、昵称以及套接字的声明

3.槽函数声明

连接按钮、发送按钮、连接成功conneced信号对应槽函数
离开disconnected信号对应槽函数、readyread、error
二、函数的编写

1.构造函数

建立四个连接,初始化状态

{
    ui->setupUi(this);
    //初始化客户端离线状态
    status = false;
    //和服务器连接成功时,发送信号connected
    connect(&tcpSocket,SIGNAL(connected()),
            this,SLOT(onConnected()));
    //和服务器断开连接时,发送信号disconnected
    connect(&tcpSocket,SIGNAL(disconnected()),
            this,SLOT(onDisconnected()));
    //当收到服务器转发消息时,发送信号readyRead
    connect(&tcpSocket,SIGNAL(readyRead()),
            this,SLOT(onReadyRead()));
    //网络通信异常时,发送信号error
    connect(&tcpSocket,SIGNAL(error(
                QAbstractSocket::SocketError)),
            this,SLOT(onError()));
}

2.槽函数

①.连接按钮

//连接服务器按钮对应的槽函数
void ClientDialog::on_connectButton_clicked()
{
    //如果当前是离线状态,则建立和服务器连接
    if(status == false){
        //获取配置的服务器的IP
        if(serverIp.setAddress(
            ui->serverIpEdit->text()) == false){
            //如果配置ip格式错误,则弹出消息提示框
            QMessageBox::critical(this,"ERROR",
                        "服务器IP地址格式错误!");
            return;
        }
        //获取配置的服务器端口号
        serverPort =
            ui->serverPortEdit->text().toShort();
        if(serverPort<1024){
            QMessageBox::critical(this,"ERROR",
                        "端口号格式错误!");
            return;
        }
        //获取配置聊天室昵称
        username = ui->usernameEdit->text();
        if(username == ""){
            QMessageBox::critical(this,"ERROR",
                        "聊天室昵称不能为空!");
            return;
        }
        //向服务器发送连接请求:
        //如果连接成功发送信号:connected
        //如果连接失败发送信号:error
        tcpSocket.connectToHost(serverIp,serverPort);
    }
    //如果当前是在线状态,则断开和服务器的连接
    else{
        //向服务器发送离开聊天室提示消息
        QString msg = username + ":离开了聊天室!";
        tcpSocket.write(msg.toUtf8());
        //断开和服务器连接
        //断开连接后发送信号:disconnected
        tcpSocket.disconnectFromHost();
    }
}

②.conneced信号槽函数

//和服务器连接成功时执行的槽函数
void ClientDialog::onConnected(void)
{
    status = true;//标记客户端为在线状态
    //恢复发送消息按钮
    ui->sendButton->setEnabled(true);
    //禁用ip/port/username输入功能
    ui->serverIpEdit->setEnabled(false);
    ui->serverPortEdit->setEnabled(false);
    ui->usernameEdit->setEnabled(false);
    //修改按钮文本:离开服务器
    ui->connectButton->setText("离开服务器");

    //向服务器发送进入聊天室提示消息
    QString msg = username + ":进入了聊天室!";
    //toUtf8:QString转换为QByteArray
    tcpSocket.write(msg.toUtf8());

}

③.disconnected信号槽函数

//和服务器断开连接时执行槽函数
void ClientDialog::onDisconnected(void)
{
    //禁用发送消息按钮
    ui->sendButton->setEnabled(false);
    //恢复ip/port/username输入功能
    ui->serverIpEdit->setEnabled(true);
    ui->serverPortEdit->setEnabled(true);
    ui->usernameEdit->setEnabled(true);
    //修改按钮文本:连接服务器
    ui->connectButton->setText("连接服务器");

    status = false;//标记为离线状态
}

④.发送按钮

//发送按钮对应的槽函数
void ClientDialog::on_sendButton_clicked()
{
    //获取用户输入的消息
    QString msg = ui->messageEdit->text();
    if(msg == ""){
        return;
    }
    //拼接要发送消息的字符串
    msg = username + ":" + msg;
    //发送消息
    tcpSocket.write(msg.toUtf8());
    //清空消息输入
    ui->messageEdit->clear();
}

⑤.readyread

//接收服务器转发聊天消息的槽函数
void ClientDialog::onReadyRead()
{
    if(tcpSocket.bytesAvailable()){
        //读取聊天消息
        QByteArray buf = tcpSocket.readAll();
        //显示聊天消息
        ui->listWidget->addItem(buf);
        ui->listWidget->scrollToBottom();
    }
}

⑥.error

//网络通信异常时执行的槽函数
void ClientDialog::onError()
{
    //tcpSocket.errorString:获取网络异常原因的字符串
    QMessageBox::critical(this,"Error",
                tcpSocket.errorString());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值