QT TCP网络聊天室
Soaring丶 人气:2服务器:
serverdialog.h
#ifndef SERVERDIALOG_H #define SERVERDIALOG_H #include <QDialog> #include <QTcpServer> #include <QTcpSocket> #include <QDebug> #include <QTimer> namespace Ui { class ServerDialog; } class ServerDialog : public QDialog { Q_OBJECT public: explicit ServerDialog(QWidget *parent = 0); ~ServerDialog(); private slots: //创建服务器按钮对应的槽函数 void on_pushButton_clicked(); //响应客户端连接请求的槽函数 void onNewConnection(); //接收客户端聊天消息的槽函数 void onReadyRead(); //转发聊天消息给其它客户端的槽函数 void sendMessage(const QByteArray&); //定时器检查客户端套接字是否为正常连接状态的槽函数 void onTimeout(void); private: Ui::ServerDialog *ui; QTcpServer tcpServer;//TCP服务器 quint16 port;//服务器端口,quint16-->unsigned short QList<QTcpSocket*> tcpClientList;//容器:保存和客户端通信的套接字 QTimer timer;//定时器,定时检查容器中和客户端通信的套接字是否为正常连接状态 }; #endif // SERVERDIALOG_H
serverdialog.cpp
#include "serverdialog.h" #include "ui_serverdialog.h" ServerDialog::ServerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ServerDialog) { ui->setupUi(this); } ServerDialog::~ServerDialog() { delete ui; } //创建服务器按钮对应的槽函数 void ServerDialog::on_pushButton_clicked() { //获取服务器端口 port = ui->lineEdit->text().toShort(); //设置监听服务器的IP和端口 //QHostAddress::Any ==> QHostAddress("0.0.0.0"); if(tcpServer.listen(QHostAddress::Any,port)==false){ qDebug() << "创建服务器失败"; return; } else{ qDebug() << "创建服务器成功"; //当有客户端向服务器发送连接请求时,发送信号newConnection connect(&tcpServer,SIGNAL(newConnection()), this,SLOT(onNewConnection())); //禁用端口输入和创建服务器按钮 ui->lineEdit->setEnabled(false); ui->pushButton->setEnabled(false); //定时器到时发送信号:timeout connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout())); //开启定时器,每隔3秒检查一次 timer.start(3000); } } //响应客户端连接请求的槽函数 void ServerDialog::onNewConnection() { //获取和客户端通信的套接字 QTcpSocket* tcpClient = tcpServer.nextPendingConnection(); //保存套接字到容器中 tcpClientList.append(tcpClient); //当客户端给服务器发送消息时,tcpClient发送信号readyRead connect(tcpClient,SIGNAL(readyRead()), this,SLOT(onReadyRead())); } //接收客户端聊天消息的槽函数 void ServerDialog::onReadyRead() { //遍历检查哪个客户端有消息 for(int i=0;i<tcpClientList.size();i++){ //at(i):获取容器中第i套接字(QTcpSocket*) //bytesAvailable:获取当前套接字等待读取消息的字节数,如果为0表示没有消息,如果 //大于0说明有消息. if(tcpClientList.at(i)->bytesAvailable()){ //读取消息并保存 QByteArray buf = tcpClientList.at(i)->readAll(); //显示消息 ui->listWidget->addItem(buf); //回滚到最底部(最新消息) ui->listWidget->scrollToBottom(); //转发消息给其它客户端 sendMessage(buf); } } } //转发聊天消息给其它客户端的槽函数 void ServerDialog::sendMessage(const QByteArray& msg) { for(int i=0;i<tcpClientList.size();i++){ tcpClientList.at(i)->write(msg); } } //定时器检查客户端套接字是否为正常连接状态的槽函数 void ServerDialog::onTimeout(void) { for(int i=0;i<tcpClientList.size();i++){ //state():获取第i个套接字的连接状态 //UnconnectedState:表示未连接(断开连接) if(tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState){ //从容器中将断开连接的套接字移除 tcpClientList.removeAt(i); --i; } } }
main.cpp
#include "serverdialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); ServerDialog w; w.show(); return a.exec(); }
客户端:
clientdialog:
#ifndef CLIENTDIALOG_H #define CLIENTDIALOG_H #include <QDialog> //QT += network #include <QTcpSocket> #include <QHostAddress> #include <QMessageBox> namespace Ui { class ClientDialog; } class ClientDialog : public QDialog { Q_OBJECT public: explicit ClientDialog(QWidget *parent = 0); ~ClientDialog(); private slots: //发送按钮对应的槽函数 void on_sendButton_clicked(); //连接服务器按钮对应的槽函数 void on_connectButton_clicked(); //和服务器连接成功时执行的槽函数 void onConnected(void); //和服务器断开连接时执行的槽函数 void onDisconnected(void); //接收服务器转发聊天消息的槽函数 void onReadyRead(void); //网络通信异常时执行的槽函数 void onError(void); private: Ui::ClientDialog *ui; bool status;//客户端状态标记,true:在线,false:离线状态 QTcpSocket tcpSocket;//和服务器通信的tcp套接字 QHostAddress serverIp;//服务器地址 quint16 serverPort;//服务器端口 QString username;//聊天室昵称 }; #endif // CLIENTDIALOG_H
clientdialog.cpp
#include "clientdialog.h" #include "ui_clientdialog.h" ClientDialog::ClientDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ClientDialog) { ui->setupUi(this); status = false; //和服务器连接成功时,tcpSocket发送信号:connected connect(&tcpSocket,SIGNAL(connected()), this,SLOT(onConnected())); //和服务器断开连接时,tcpSocket发送信号:disconnected connect(&tcpSocket,SIGNAL(disconnected()), this,SLOT(onDisconnected())); //收到聊天消息时,tcpSocket发送信号:readyRead connect(&tcpSocket,SIGNAL(readyRead()), this,SLOT(onReadyRead())); //网络通信异常时,tcpSocket发送信号:error connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(onError())); } ClientDialog::~ClientDialog() { delete ui; } //发送按钮对应的槽函数 void ClientDialog::on_sendButton_clicked() { //获取用户输入的消息 QString msg = ui->messageEdit->text(); if(msg == ""){ return; } msg = username + ":" + msg; //发送消息 tcpSocket.write(msg.toUtf8()); //清空已输入的消息 ui->messageEdit->clear(); } //连接服务器按钮对应的槽函数 void ClientDialog::on_connectButton_clicked() { if(status == false){//如果当前是离线状态,则连接服务器 //获取服务器IP if(serverIp.setAddress(ui->serverIpEdit->text())==false){ //critical():表示错误的消息提示框 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 + ":离开了聊天室"; //toUtf8():将QString(unicode)转换QByteArray(utf-8) tcpSocket.write(msg.toUtf8()); //断开连接 //断开后发送信号:disconnected tcpSocket.disconnectFromHost(); } } //和服务器连接成功时执行的槽函数 void ClientDialog::onConnected(void) { status = true;//设置状态标记:在线 ui->sendButton->setEnabled(true);//恢复"发送"按钮为正常可用状态 ui->serverIpEdit->setEnabled(false);//禁用ip输入 ui->serverportEdit->setEnabled(false);//禁用端口输入 ui->usernameEdit->setEnabled(false);//禁用昵称输入 ui->connectButton->setText("离开聊天室");//修改连接服务器按钮文本 //向服务器发送进入聊天室提示消息 QString msg = username + ":进入了聊天室"; //toUtf8():将QString(unicode)转换QByteArray(utf-8) tcpSocket.write(msg.toUtf8()); } //和服务器断开连接时执行的槽函数 void ClientDialog::onDisconnected(void) { status = false;//设置离线状态 ui->sendButton->setEnabled(false);//禁用"发送"按钮 ui->serverIpEdit->setEnabled(true);//恢复ip输入 ui->serverportEdit->setEnabled(true);//恢复端口输入 ui->usernameEdit->setEnabled(true);//恢复昵称输入 ui->connectButton->setText("连接服务器");//修改离开聊天室按钮文本 } //接收服务器转发聊天消息的槽函数 void ClientDialog::onReadyRead(void) { //bytesAvailable():获取等待读取消息的字节数 if(tcpSocket.bytesAvailable()){ //读取消息 QByteArray buf = tcpSocket.readAll(); //显示消息到界面 ui->listWidget->addItem(buf); ui->listWidget->scrollToBottom(); } } //网络通信异常时执行的槽函数 void ClientDialog::onError(void) { //errorString():获取网络通信异常原因的字符串 QMessageBox::critical(this,"Error",tcpSocket.errorString()); }
main.cpp
#include "clientdialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); ClientDialog w; w.show(); return a.exec(); }
最终实现效果:
加载全部内容