Qt为我们封装好了QTcpServer和QTcpSocket这两个类,我们只需要学习搭建流程以及接口使用就行,本文章主要实现了简易的客户端和服务器之间的tcp通信,服务器可指定客户端端口单发信息,也可群发。
下面这张图展示服务器和客户端怎么建联和通信流程
TCP服务器
下面我们来搭建一个简易的服务器,
最主要的功能:监听(listen),处理新的连接,处理收到的数据,处理断开连接
创建Widget工程
注意再.pro文件添加network模块
添加private成员
将光标移动到类类型上,使用快捷键Alt+Enter添加缺省的头文件
这里缺省的是<QTcpServer>,<QTcpSocket>
ui设计
控件名字参考红框
逻辑代码编写
Widget.h
成员函数功能见注释
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QString getCurrentTime();//获取系统当前时间
QString getLocalIP();//获取本机ip
void highLight(QString displayString,QColor color);//设置高亮格式
void myComboBox_refresh();//刷新已连接的socket端口号
public slots:
void do_newConnection();//处理新来的连接
void do_readyRead();//处理读到的数据
void do_disConnection();//设置收到的客户端数据为高亮
void do_stateChanged();//处理客户端的状态变化
private slots:
void on_openServerBtn_clicked();//点击打开服务器
void on_closeServerBtn_clicked();//点击关闭服务器
void on_sendBtn_clicked();//点击发送信息
void on_comboBox_socket_activated(int index);//获取comboBox下标
void on_pushButton_refresh_clicked();//点击刷新comboBox列表
private:
Ui::Widget *ui;
QTcpServer* server;
QTcpSocket* socket;
int childrenIndex = 0;//记录comboBox的某一个下标
};
#endif // WIDGET_H
Widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDateTime>
#include <QHostInfo>
#include <QMessageBox>
#include <QTextCursor>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
server = new QTcpServer(this);
//socket = new QTcpSocket(this);
ui->lineEdit_port->setText("8888");//设置服务器默认端口
ui->closeServerBtn->setEnabled(false);
ui->sendBtn->setEnabled(false);
connect(server,&QTcpServer::newConnection,this,&Widget::do_newConnection);//处理连接的信号槽
}
//获取当前时间
QString Widget::getCurrentTime(){
//获取当前时间
QDateTime currentTime = QDateTime::currentDateTime();
QString timeString = currentTime.toString("yyyy-MM-dd hh:mm:ss");
return timeString;
}
//处理新来的连接
void Widget::do_newConnection(){
socket = server->nextPendingConnection();//下一个待处理的连接
connect(socket,&QTcpSocket::disconnected,this,&Widget::do_disConnection);
connect(socket,&QTcpSocket::readyRead,this,&Widget::do_readyRead);
connect(socket,&QTcpSocket::stateChanged,this,&Widget::do_stateChanged);
QString displayString = "\n客户端:["+getCurrentTime()+"]["+socket->peerAddress().toString() +"|"
+QString::number(socket->peerPort())+"] connected!!!";
highLight(displayString,Qt::green);
myComboBox_refresh();//刷新连接列表
ui->sendBtn->setEnabled(true);
}
//处理断开连接
void Widget::do_disConnection(){
QTcpSocket *tmpSocket = qobject_cast<QTcpSocket *>(sender());//获取断开连接的发送者
QString displayString = "\n客户端:["+getCurrentTime()+"]["+socket->peerAddress().toString() +"|"
+QString::number(socket->peerPort())+"] disConnected!!!!!";
highLight(displayString,Qt::red);
tmpSocket->deleteLater();
myComboBox_refresh();//刷新连接列表
ui->comboBox_socket->setCurrentIndex(ui->comboBox_socket->count()-1);
if(server->findChildren<QTcpSocket*>().empty()) ui->sendBtn->setEnabled(false);
}
//处理读到的数据
void Widget::do_readyRead(){
QTcpSocket *tmpSocket = qobject_cast<QTcpSocket *>(sender());//获取断开连接的发送者
QString buf;
buf = QString::fromUtf8(tmpSocket->readAll());//读取内容
QString displayString = "\n客户端:[" + getCurrentTime() + "][" + tmpSocket->peerAddress().toString() + "|"
+ QString::number(tmpSocket->peerPort()) + "] " + buf;
//设置高亮
highLight(displayString,Qt::blue);
}
//处理socket状态变化
void Widget::do_stateChanged(){
}
Widget::~Widget()
{
delete ui;
}
//点击打开服务器
void Widget::on_openServerBtn_clicked()
{
if(ui->lineEdit_port->text().isEmpty()){
QMessageBox::information(this,"错误","端口号未设置",QMessageBox::Ok);
return ;
}
if(! server->listen(QHostAddress(getLocalIP()),ui->lineEdit_port->text().toInt())){
QMessageBox::information(this,"错误","端口号被占用",QMessageBox::Ok);
return;
}
//server->listen(QHostAddress(getLocalIP()),ui->lineEdit_port->text().toInt());//监听所有来连接服务器的客户端
ui->openServerBtn->setEnabled(false);
ui->closeServerBtn->setEnabled(true);
}
//点击关闭服务器
void Widget::on_closeServerBtn_clicked()
{
//关闭所有socket
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
for(QTcpSocket *tmp : tcpSocketClients){
tmp->close();
tmp->deleteLater();
}
server->close();
ui->closeServerBtn->setEnabled(false);
ui->openServerBtn->setEnabled(true);
}
//点击发送(单发还是群发)
void Widget::on_sendBtn_clicked()
{
//socket->write(ui->lineEdit_send->text().toLocal8Bit().data());
QString displayString = "\n我:"+ui->lineEdit_send->text();
highLight(displayString,Qt::black);
//server自己会保存客户端的连接,通过findChildren找到客户端的连接
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
//选择单独发送还是群发
if(!ui->comboBox_socket->currentText().isEmpty() && ui->comboBox_socket->currentText()!="all"){
tcpSocketClients[childrenIndex]->write(ui->lineEdit_send->text().toLocal8Bit().data());
}
else{
//发送到每个客户端
for(QTcpSocket *tmp : tcpSocketClients){
tmp->write(ui->lineEdit_send->text().toLocal8Bit().data());
}
}
}
//获取本机iP
QString Widget::getLocalIP()
{
//获取本机IPv4地址
QString hostName = QHostInfo::localHostName();//本机主机名
QHostInfo hostInfo = QHostInfo::fromName(hostName);
QString localIP="";
QList<QHostAddress> addList = hostInfo.addresses();
if(!addList.isEmpty())
{
for(int i = 0;i<addList.count();i++)
{
QHostAddress aHost = addList.at(i);
if(QAbstractSocket::IPv4Protocol==aHost.protocol())
{
localIP = aHost.toString();
break;
}
}
}
return localIP;
}
//设置收到的客户端数据高亮
void Widget::highLight(QString displayString,QColor color){
QTextCursor cursor = ui->textEdit_history->textCursor();// 获取 QTextEdit 的文本光标
cursor.movePosition(QTextCursor::End);// 移动光标到文档末尾
// 设置高亮样式
QTextCharFormat highlightFormat;
highlightFormat.setForeground(color); // 设置为蓝色字体
cursor.insertText(displayString, highlightFormat);// 插入带高亮的文本
ui->textEdit_history->setTextCursor(cursor);// 确保文本编辑器的光标保持在文档末尾(这一步在这个特定情况下是可选的)
ui->textEdit_history->ensureCursorVisible();// 如果你想要滚动到文本编辑器的底部(显示最新消息)
}
//刷新已经连接的客户端port列表
void Widget::myComboBox_refresh()
{
//server自己会保存客户端的连接,通过findChildren找到客户端的连接
QList<QTcpSocket*> tcpSocketClients = server->findChildren<QTcpSocket*>();
ui->comboBox_socket->clear();
//发送到每个客户端
for(QTcpSocket *tmp : tcpSocketClients){
if(tmp!=nullptr)
{
ui->comboBox_socket->addItem(QString::number(tmp->peerPort()));
}
}
ui->comboBox_socket->addItem("all");
}
//获取客户端端口列表下标
void Widget::on_comboBox_socket_activated(int index)
{
childrenIndex = index;
}
//点击刷新客户端端口列表
void Widget::on_pushButton_refresh_clicked()
{
myComboBox_refresh();
}
TCP客户端
客户端相对服务器相对简单,
最主要的功能:处理连接,处理收到的数据,处理断开连接
创建Widget工程
同样需要再.pro文件中添加network
ui设计
逻辑代码
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void do_readyRead();//处理收到的数据
void do_connected();//处理连接
void do_disconnected();//处理断开连接
private slots:
void on_pushButton_connect_clicked();//点击连接
void on_pushButton_disconnect_clicked();//点击断开连接
void on_pushButton_send_clicked();//点击发送
private:
Ui::Widget *ui;
QTcpSocket* socket;
};
#endif // WIDGET_H
Widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDateTime>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
socket = new QTcpSocket(this);
ui->lineEdit_ip->setText("10.6.134.244");//注意:根据自己的本地ip修改
ui->lineEdit_Port->setText("8888");
ui->pushButton_connect->setEnabled(true);
ui->pushButton_disconnect->setEnabled(false);
ui->pushButton_send->setEnabled(false);
connect(socket,&QTcpSocket::connected,this,&Widget::do_connected);
connect(socket,&QTcpSocket::disconnected,this,&Widget::do_disconnected);
connect(socket,&QTcpSocket::readyRead,this,&Widget::do_readyRead);
}
Widget::~Widget()
{
delete ui;
}
//处理连接
void Widget::do_connected(){
ui->textEdit_history->append("连接服务器成功!!");
ui->pushButton_connect->setEnabled(false);
ui->pushButton_disconnect->setEnabled(true);
ui->pushButton_send->setEnabled(true);
}
//处理断开
void Widget::do_disconnected(){
ui->textEdit_history->append("连接服务器断开!!!");
socket->close();
ui->pushButton_connect->setEnabled(true);
ui->pushButton_disconnect->setEnabled(false);
ui->pushButton_send->setEnabled(false);
}
//点击连接
void Widget::on_pushButton_connect_clicked()
{
socket->connectToHost(ui->lineEdit_ip->text(),ui->lineEdit_Port->text().toUInt());
}
//处理收到的数据
void Widget::do_readyRead(){
//获取当前时间
QDateTime currentTime = QDateTime::currentDateTime();
QString timeString = currentTime.toString("yyyy-MM-dd hh:mm:ss");
QString buf;
buf = QString::fromUtf8(socket->readAll());
ui->textEdit_history->append("服务器:["+timeString+":"+socket->peerAddress().toString()
+"|"+QString::number(socket->peerPort())+"]"+buf);
}
//点击断开连接
void Widget::on_pushButton_disconnect_clicked()
{
socket->close();
ui->pushButton_connect->setEnabled(true);
ui->pushButton_disconnect->setEnabled(false);
ui->pushButton_send->setEnabled(false);
}
//点击发送
void Widget::on_pushButton_send_clicked()
{
socket->write(ui->lineEdit_send->text().toLocal8Bit().data());
ui->textEdit_history->append("我:"+ui->lineEdit_send->text());
}