四、网络聊天室
原理:基于tcp协议的网络聊天室,实现服务器端和客户端之间的通信。
需求:利用tcpSocket套接字实现服务器和客户端的通信。
如何实现通讯?
服务器:
一、头文件
- 服务器界面(利用qtcreator画出来),如图所示:
- 在头文件中声明套接字,服务器端口,已经保存通信套接字的容器
QTcpServer tcpServer;//TCP服务器
quint16 port;//服务器端口
//列表容器:保存和客户端通信的套接字
QList<QTcpSocket*> tcpClientList; - 增加以下槽函数:
① 、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());
}
来源:CSDN
作者:渐,忘记※
链接:https://blog.csdn.net/qq_45630216/article/details/104757203