效果:
服务器
头文件
#ifndef SERVERNETDIALOG_H
#define SERVERNETDIALOG_H
#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QTimer>
namespace Ui {
class serverNetDialog;
}
class serverNetDialog : public QDialog
{
Q_OBJECT
public:
explicit serverNetDialog(QWidget *parent = 0);
~serverNetDialog();
private slots:
//创建服务器按钮对应槽函数
void on_pushButton_creatr_clicked();
//响应客户端连接请求的槽函数
void onNewConnection();
//接收客户端消息的槽函数
void onReadyRead();
//转发聊天消息给其他客户端
void sendMessage(const QByteArray& buf);
//定时器定时删除无效连接套接字槽函数
void onTimeOut();
private:
Ui::serverNetDialog *ui;
QTcpServer tcpserver;
quint16 port;//服务器端口
QList<QTcpSocket *> tcpClientList;//容器:保存所有和客户端通信的套接字
QTimer timer;//定时器,定时检查断开连接的套接字
};
#endif // SERVERNETDIALOG_H
源文件
#include "servernetdialog.h"
#include "ui_servernetdialog.h"
#pragma execution_character_set("utf-8")
serverNetDialog::serverNetDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::serverNetDialog)
{
ui->setupUi(this);
//当有客户端向服务器发送连接请求,发送信号
connect(&tcpserver,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
connect(&timer,SIGNAL(timeout()),SLOT(onTimeOut()));
}
serverNetDialog::~serverNetDialog()
{
delete ui;
}
//创建服务器按钮对应槽函数
void serverNetDialog::on_pushButton_creatr_clicked()
{
//获取端口号
port = ui->lineEdit_port->text().toShort();
//设置服务器IP和端口
if(tcpserver.listen(QHostAddress::Any,port)== true){
qDebug() << "创建服务器成功。";
//禁用按钮与输入框
ui->pushButton_creatr->setEnabled(false);
ui->lineEdit_port->setEnabled(false);
//开启定时器
timer.start(3000);
}
else{
qDebug() << "创建服务器失败!";
}
}
//响应客户端连接请求的槽函数
void serverNetDialog::onNewConnection()
{
//获取和客户端通信的套接字
QTcpSocket* tcpClient = tcpserver.nextPendingConnection();
//保存套接字到容器
tcpClientList.append(tcpClient);
//当客户端向服务器发送消息时,通信套接字发送信号:readyRead
connect(tcpClient,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
}
//接收客户端消息的槽函数
void serverNetDialog::onReadyRead()
{
for(int i = 0;i < tcpClientList.size();i++)
{
//bytesAvailable:获取当前套接字等待读取消息字节数
//返回0表示没消息
//返回>0,说明当前套接字有消息到来
if(tcpClientList.at(i)->bytesAvailable())
{
//读取消息并保存
//readAll:读取全部内容
QByteArray buf = tcpClientList.at(i)->readAll();
//显示聊天消息
ui->listWidget->addItem(buf);
//最新消息显示在底部
ui->listWidget->scrollToBottom();
//转发消息给所有在线客户端
sendMessage(buf);
}
}
}
//转发聊天消息给其他客户端
void serverNetDialog::sendMessage(const QByteArray& buf)
{
for(int i = 0;i < tcpClientList.size();i++)
{
tcpClientList.at(i)->write(buf);
}
}
//定时器定时删除无效连接套接字槽函数
void serverNetDialog::onTimeOut()
{
//遍历容器中保存的客户端通信套接字是否已经断开连接,如果是则删除
for(int i = 0;i < tcpClientList.size();i++)
{
//state:获取套接字状态,0为断开连接
if(tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState)
{
tcpClientList.removeAt(i);
--i;//将删除的套接字后的个数往前提一个
}
}
}
ui界面
客户端
头文件
#ifndef CLIENTNETDIALOG_H
#define CLIENTNETDIALOG_H
#include <QDialog>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QDebug>
namespace Ui {
class clientNetDialog;
}
class clientNetDialog : public QDialog
{
Q_OBJECT
public:
explicit clientNetDialog(QWidget *parent = 0);
~clientNetDialog();
private slots:
//发送消息槽函数
void on_pushButton_send_clicked();
//连接到服务器槽函数
void on_pushButton_connect_clicked();
//和服务器连接成功时执行的槽函数
void onConnected();
//和服务器断开连接时执行的槽函数
void onDisConnct();
//接收聊天消息的槽函数
void onReadyRead();
//网络异常时执行的槽函数
void onError();
private:
Ui::clientNetDialog *ui;
bool status;//状态标识:在线/离线
QTcpSocket tcpSocket;//和服务器通信的套接字
QHostAddress serverIP;//服务器ip
quint16 serverPort;//服务器端口
QString name;//聊天室昵称
};
#endif // CLIENTNETDIALOG_H
源文件
#include "clientnetdialog.h"
#include "ui_clientnetdialog.h"
#pragma execution_character_set("utf-8")
clientNetDialog::clientNetDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::clientNetDialog)
{
ui->setupUi(this);
status = false;//初始化为离线状态
connect(&tcpSocket,SIGNAL(connected()),this,SLOT(onConnected()));
connect(&tcpSocket,SIGNAL(disconnected()),this,SLOT(onDisConnct()));
connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));
}
clientNetDialog::~clientNetDialog()
{
delete ui;
}
//发送消息槽函数
void clientNetDialog::on_pushButton_send_clicked()
{
//获取用户输入聊天消息
QString msg = ui->lineEdit_messge->text();
if(msg == ""){
return;
}
else{
msg = name + ":" + msg;
//发送聊天消息
tcpSocket.write(msg.toUtf8());
//清空消息输入框
ui->lineEdit_messge->clear();
}
}
//连接到服务器槽函数
void clientNetDialog::on_pushButton_connect_clicked()
{
//如果是离线状态,建立和服务器的连接
if(status == false){
//获取服务器IP
serverIP.setAddress(ui->lineEdit_ip->text());
//获取服务器端口号
serverPort = ui->lineEdit_port->text().toShort();
//获取聊天室名字
name = ui->lineEdit_name->text();
//向服务器发送连接请求
//成功:发送信号connect
//失败:发送信号error
tcpSocket.connectToHost(serverIP,serverPort);
}
//如果是在线状态,断开和服务器的连接
else{
//向服务器发送离开聊天室提示消息
QString msg = name + ":离开聊天室。";
//toUtf8:QString转换成QByteArray
tcpSocket.write(msg.toUtf8());
//断开连接,会发送信号:disconnect
tcpSocket.disconnectFromHost();
}
}
//和服务器连接成功时执行的槽函数
void clientNetDialog::onConnected()
{
status = true;//在线状态
ui->pushButton_send->setEnabled(true);//恢复发送消息按钮使能
ui->lineEdit_ip->setEnabled(false);//禁用IP
ui->lineEdit_port->setEnabled(false);//禁用端口
ui->lineEdit_name->setEnabled(false);//禁用聊天室昵称
ui->pushButton_connect->setText("离开聊天室");
//向服务器发送进入聊天室提示消息
QString msg = name + ":进入聊天室。";
//toUtf8:QString转换成QByteArray
tcpSocket.write(msg.toUtf8());
}
//和服务器断开连接时执行的槽函数
void clientNetDialog::onDisConnct()
{
status = false;//在线状态
ui->pushButton_send->setEnabled(false);//恢复发送消息按钮使能
ui->lineEdit_ip->setEnabled(true);//禁用IP
ui->lineEdit_port->setEnabled(true);//禁用端口
ui->lineEdit_name->setEnabled(true);//禁用聊天室昵称
ui->pushButton_connect->setText("连接服务器");
}
//接收聊天消息的槽函数
void clientNetDialog::onReadyRead()
{
if(tcpSocket.bytesAvailable()){
//接收消息
QByteArray buf = tcpSocket.readAll();
//显示消息
ui->listWidget->addItem(buf);
ui->listWidget->scrollToBottom();
}
}
//网络异常时执行的槽函数
void clientNetDialog::onError()
{
//errorString():获取网络异常的原因
QMessageBox::critical(this,"ERROR",tcpSocket.errorString());
}
ui界面