利用QT写一个简单的TCP服务器和客户端进行聊天
TCP与UDP基本区别
1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证
《案例》网络聊天室
1 服务器
1)使用QTcpServer创建并发服务器
2)保存所有客户端socket套接字
3)接收客户端消息(read)
4)转发消息给所有的客户端(write)
2 客户端:
1)QTcpSocket建立基于TCP的客户端
2)连接到指定的服务器(IP/端口)
3)发送消息到服务器(write)
4)接收服务器返回聊天信息并显示
服务器端
ui界面布局
1.添加pro文件 因为用到了网络传输,所以需要添加network模块
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
2.添加头文件
#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
3.添加变量函数声明
private slots:
//创建聊天室对应对应槽函数
void on_createButton_clicked();
//当客户端和服务器建立链接发送信号newConnection
void onNewConnection();
//连接readyRead信号
void slotDataReceived();
private:
//转发消息给所有的客户端
void sendMessage(const QByteArray&);
private:
Ui::ServerDialog *ui;
private:
QTcpServer tcpServer;//服务器对象
qint16 port;//服务器端口
//保存所有和客户端通信的套接字
QList <QTcpSocket*> tcpClientList;
4.添加函数代码
ServerDialog::ServerDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ServerDialog)
{
ui->setupUi(this);
connect(&tcpServer,SIGNAL(newConnection()),
this,SLOT(onNewConnection()));
}
ServerDialog::~ServerDialog()
{
delete ui;
}
//创建TCP服务器
void ServerDialog::on_createButton_clicked()
{
port = ui->portEdit->text().toShort();
//开启服务器
if(tcpServer.listen(QHostAddress::Any,port)){
qDebug("TCP服务器创建成功");
}
else{
qDebug("TCP服务器创建失败");
}
//禁用创建按钮和端口输入
ui->createButton->setEnabled(false);
ui->portEdit->setEnabled(false);
}
//当客户端和服务器建立链接发送信号newConnection
void ServerDialog::onNewConnection()
{
//获取和客户端通信的套接字
QTcpSocket* tcpClientSocket =
tcpServer.nextPendingConnection();
//保存套接字到容器
tcpClientList.append(tcpClientSocket);
//收到数据时,将会发送readyRead
connect(tcpClientSocket,SIGNAL(readyRead()),
this,SLOT(slotDataReceived()));
}
//连接readyRead信号
void ServerDialog::slotDataReceived()
{
//遍历所有的客户端
for(int i=0; i<tcpClientList.count(); i++){
//检查当前客户端是否有数据到来
if(tcpClientList.at(i)->bytesAvailable()){
//接收客户端发送来的消息
QByteArray readbuf =
tcpClientList.at(i)->readAll();
//显示到服务器Ui
ui->listWidget->addItem(readbuf);
//转发消息给所有的客户端
sendMessage(readbuf);
}
}
}
//转发消息给所有的客户端
void ServerDialog::sendMessage(const QByteArray& msg)
{
for(int i=0;i<tcpClientList.count();i++){
tcpClientList.at(i)->write(msg);
}
}
客户端
ui界面布局
1.同样pro文件需要添加network模块
2.添加头文件
#include <QDialog>
#include <QHostAddress>
#include <QTcpSocket>
#include <QMessageBox>
3.添加变量函数声明
private slots:
//发送消息,按钮的槽函数
void on_sendButton_clicked();
//连接服务器,按钮的槽函数
void on_connectButton_clicked();
//和服务器连接时调用的槽函数
void onConnected(void);
//和服务器断开连接调用的槽函数
void onDisconnected(void);
//接收聊天消息的槽函数
void slotDataReceived(void);
//网络异常处理的槽函数
void slotSockError(QAbstractSocket::SocketError);
private:
Ui::ClientDialog *ui;
bool status;//标记客户端的连接状态,true在线,false离线
QTcpSocket tcpSocket;//和服务器通信的套接字
qint16 port;//服务器通信的端口
QHostAddress serverIP;//服务器通信的IP地址
QString userName;//聊天室的昵称
4.添加函数代码
ClientDialog::ClientDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ClientDialog)
{
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(slotDataReceived()));
//网络异常,发送error
connect(&tcpSocket,
SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(slotSockError(QAbstractSocket::SocketError)));
}
ClientDialog::~ClientDialog()
{
delete ui;
}
void ClientDialog::on_sendButton_clicked()
{
//获取用户输入的消息
if(ui->sendEdit->text() == ""){
return;
}
QString msg = userName + ":" + ui->sendEdit->text();
//发送消息到服务器
tcpSocket.write(msg.toLocal8Bit());
//清空消息
ui->sendEdit->clear();
}
void ClientDialog::on_connectButton_clicked()
{
if(status == false){//当前没有连接,建立连接
//从界面获取IP、用户名、PORT
QString ip = ui->serverIpEdit->text();
if(serverIP.setAddress(ip) == false){
QMessageBox::information(
this,"错误","请输入正确的IP地址");
return;
}
if(ui->userNameEdit->text() == ""){
QMessageBox::information(
this,"错误","请输入聊天室昵称");
return;
}
userName = ui->userNameEdit->text();
if(ui->portEdit->text() == ""){
QMessageBox::information(
this,"错误","请输入正确的端口");
return;
}
port = ui->portEdit->text().toShort();
//连接服务器
tcpSocket.connectToHost(serverIP,port);
status = true;//标记为在线状态
}
else{
//发送下线消息
QString msg = userName+":离开聊天室";
tcpSocket.write(msg.toLocal8Bit());
//关闭和服务器的连接
tcpSocket.disconnectFromHost();
status = false;//标记为离线状态
}
}
//和服务器连接时调用的槽函数
void ClientDialog::onConnected(void)
{
//使能发送消息按钮
ui->sendButton->setEnabled(true);
//连接服务器 按钮文本改为 离开服务器
ui->connectButton->setText(tr("离开服务器"));
//禁用:ip port 用户名输入
ui->serverIpEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->userNameEdit->setEnabled(false);
//向服务器发送一条进入聊天室消息
QString msg = userName+":进入聊天室";
// tcpSocket.write(msg.toLocal8Bit());
tcpSocket.write(msg.toLatin1());
}
//和服务器断开连接调用的槽函数
void ClientDialog::onDisconnected(void)
{
//禁用发送消息按钮
ui->sendButton->setEnabled(true);
//使能ip port 用户 输入
ui->serverIpEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
ui->userNameEdit->setEnabled(true);
ui->connectButton->setText("连接服务器");
}
//接收聊天消息的槽函数
void ClientDialog::slotDataReceived(void)
{
if(tcpSocket.bytesAvailable()){
QByteArray buf;
buf.resize(tcpSocket.bytesAvailable());
tcpSocket.read(buf.data(),buf.size());
//显示消息到界面
QString msg = buf.data();
ui->listWidget->addItem(msg);
}
}
//网络异常处理的槽函数
void ClientDialog::slotSockError(
QAbstractSocket::SocketError){
QMessageBox::critical(this,"网络异常",
tcpSocket.errorString());
}
运行演示
可能有点乱码,后续再优化