Qt网络通信实战(聊天室小项目TCP实现)

一、UDP与TCP的区别

用一个表格来显示这两者的区别

比较项 TCP UDP
是否连接 面向连接 无连接
传输是否可靠 可靠 不可靠
流量控制 提供 不提供
工作方式 全双工 可以是全双工
应用场合 大量数据 少量数据
速度

我们这里采用TCP

//当有新的连接出现时就会调用此函数,和newConnection功能类似
void Server::incomingConnection(int socketDescriptor)
{

    TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
    //将新创建的通信套接字描述符指定为参数socketdescriptor
    tcpclientsocket->setSocketDescriptor(socketDescriptor);
    //将这个套接字加入客户端套接字列表中
    tcpclientsocketlist.append(tcpclientsocket);
    //接收到tcpclientsocket发送过来的更新界面的信号
    connect(tcpclientsocket, SIGNAL(updateserver(QString,int)), this, SLOT(slotupdateserver(QString,int)));
    connect(tcpclientsocket, SIGNAL(clientdisconnected(int)), this, SLOT(slotclientdisconnect(int)));
}

这个函数我就不多说了,注释我写的很清楚了

二、客户端程序,

在Qt中往往客户端比较简单,这里只要一个类即可,继承QTcpSocket

#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class TcpClient;
}

class TcpClient : public QWidget
{
    Q_OBJECT

public:
    explicit TcpClient(QWidget *parent = 0);
    ~TcpClient();

private slots:
    //用来处理连接成功的信号
    void slotconnectedsuccess();
    //接收服务器传过来的信息
    void slotreceive();
    //用来处理离开聊天室的信号
    void slotdisconnected();
    //发送按钮的槽函数
    void on_Send_clicked();
    //进入离开聊天室按钮
    void on_pushButtonEnter_clicked();

private:
    Ui::TcpClient *ui;
    //用来判断是否进入了聊天室
    bool status;

    int port;

    QHostAddress *serverIP;

    QString userName;

    QTcpSocket *tcpsocket;

};


#endif // TCPCLIENT_H
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QHostAddress>
#include <QMessageBox>
#include <QDebug>


TcpClient::TcpClient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpClient)
{
    ui->setupUi(this);
    //将进入聊天室的标志位置为false
    status = false;
    //端口为8888
    port = 5555;
    ui->Lineport->setText(QString::number(port));//界面中端口默认显示8888
    serverIP = new QHostAddress();
    //未进入聊天室内不能发送信息
    ui->Send->setEnabled(false);

}



TcpClient::~TcpClient()
{

    delete ui;

}

//用来处理连接成功的信号

void TcpClient::slotconnectedsuccess()
{
    //进入聊天室可以发送信息了
    ui->Send->setEnabled(true);
    //将进入聊天的按钮改为离开聊天室
    ui->pushButtonEnter->setText("离开聊天室");

    int length = 0;
    //将用户名发送给服务器
    QString msg= userName + " :Enter Chat Room";



    tcpsocket->write(msg.toUtf8().data());
}

void TcpClient::slotreceive()
{


    QByteArray array = tcpsocket->readAll();

    ui->textEdit->append(array);

}



void TcpClient::slotdisconnected()
{

    ui->Send->setEnabled(false);

    ui->pushButtonEnter->setText("进入聊天室");
}

void TcpClient::on_Send_clicked()
{
    if(ui->lineEditSend->text() == "")
    {
        return;
    }

    QString msg = userName + ":" + ui->lineEditSend->text();
    tcpsocket->write(msg.toUtf8().data());
    ui->lineEditSend->clear();
}

