TCP通信的服务器(两个客户端通过服务器进行数据中转)

实习的时候要用上QT开发,之前没用过,再看我之前的博客,我发现我什么都用过一点,却什么都不精。不过之前春招告诉我,JAVA这条路真难走,竞争太大了。真不如安心学C/C++,正好这次实习QT也是用的C++的,正好重新学习一下。

这次实习采用小组合作形式,内容是模拟一个只能家居系统,我负责TCP通信这一块(PC客户端与硬件客户端通信,中间有一个服务器),之前其实没有怎么用过,其实也算新手上路,也遇到挺多问题的。

我不确定自己说的对不对(下同),但是我这次遇到的问题主要是服务器要对两个客户端进行区分,而我没有发现socket有明确的标记(也可能仅仅是我没发现)可以用来区分(ip地址是会随局域网变化而变化的,端口是人为设定的,也没发现可以人为设定一些标记)。所以我就只能用蠢方法,就是一旦客户端与服务器成功连接,那么客户端需要发送一个“信息”,告诉服务器自己是谁。从而进行区分。

还有一个问题是数据转接问题,此时先假设通过上面方法连接了两个客户端,一个为“A”,一个为“B”,首先“A”向服务器发送了一段信息,这段信息需要转发给“B”,注意这时“A“和”B”是两个不同的对象,他们之间的信息是不互通的,所以是不能直接将得到的信息转发给“B”,我首先想到的是“static”变量,但是我发现这里的“static”和Java里的不一样,他仅仅是在这个类中的所有该类的对象共享该变量(具体百度),并不能在别的类中也共享变量,所以这个方案行不通。第二个方案是用extern关键字,这个我之前没用过,用不通,也就放弃了。最后还是用Qt里很强大的信号槽机制去解决。

connection.h:

#ifndef CONNECTION_H
#define CONNECTION_H

#include <QObject>
#include <QTcpSocket>

class Connection : public QObject
{
    Q_OBJECT
public:
    explicit Connection(QTcpSocket *pSocket,QObject *parent = 0);
    ~Connection();

    bool signal;//标记该socket连接的是"A"还是"B",true表示"A",false表示"B"
    QTcpSocket *m_socket;//默认收发数据的socket


signals:
    void SignalDisconnect();
    void SignalReadSend(QByteArray array,bool sig);
    void SignalRemoveThis();

public slots:
    void slotDisconnect();
    void slotReadData();
    void slotVerifyReadData();
    void readyToSend();

private:

    QByteArray m_buffer;
};

#endif // CONNECTION_H

serverwidget.h

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include <QTcpServer>
#include "connection.h"

namespace Ui {
class ServerWidget;
}

class ServerWidget : public QWidget
{
    Q_OBJECT

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

signals:
    void readSend();

private slots:
    void slotNewConnect();
    void on_pbNetConn_clicked();
    void slotDisconnect();
    void send();
    void setByte(QByteArray arr, bool sig);
    void removeSocket();


private:
    Ui::ServerWidget *ui;

    QTcpServer m_tcpServer;//定义一个服务器对象

    QByteArray byte;

    bool signal;//用于判断数据要发给哪一个客户端,true表示发给“A”客户端,否则“B”

    QVector<Connection *> m_List;//存放当前连接中给的客户端

    QHostAddress address;

};

#endif // SERVERWIDGET_H

connection.cpp

#include "connection.h"
#include <QDebug>


Connection::Connection(QTcpSocket *pSocket
                       , QObject *parent) : QObject(parent),m_socket(pSocket)
{

    connect(m_socket,&QTcpSocket::readyRead,this,
            &Connection::slotVerifyReadData);//首先接收一个身份信息,然后触发验证函数slotVerifyReadData

    connect(m_socket,&QTcpSocket::disconnected,this,
            &Connection::slotDisconnect);//当客户端断开连接时触发slotDisconnect发送信号源SignalDisconnect

}

