• 对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)


    在汇文培训老师给讲了这个例子。讲的挺好的

    Qt编写聊天服务器与客户端主要用到下面两个类:

    • QTcpSocket --- 处理连接的
    • QTcpServer --- 处理服务器,对接入进行响应,创建每个链接的QTcpSocket实例

    编写网络程序需要在 .pro 文件中加上 network,如下

    QT       += core gui network

    1.客户端的编写

    客户端需要做的事:

    • 获取服务器的主机ip和端口(port)
    • 链接主机(connectToHost)
    • 链接状态下等待一些信号(signal)的产生并作出相应的回应(slot)

    主要等待的信号由一下几个:

    void connected()
    void disconnected()
    void error(QAbstractSocket::SocketError socketError)
    void readyRead()

    并为这几个信号写相应的槽函数对该情况作出处理

    以下为我的具体实现代码:

    我的ui界面是这样的:

     1 #ifndef TCPCLIENT_H
     2 #define TCPCLIENT_H
     3 
     4 #include <QWidget>
     5 #include <QMessageBox>
     6 #include <QTcpSocket>
     7 #include <QDebug>
     8 #include <QHostAddress>
     9 #include <QTextStream>
    10 
    11 namespace Ui {
    12 class TcpClient;
    13 }
    14 
    15 class TcpClient : public QWidget
    16 {
    17     Q_OBJECT
    18 
    19 public:
    20     explicit TcpClient(QWidget *parent = 0);
    21     ~TcpClient();
    22 
    23 private slots:
    24     void on_connectPushButton_clicked();    //connect按键槽函数
    25     
    26     void slotConnect();  
    27     void slotErr(QAbstractSocket::SocketError err);
    28     void slotDisconnect();
    29     void slotReadData();
    30 
    31     void on_sendPushButton_clicked();     //send按键槽函数
    32     void on_sendLineEdit_returnPressed();    //sendLineEdit的回车槽
    33 
    34 private:
    35     Ui::TcpClient *ui;    
    36     QTcpSocket *socket;   //客户端的文件描述符
    37     bool isConnect;      //connect按键的状态变换值
    38 };
    39 
    40 #endif // TCPCLIENT_H
    tcpclient.h
      1 #include "tcpclient.h"
      2 #include "ui_tcpclient.h"
      3 
      4 TcpClient::TcpClient(QWidget *parent) :
      5     QWidget(parent),
      6     ui(new Ui::TcpClient)
      7 {
      8     ui->setupUi(this);
      9     isConnect = true;
     10 }
     11 
     12 TcpClient::~TcpClient()
     13 {
     14     delete ui;
     15 }
     16 
     17 void TcpClient::on_connectPushButton_clicked()
     18 {
     19     QString hostip;
     20     quint16 port;
     21     if(isConnect)
     22     {
     23         /*判断ip和端口是否填写,并获取该ip和端口*/
     24         if(ui->ipLineEdit->text().isEmpty())
     25         {
     26             QMessageBox::information(this,"ip","The ip is empty,please input a ip");
     27             return ;
     28         }
     29         hostip = ui->ipLineEdit->text();
     30         if(ui->portLineEdit->text().isEmpty())
     31         {
     32             QMessageBox::information(this,"port","The port is empty,please input a port");
     33             return ;
     34         }
     35         port = ui->portLineEdit->text().toShort();
     36 
     37         /*连接服务器,并在这之前连接几个必要的信号*/
     38         socket = new QTcpSocket;
     39         //已连接信号
     40         connect(socket,SIGNAL(connected()),this,SLOT(slotConnect()));
     41         //连接出现错误信号
     42         connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(slotErr(QAbstractSocket::SocketError)));
     43         //连接断开信号
     44         connect(socket,SIGNAL(disconnected()),this,SLOT(slotDisconnect()));
     45         //准备读取数据信号
     46         connect(socket,SIGNAL(readyRead()),this,SLOT(slotReadData()));
     47         socket->connectToHost(QHostAddress(hostip),port);
     48     }
     49     else
     50     {
     51         socket->disconnectFromHost();
     52         ui->connectPushButton->setText("connect");
     53         isConnect = true;
     54     }
     55 }
     56 
     57 //槽函数定义
     58 void TcpClient::slotConnect()
     59 {
     60     QMessageBox::information(this,"connect","host connect sucess");
     61     ui->ipLineEdit->setEnabled(false);
     62     ui->portLineEdit->setEnabled(false);
     63     ui->connectPushButton->setText("disconnect");
     64     isConnect = false;
     65 }
     66 
     67 void TcpClient::slotErr(QAbstractSocket::SocketError err)
     68 {
     69     qDebug("error is %d",err);
     70     ui->connectPushButton->setText("connect");
     71 }
     72 
     73 void TcpClient::slotDisconnect()
     74 {
     75     QMessageBox::information(this,"disconnect","host disconnect sucess");
     76     ui->ipLineEdit->setEnabled(true);
     77     ui->portLineEdit->setEnabled(true);
     78     ui->connectPushButton->setText("connect");
     79     isConnect = true;
     80 }
     81 
     82 void TcpClient::slotReadData()
     83 {
     84     qDebug()<<"have data";
     85     if(socket->bytesAvailable()>0)
     86     {
     87         QString msg;
     88         QByteArray ba;
     89         ba.resize(socket->bytesAvailable());
     90         socket->read(ba.data(),ba.size());
     91         msg = QString(ba);
     92 
     93 //        QTextStream out(socket);
     94 //        QString msg;
     95 //        out >> msg;
     96         ui->msgListWidget->addItem(msg);
     97     }
     98 }
     99 
    100 
    101 void TcpClient::on_sendPushButton_clicked()
    102 {
    103     QTextStream in(socket);
    104     in << ui->sendLineEdit->text();
    105     ui->sendLineEdit->clear();
    106 }
    107 
    108 
    109 void TcpClient::on_sendLineEdit_returnPressed()
    110 {
    111     QTextStream in(socket);
    112     in << ui->sendLineEdit->text();
    113     ui->sendLineEdit->clear();
    114 }
    tcpcilent.cpp
    #include "tcpclient.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        TcpClient w;
        w.show();
    
        return a.exec();
    }
    main.cpp

    总结:客户端的编写比较容易,只要连接服务器,发送信息给服务器就可以


    2.服务器的编写

    服务器需要做的事:

    • Qt对于服务器有专门的类 QTcpServer封装,所以不需要去自己配置服务器(socket,bind),可以直接侦听(listen)
    • 当有客户端连接的时候分配文件描述符和地址标示客户端
    • 然后等待客户端发送信息(这里会得到一些信号,通过信号来处理),接收信号,转发(发送给链接在服务器的其他人)或者回馈信息。
    • 服务器需要支持多客户端,所以要有保存和删除客户端信息的处理。

    因为需要实现支持多客户端接入的服务器,所以需要将显示层(ui),服务器层,socket层分开。可以看下面这个图来理解

    简述下这里的流程,首先,客户端发送请求,服务器分配(创建)一个socket,然后incomingConnection()函数捕捉到这个socket,并在此响应客户端(接收消息,转发消息)。然后在这里创建一个信息转发消息到ui界面

    通过这样的模型,我们就可以创建一个多客户端的服务器。

    接下来的一张图我来解释下我的程序中信号与槽函数之间连接的关系(人类总是对视觉的感官来的更直接点)

    结合上面的图和我的代码就很清晰的能看出信号与槽在这三层之间的传递关系

    (1)由MyTcpSocket socket产生两个信号,readyread()和disconnected(),然后通过connect连接到两个槽函数readDataSlot()与disconnectSlot()

    (2)在上面的两个槽函数中发送emit两个信号sockReadDataSignal(qintptr,QString),disconnectSignal(qintptr),附带信息的信号,使得MyTcpServer能够访问到信息

    (3)在MyTcpServer类中通过socket接受这两个信号,并连接到相应的槽函数,把信息返回给其他客户端。

    (4)并且我把信号sockReadDataSignal()连接到MyTcpServer内显示到ui界面

    下面是的我的代码区

     1 #ifndef MYTCPSOCKET_H
     2 #define MYTCPSOCKET_H
     3 
     4 #include <QObject>
     5 #include <QTcpSocket>
     6 #include <QTextStream>
     7 #include <QHostAddress>
     8 
     9 
    10 class MyTcpSocket : public QTcpSocket
    11 {
    12     Q_OBJECT
    13 public:
    14 
    15     MyTcpSocket();
    16     ~MyTcpSocket();
    17 
    18 signals:
    19     void disconnectSignal(qintptr);
    20     void sockReadDataSignal(qintptr,QString);
    21 
    22 private slots:
    23     void readDataSlot();
    24     void disconnectSlot();
    25 };
    26 
    27 #endif // MYTCPSOCKET_H
    mytcpsocket.h
     1 #include "mytcpsocket.h"
     2 
     3 MyTcpSocket::MyTcpSocket()
     4 {
     5     QObject::connect(this,SIGNAL(readyRead()),this,SLOT(readDataSlot()));
     6     QObject::connect(this,SIGNAL(disconnected()),this,SLOT(disconnectSlot()));
     7 }
     8 
     9 MyTcpSocket::~MyTcpSocket()
    10 {
    11 
    12 }
    13 
    14 void MyTcpSocket::readDataSlot()
    15 {
    16     //读取数据的两种方式
    17     qDebug()<<"read data";
    18     QString msg;
    19     QByteArray ba;
    20     ba.resize(this->bytesAvailable());
    21     this->read(ba.data(),ba.size());
    22     msg = QString(ba);
    23     qDebug()<<"ip:"<<this->peerAddress().toString();
    24 
    25 //    QTextStream out(this),in(this);
    26 //    QString msg;
    27 //    out >> msg;
    28     emit sockReadDataSignal(this->socketDescriptor(),msg);
    29 }
    30 
    31 void MyTcpSocket::disconnectSlot()
    32 {
    33     qDebug()<<"disconnect";
    34     emit disconnectSignal(this->socketDescriptor());
    35 }
    mytcpsocket.cpp
     1 #ifndef MYTCPSERVER_H
     2 #define MYTCPSERVER_H
     3 
     4 #include <QObject>
     5 #include <QHostAddress>
     6 #include <QTcpServer>
     7 #include <QDebug>
     8 #include <QList>
     9 #include "mytcpsocket.h"
    10 
    11 class MyTcpServer : public QTcpServer
    12 {
    13    Q_OBJECT
    14 public:
    15     MyTcpServer();
    16     ~MyTcpServer();
    17 
    18 protected:
    19     void incomingConnection(qintptr socketDescriptor);
    20 
    21 
    22 private slots:
    23     void disconnectSlot(qintptr);
    24     void answerMsgSlot(qintptr,QString);
    25 
    26 signals:
    27     void serverReadDataSignal(qintptr,QString);
    28 
    29 private:
    30     QList<MyTcpSocket *> clients;
    31 
    32 };
    33 
    34 #endif // MYTCPSERVER_H
    mytcpserver.h
     1 #include "mytcpserver.h"
     2 
     3 MyTcpServer::MyTcpServer()
     4 {
     5     listen(QHostAddress::Any,8080);
     6 }
     7 
     8 MyTcpServer::~MyTcpServer()
     9 {
    10 
    11 }
    12 
    13 void MyTcpServer::incomingConnection(qintptr socketDescriptor)//when a new connection is available.
    14 {
    15     qDebug()<<"connect success";
    16     MyTcpSocket  *sock = new MyTcpSocket;
    17     sock->setSocketDescriptor(socketDescriptor);
    18     QObject::connect(sock,SIGNAL(disconnectSignal(qintptr)),this,SLOT(disconnectSlot(qintptr)));
    19     QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SIGNAL(serverReadDataSignal(qintptr,QString)));
    20     QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SLOT(answerMsgSlot(qintptr,QString)));
    21     clients << sock;
    22 }
    23 
    24 
    25 void MyTcpServer::disconnectSlot(qintptr sockfd)
    26 {
    27 //    MyTcpSocket *sock = new MyTcpSocket;
    28 //    sock->setSocketDescriptor(socketDescriptor);
    29     qDebug()<<"delete client "<<sockfd;
    30     for (int i = 0; i < clients.size(); ++i)
    31     {
    32         if (clients.at(i)->socketDescriptor() == sockfd)
    33             clients.removeAt(i);
    34     }
    35 
    36    // delete sock;
    37 }
    38 
    39 void MyTcpServer::answerMsgSlot(qintptr sockfd, QString msg)
    40 {
    41     for (int i = 0; i < clients.size(); ++i)
    42     {
    43         if(clients.at(i)->socketDescriptor() != sockfd)
    44         {
    45             QTextStream in(clients.at(i));
    46             in << sockfd << ":" << msg << endl;
    47         }
    48     }
    49 }
    mytcpserver.cpp
     1 #ifndef MYTCPSERVERWIDGET_H
     2 #define MYTCPSERVERWIDGET_H
     3 
     4 #include <QWidget>
     5 #include "mytcpserver.h"
     6 #include <QObject>
     7 
     8 namespace Ui {
     9 class MyTcpServerWidget;
    10 }
    11 
    12 class MyTcpServerWidget : public QWidget
    13 {
    14     Q_OBJECT
    15 
    16 public:
    17     explicit MyTcpServerWidget(QWidget *parent = 0);
    18     ~MyTcpServerWidget();
    19 
    20 private slots:
    21     void widgetReadDataSlot(qintptr,QString);
    22 
    23 private:
    24     Ui::MyTcpServerWidget *ui;
    25     MyTcpServer *tcpserver;
    26 };
    27 
    28 #endif // MYTCPSERVERWIDGET_H
    mytcpserverwidget.h
     1 #include "mytcpserverwidget.h"
     2 #include "ui_mytcpserverwidget.h"
     3 
     4 MyTcpServerWidget::MyTcpServerWidget(QWidget *parent) :
     5     QWidget(parent),
     6     ui(new Ui::MyTcpServerWidget)
     7 {
     8     ui->setupUi(this);
     9     tcpserver = new MyTcpServer; //connect to the server
    10     QObject::connect(tcpserver,SIGNAL(serverReadDataSignal(qintptr,QString)),this,SLOT(widgetReadDataSlot(qintptr,QString)));
    11 
    12 }
    13 
    14 MyTcpServerWidget::~MyTcpServerWidget()
    15 {
    16     delete ui;
    17 }
    18 
    19 
    20 void MyTcpServerWidget::widgetReadDataSlot(qintptr sock, QString msg)
    21 {
    22     QString id;
    23     id = QString::number(sock);
    24     ui->listWidget->addItem(id+":"+msg);
    25 }
    mytcpserverwidget.cpp
     1 #include "mytcpserverwidget.h"
     2 #include <QApplication>
     3 
     4 int main(int argc, char *argv[])
     5 {
     6     QApplication a(argc, argv);
     7     MyTcpServerWidget w;
     8     w.show();
     9 
    10     return a.exec();
    11 }
    main.cpp

    视频教程在汇文的网校上也是有的:www.huiwen.com 

    最后附带一个写的很好的blog

    Qt浅谈之十六:TCP和UDP(之一)

  • 相关阅读:
    python基础学习Day15 面向对象、类名称空间、对象名称空间 (2)
    python基础学习Day14 内置函数 匿名函数
    python基础学习Day12 生成器、列表推导式、字典的表达式、字典键值对的互换、集合推导式
    python基础学习Day11 函数名的应用、闭包、迭代器
    python基础学习Day10 函数形参的动态参数、*args **kwargs 命名空间 global 与 nonlocal
    python基础学习Day9 函数的初识,实参、形参、
    定时器定时循环执行和只执行一次
    CocoaPods 安装和使用
    tableView的cell之间间隔显示空白区域
    去掉tableView空白区域的分割线
  • 原文地址:https://www.cnblogs.com/panhao/p/4670999.html
Copyright © 2020-2023  润新知