void TcpClient::on_pushButtonEnter_clicked()
{
    //首先判断这个用户是不是在聊天室中
        if(status == false)
        {
            //不在聊天室中就和服务器进行连接
            //从界面获取ip地址
            QString ip = ui->lineEditServerIp->text();
            //用这个函数判断IP地址是否可以被正确解析
            if(!serverIP->setAddress(ip))
            {
                //不能被正确解析就弹出一个警告窗口
                QMessageBox::warning(this, "错误", "IP地址不正确");
                return;
            }
            if(ui->lineEditUserName->text() == "")
            {
                //用户名不能为空,弹窗效果
                QMessageBox::warning(this, "错误", "用户名不能为空");
                return;
            }
            //从界面获取用户名
            userName = ui->lineEditUserName->text();
            //创建一个通信套接字,用来和服务器进行通信
            tcpsocket = new QTcpSocket(this);
             //和服务器进行连接
            tcpsocket->connectToHost(*serverIP, port);
            //和服务器连接成功能会触发connected信号
            connect(tcpsocket, SIGNAL(connected()), this, SLOT(slotconnectedsuccess()));
            //接收到服务器的信息就会触发readyRead信号
            connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(slotreceive()));
              //将进入聊天室的标志位置为true;
            status = true;
        }
        else//已经进入了聊天室
        {
           int length = 0;
            QString msg = userName + ":Leave Chat Room";

            //离开之前给服务器发送离开信息
            tcpsocket->write(msg.toUtf8().data());
            tcpsocket->disconnectFromHost();
            status = false;
            //离开聊天室就会触发disconnected信号
            connect(tcpsocket, SIGNAL(disconnected()), this, SLOT(slotdisconnected()));
        }
}
#include <QtGui/QApplication>
#include "tcpclient.h"
#include <QTextCodec>

int main(int argc, char *argv[])
{
    //让中文字符不乱的固定套路
    QTextCodec* code = QTextCodec::codecForName("ut    f-8");
    QTextCodec::setCodecForTr(code);
    QTextCodec::setCodecForLocale(code);
    QTextCodec::setCodecForCStrings(code);
    QApplication a(argc, argv);
    TcpClient w;
    w.show();
    
    return a.exec();
}

这里说明一下,网上很多例子会把qt中生成的代码贴出来,只要自己创建就好了,没必要贴出来,主函数贴出来是是因为给出了解决中文字符乱码的小套路,大家可以学习一下。

三、服务端程序

服务端分为三个类,我注释写的挺详细了,看不懂评论解答

#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include <QTcpSocket>
//此类为一个继承了Qtcpsocket的类,为多客户端服务
class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT
public:
    explicit TcpClientSocket(QObject *parent = 0);

signals:
    void updateserver(QString, int);//用来告诉tcpserver需要跟新界面的显示

    void clientdisconnected(int); //告诉server有客户端断开连接
    
protected slots:

    void receivedata();//处理readyRead信号读取数据

    void slotclientdisconnected();//客户端断开连接触发disconnected信号,这个槽函数用来处理这个信号

    
};

#endif // TCPCLIENTSOCKET_H
#include "tcpclientsocket.h"
#include <QDebug>

TcpClientSocket::TcpClientSocket(QObject *parent)
{

    //客户端发送数据过来就会触发readyRead信号
    connect(this, SIGNAL(readyRead()), this, SLOT(receivedata()));
    connect(this, SIGNAL(disconnected()), this, SLOT(slotclientdisconnected()));
}


void TcpClientSocket::receivedata()
{


    int length = 10;
    //读取客户端发送的信息并且发送信号给server端
    QByteArray array = readAll();
    QString msg = array;
    emit updateserver(msg,length);

}



void TcpClientSocket::slotclientdisconnected()
{
    emit clientdisconnected(this->socketDescriptor());
}
#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QList>
#include <QObject>
#include "tcpclientsocket.h"

class Server : public QTcpServer
{
    Q_OBJECT
public:
    Server(QObject *parent = 0,int port=0);
    
    QList<TcpClientSocket*> tcpclientsocketlist;

protected:

    void incomingConnection(int socketDescriptor);//只要出现一个新的连接,就会自动调用这个函数

protected slots:

    void slotupdateserver(QString, int);//用来处理tcpclient发过来的信号

    void slotclientdisconnect(int);



signals:

    void updateserver(QString, int);//发送信号给界面,让界面更新信息
    
};

#endif // SERVER_H
#include "server.h"
#include <QHostAddress>
#include <QDebug>


Server::Server(QObject *parent, int port):QTcpServer(parent)
{
    listen(QHostAddress::Any, port); //监听

}
//当有新的连接出现时就会调用此函数,和newConnection功能类似
void Server::incomingConnection(int socketDescriptor)
{

    TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
    //将新创建的通信套接字描述符指定为参数socketdescriptor
    tcpclientsocket->setSocketDescriptor(socketDescriptor);
    //将这个套接字加入客户端套接字列表中
    tcpclientsocketlist.append(tcpclientsocket);
    //接收到tcpclientsocket发送过来的更新界面的信号
    connect(tcpclientsocket, SIGNAL(updateserver(QString,int)), this, SLOT(slotupdateserver(QString,int)));
    connect(tcpclientsocket, SIGNAL(clientdisconnected(int)), this, SLOT(slotclientdisconnect(int)));
}

void Server::slotupdateserver(QString msg, int length)
{
    //将这个信号发送给界面
    emit updateserver(msg, length);
    //将收到的信息发送给每个客户端,从套接字列表中找到需要接收的套接字
    for(int i = 0; i < tcpclientsocketlist.count(); i++)

    {

        QTcpSocket *item = tcpclientsocketlist.at(i);

        item->write(msg.toUtf8().data());
    }
}

void Server::slotclientdisconnect(int descriptor)
{
    for(int i = 0; i < tcpclientsocketlist.count(); i++)
    {
        QTcpSocket *item = tcpclientsocketlist.at(i);


        if(item->socketDescriptor() == descriptor)
        {
            //如果有客户端断开连接, 就将列表中的套接字删除
            tcpclientsocketlist.removeAt(i);
            return;
        }
    }
    return;
}

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QWidget>
#include"server.h"
#include <QString>

namespace Ui {
class Tcpserver;
}

class Tcpserver : public QWidget
{
    Q_OBJECT
    
public:
    explicit Tcpserver(QWidget *parent = 0);
    ~Tcpserver();
    
private:
    Ui::Tcpserver *ui;
    int port;
    Server* server;
public slots:
    void slotupdateserver(QString, int);//接收到server发过来的信号就更新界面的信息
private slots:
    void on_createbutoon_clicked();
};

#endif // TCPSERVER_H
#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QString>
#include <QDebug>

Tcpserver::Tcpserver(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Tcpserver)
{
    ui->setupUi(this);
    port=5555;
    ui->portline->setText(QString::number(port));
    server  = new Server(this, port);
    connect(server, SIGNAL(updateserver(QString, int)), this, SLOT(slotupdateserver(QString, int)));
   // ui->pushButtonCreateChattingRoom->setEnabled(false);
}

Tcpserver::~Tcpserver()
{
    delete ui;
}
void Tcpserver::slotupdateserver(QString msg, int length)

{
   

    ui->textEdit->append(msg);

}

void Tcpserver::on_createbutoon_clicked()
{
    server  = new Server(this, port);

    connect(server, SIGNAL(updateserver(QString,int)) ,this, SLOT(slotupdateserver(QString,int)));

    ui->createbutoon->setEnabled(false);
}
#include <QtGui/QApplication>
#include "tcpserver.h"
#include <QTextCodec>

int main(int argc, char *argv[])
{

    QTextCodec* code = QTextCodec::codecForName("ut    f-8");
    QTextCodec::setCodecForTr(code);
    QTextCodec::setCodecForLocale(code);
    QTextCodec::setCodecForCStrings(code);
    QApplication a(argc, argv);
    Tcpserver w;
    w.show();
    
    return a.exec();
}

这里主要用到很多的信号和槽函数,一层套着一层,从最上面看到下面就好了。有些只是控件名,在Qt中统一一下即可。

猜你喜欢

转载自blog.csdn.net/Dachao0707/article/details/82287537