Connection::~Connection()
{
    if(m_socket->state()==QTcpSocket::ConnectedState)
            m_socket->close();
}

//当该客户端断开连接时触发
void Connection::slotDisconnect()
{
    qDebug()<<7;
    emit SignalDisconnect();

}


//读取接收到的数据
void Connection::slotReadData()
{
    qDebug()<<8;

    if(m_socket->bytesAvailable()){

        m_buffer=m_socket->readAll();

        readyToSend();
    }
}

//用于验证是否为合法客户端
void Connection::slotVerifyReadData()
{
    qDebug()<<9;
    QByteArray arr;
    if(m_socket->bytesAvailable()){
        arr=m_socket->readAll();

        if(arr=="A"){//如果发送的信息是“A”,则判断它为"A"客户端
            disconnect(m_socket,&QTcpSocket::readyRead,this,
                       &Connection::slotVerifyReadData);//解除绑定,防止干扰之后的获取数据

            connect(m_socket,&QTcpSocket::readyRead,this,
                    &Connection::slotReadData);//绑定一个获取数据函数,当有数据发过来时触发
            signal=true;//代表着“A”客户端
        }

        else if (arr=="B"){//如果发送的信息是“B”,则判断它为"B"客户端

            disconnect(m_socket,&QTcpSocket::readyRead,this,
                       &Connection::slotVerifyReadData);//解除绑定,防止干扰之后的获取数据

            connect(m_socket,&QTcpSocket::readyRead,
                    this,&Connection::slotReadData);//绑定一个获取数据函数,当有数据发过来时触发

            signal=false;//代表着“B”客户端
        }
        else{//如果收到的信息不合法,那就发送信号源removeThis,将其移除
            emit SignalRemoveThis();
        }
    }
}

//接收到数据后就触发,告诉服务器可以进行数据转发了
void Connection::readyToSend()
{
    qDebug()<<10;
    //当然可以在这里加一些条件,比如数据要包含一定过得帧头帧尾,才能进行发送
    emit SignalReadSend(m_buffer,signal);
    m_buffer.clear();
}

serverwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"

ServerWidget::ServerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    connect(&m_tcpServer,&QTcpServer::newConnection,
            this,&ServerWidget::slotNewConnect);//当有新客户端连接时触发

    connect(this,&ServerWidget::readSend,this,&ServerWidget::send);//需要进行数据转发时触发
}

ServerWidget::~ServerWidget()
{
    delete ui;
}


//开启服务器监听连接,这是的第一步
void ServerWidget::on_pbNetConn_clicked()
{   qDebug()<<1;

    if(!m_tcpServer.isListening()){
        if(m_tcpServer.listen(QHostAddress::Any
                           ,ui->ldtPort->text().toInt())){
            ui->pbNetConn->setText("关闭");
            ui->ldtPort->setReadOnly(true);//设置为只读,不允许修改
        }
    }else{
        m_tcpServer.close();
        ui->pbNetConn->setText("开启");
        ui->ldtPort->setReadOnly(false);
    }
}

//用于移除非法客户端
void ServerWidget::removeSocket()
{
    qDebug()<<2;
    int a=m_List.size()-1;
    while(1){

       if(m_List.at(a)->m_socket->state()==QTcpSocket::ConnectedState){
         m_List.at(a)->m_socket->close();
         break;
       }

       else if(NULL == m_List.at(a))
           a--;
    }
}


//将新的连接添加到m_List中,表明当前服务器连接的客户端
void ServerWidget::slotNewConnect()
{   qDebug()<<3;
    if(m_tcpServer.hasPendingConnections()){

        QTcpSocket *newSocket=m_tcpServer.nextPendingConnection();//即将进行连接的客户端

        Connection *conn=new Connection(newSocket,this);//每一个socket对象与一个connection对象绑定

        m_List.append(conn);//存放到list中

        connect(conn,&Connection::SignalRemoveThis,this,
                &ServerWidget::removeSocket);//当客户端没有发来正确的身份验证信息,就触发removeSocket将它从list中移除

        connect(conn,&Connection::SignalDisconnect,this,
                &ServerWidget::slotDisconnect);//当客户端断开连接时触发slotDisconnect,将它从list中移除

        connect(conn,&Connection::SignalReadSend,this,
                &ServerWidget::setByte);//当接收到信息并且能够进行转发时触发,发给指定的客户端

        address =newSocket->peerAddress();//存放ip地址

        ui->textEdit->append(address.toString());//将ip地址显示在界面上

    }
}


//客户端断开连接时触发
void ServerWidget::slotDisconnect()
{
    qDebug()<<4;
    for(int i=0;i<m_List.size();i++){
        if(m_List.at(i) == (Connection *)sender()){
            if(m_List.at(i)->signal==true)
                ui->textEdit_2->append("A客户端断开连接");
            else if(m_List.at(i)->signal==false)
                ui->textEdit_2->append("B客户端断开连接");
            else
                ui->textEdit_2->append("无效客户端断开连接");
            m_List.remove(i);
            break;
        }
    }

}


void ServerWidget::send()
{
    qDebug()<<5;
    if(signal==false){//false表示数据是由"B"客户端发来的,所以要发给“A”
        for(int i=0;i<m_List.size();i++){//因为存放时是非定点存放,所以要找到“A”客户端只能便利,当然我觉得这里肯定可以优化
            if(m_List.at(i)->signal==true){

                if(m_List.at(i)->m_socket->state()==QAbstractSocket::ConnectedState){

                    m_List.at(i)->m_socket->write(byte);
                }

                return;
            }
        }
    }

    else{//发给"B"

        for(int i=0;i<m_List.size();i++){
            if(m_List.at(i)->signal==false){

                if(m_List.at(i)->m_socket->state()==QAbstractSocket::ConnectedState){

                    m_List.at(i)->m_socket->write(byte);
                }

                return;
            }
        }
    }

}


void ServerWidget::setByte(QByteArray arr, bool sig)
{
    qDebug()<<6;

    byte=arr;
    signal=sig;
    emit readSend();
}


main.cpp

#include "serverwidget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ServerWidget w;
    w.show();

    return a.exec();
}

注释很详细了,应该能看懂。提醒一下,记得在.pro文件里添加 network,并保存,才能使用tcp模块。

给一个效果图:
这里写图片描述

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
qt——服务器客户端进行tcp通信代码.rar是一个使用Qt框架编写的用于实现服务器客户端之间进行TCP通信的程序。TCP是一种高可靠性的传输层协议,通过它可以实现数据的可靠传输,具有较高的传输速度和较低的错误率。在程序中,服务器客户端都可以进行TCP通信,它们之间通过网络连接进行数据的传输和接收。 在程序中,使用了Qt提供的QTcpSocketQTcpServer两个类来实现TCP通信QTcpServer类用于实现服务器,它可以监听指定的口,等待客户端的连接请求,并实现TCP通信QTcpSocket类用于实现客户端,它可以主动发起连接请求,并与服务器进行TCP通信。 在程序中,服务器客户端都实现了数据的发送和接收,通过使用Qt提供的信号和槽机制来实现。信号表示某个事件的发生,槽表示某个事件的响应。当服务器客户端数据发送或接收时,发出相应的信号,程序响应相应的槽来实现数据的发送和接收。 此外,在程序中还使用了一些其他的Qt类和模块,如QDataStream、QByteArray和QHostAddress等,来实现数据的封装和解析,IP地址和口号的管理等功能。 总之,这个qt——服务器客户端进行tcp通信代码.rar是一个优秀的实现TCP通信的程序,通过它可以学习到Qt框架的使用,以及实现TCP通信的方法和技巧,对于想要开发网络应用的开发者来说是一个非常有用的代码库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lsjweiyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值