专业课程设计之客户与服务器程序的同步与通信机制的设计(二)TCP通信

583 篇文章 127 订阅
36 篇文章 1 订阅

源码下载地址为:

http://download.csdn.net/detail/qq78442761/9856423


---------------------------------------------------------------------------------------------------------------------------------------------------------

上一节为项目介绍,未读的博友请先阅读:

http://blog.csdn.net/qq78442761/article/details/72819181


---------------------------------------------------------------------------------------------------------------------------------------------------------

关于TCP通信下面说明下TCP的流程:



所以TCP服务器,初始化,绑定,监听,接收连接,然后处理

注意:本IDE为QtCreator5.7,请用5以上的版本打开。


服务器源码如下图:



服务器如何TCP服务器,初始化,绑定,监听,接收连接,然后处理,如何实现这些功能呢?

在Server.h里面提供了这些过程:

#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QObject>
#include <QMutex>
#include <QTimerEvent>
#include "tcpclientsocket.h"
#include "resource.h"


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

    virtual void timerEvent ( QTimerEvent * event );

    bool resourceNumReduce(int reduceNum);

signals:
    void updateServer(QString,int);
public slots:
    void updateClients(QString,int,QHostAddress,QString);
    void slotDisconnected(int);
protected:
    void incomingConnection(int socketDescriptor);

private:
    int m_timerId; // 每个Timer有一个id
    int resourceNumTemp;

    int peerNameInt;

};

#endif // SERVER_H


注意:具体是如何实现的呢?此处,不再说明,具体去看.cpp文件。本文开头提供源码和程序下载


如何得到连接上的客户端的信息的

这是tcpclientsocket.h所做的事情,源码如下:

#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include <QTcpSocket>
#include <QHostAddress>
#include <QObject>

class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT
public:
    TcpClientSocket(QObject *parent=0);
signals:
    void updateClients(QString,int,QHostAddress,QString);
    void disconnected(int);
protected slots:
    void dataReceived();
    void slotDisconnected();
};

#endif // TCPCLIENTSOCKET_H


如何实现客户机是消耗产品,还是单纯的连接产品呢?

下面这个函数给出了解决办法:

void TcpClientSocket::dataReceived()
{
    while(bytesAvailable()>0)
    {
        QHostAddress ipAddress;
        QString port;
        int length=bytesAvailable();
        QByteArray datagram;
        datagram.resize(bytesAvailable());
        read(datagram.data(),datagram.size());
        QString msg=QString::fromLocal8Bit(datagram);

        //判断是不是要消耗产品,如果要就记录IP和端口号
        if(msg.startsWith("B"))
        {
            //记录IP和端口号
            ipAddress=this->peerAddress();
            port=QString::number(this->peerPort());


            emit updateClients(msg,length,ipAddress,port);
        }
        else
        {
            //单纯的连接服务器
            emit updateClients(msg,length,ipAddress,"");

        }
    }
}


那么服务器改如何对客户机的数据进行处理呢?在server.cpp里面是这样处理的:

void Server::updateClients(QString msg, int length, QHostAddress ip, QString port)
{


    resourceNumTemp=Resource::getResourceNum();

    //服务器主动更新客户机
    if(msg.startsWith("Final"))
    {
        msg=msg.append(QString::number(Resource::getResourceNum(),10));
        msg=msg.append(".");
        int msgResourceNumLength=msg.length();
        for(int i=0;i<tcpClientSocketList.count();i++)
        {
            QTcpSocket *item=tcpClientSocketList.at(i);
            //qDebug()<<msg;
            if(item->write(msg.toLocal8Bit())!=msgResourceNumLength)
            {
                continue;
            }

        }
        return;
    }

    //客户连接
    if(msg.startsWith('Z'))
    {
        emit updateServer(msg,msg.length());
    }

    //对单个客户操作
    if(!port.isEmpty())
    {
        //提取用户名和数字
        QStringList list = msg.split(".");
        QString Name=list[1];
        QString Num=list[2];
        if(resourceNumReduce(Num.toInt()))
        {
            for(int i=0;i<tcpClientSocketList.count();i++)
            {
                QTcpSocket *item=tcpClientSocketList.at(i);

                if(item->peerAddress()==ip && item->peerPort()==port.toInt())
                {
                    QString msg="B.商品消耗成功!.";
                    item->write(msg.toLocal8Bit());
                    msg="B.";
                    msg=msg.append(Name);
                    msg=msg.append(".");
                    msg=msg.append(Num);

                    emit updateServer(msg,msg.length());

                    return;
                }
            }
        }
        else    //产品数量不够,客户消耗失败
        {
            for(int i=0;i<tcpClientSocketList.count();i++)
            {
                QTcpSocket *item=tcpClientSocketList.at(i);

                if(item->peerAddress()==ip && item->peerPort()==port.toInt())
                {
                    QString msg="C.商品消耗失败!.";
                    item->write(msg.toLocal8Bit());
                    msg="C.";
                    msg=msg.append(Name);
                    msg=msg.append(".");
                    msg=msg.append(Num);

                    emit updateServer(msg,msg.length());

                    return;
                }
            }
        }
    }
    //对单个客户操作


    //更新客户端产品数量
    QString msgResourceNum="A.";
    msgResourceNum=msgResourceNum.append(QString::number(Resource::getResourceNum(),10));
    msgResourceNum=msgResourceNum.append(".");
    int msgResourceNumLength=msgResourceNum.length();
    for(int i=0;i<tcpClientSocketList.count();i++)
    {
        QTcpSocket *item=tcpClientSocketList.at(i);
        if(item->write(msgResourceNum.toLocal8Bit())!=msgResourceNumLength)
        {
            continue;
        }
    }


}

所以服务器的部分就结束了。

下面看客户机的,如下图所示:




运行界面如下:



widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QListWidget>
#include <QTimerEvent>
#include <QCloseEvent>
#include <QPushButton>
#include <QHostAddress>
#include <QTcpSocket>
#include <QMessageBox>

#include <stdlib.h>
#include <time.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    virtual void timerEvent(QTimerEvent *event);
    virtual void closeEvent(QCloseEvent *event);
    ~Widget();

private:
    Ui::Widget *ui;

    bool status;
    int port;
    QHostAddress *serverIP;
    QString userName;
    QTcpSocket *tcpSocket;
    int m_timerId; // 定时器ID
    int AutoInrestatus;


    int randomNum;

    int speed;  //毫秒


public slots:
    void slotEnter();
    void slotConnected();
    void slotDisconnected();
    void dataReceived();
    void slotSend();
    void ConsumeBtnEvent();
    void AutoConsumeBtnEvent();
};

#endif // WIDGET_H


widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <qDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    srand((unsigned) time(NULL)); //用时间做种,每次产生随机数不一样

    QString NameArr[40]={"刘一","陈二","张三","李四","王五","赵六","孙七"
                         ,"周八","吴九","郑十","小明","小黄","小白","小红"
                         ,"小黑","小绿","大明","大黄","大白","大红","大黑"
                         ,"大绿","小哈","乌龟","蚂蚁","螃蟹","蜻蜓","飞机"
                         ,"坦克","大炮","火箭","蘑菇头","朱小明","陈翔","点点"
                         ,"闰土","茅台","妹爷","球球","腿腿"};

    int number = rand() % 41; //产生0-24的随机数
    userName=NameArr[number];
    AutoInrestatus=0;
    this->setWindowTitle(tr("客户端"));
    this->setFixedSize(this->width(),this->height());

    ui->IplineEdit->setText("127.0.0.1");
    ui->NamelineEdit->setText(userName);
    port=9999;
    status=false;
    ui->PortlineEdit->setText(QString::number(port));

    serverIP=new QHostAddress();

    ui->AutoConsumepushButton->setEnabled(false);
    ui->OkConsumepushButton->setEnabled(false);


    connect(ui->LinkSocketpushButton,SIGNAL(clicked(bool)),this,SLOT(slotEnter()));
    connect(ui->OkConsumepushButton,SIGNAL(clicked(bool)),this,SLOT(ConsumeBtnEvent()));
    connect(ui->AutoConsumepushButton,SIGNAL(clicked(bool)),this,SLOT(AutoConsumeBtnEvent()));

}

void Widget::timerEvent(QTimerEvent *event)
{
    if(ui->resourceNumlabel->text().toInt()==0)
    {
        QString msgFail="商品号为0,不能发送消息给服务器!";
        QListWidgetItem *msg_Item=new QListWidgetItem(msgFail);
        msg_Item->setTextColor(0xff969696);
        ui->ContenListWidget->insertItem(0,msg_Item);
        return;
    }
    QString msg="B."+userName+".1.";
    tcpSocket->write(msg.toLocal8Bit());
}
void Widget::closeEvent(QCloseEvent *event)
{
    if(status)
    {
        switch( QMessageBox::information(this,tr("提示"),tr("你确定退出该软件?"),tr("确定"), tr("取消"),0,1))
        {
        case 0:
            status=1;
            slotEnter();
            event->accept();
            break;
        case 1:
        default:
            event->ignore();
            break;
        }
    }
}

void Widget::AutoConsumeBtnEvent()
{
    AutoInrestatus++;
    if(AutoInrestatus%2==0) //偶数为没有自增的情况(或想让他停止自增)
    {
        ui->AutoConsumepushButton->setText("自动消耗");
        killTimer(m_timerId); // 关闭定时器
    }
    else    //奇数为在自增(或,想让他自增)
    {
        m_timerId = startTimer(ui->speedlineEdit->text().toInt());
        ui->AutoConsumepushButton->setText("停止消耗");
    }

}

void Widget::ConsumeBtnEvent()
{
    if(ui->ConsumelineEdit->text().isEmpty())
    {
        QMessageBox::information(this,tr("error"),tr("ConsumelineEdit is empty!"));
        return;
    }
    QString msg="B."+userName+"."+ui->ConsumelineEdit->text()+".";

    if(ui->resourceNumlabel->text().toInt()==0)
    {
        QString msgFail="商品号为0,不能发送消息给服务器!";
        QListWidgetItem *msg_Item=new QListWidgetItem(msgFail);
        msg_Item->setTextColor(0xff969696);
        ui->ContenListWidget->insertItem(0,msg_Item);
        return;
    }
    tcpSocket->write(msg.toLocal8Bit());
    ui->ConsumelineEdit->text();
}

void Widget::slotEnter()
{
    if(!status)
    {
        QString ip = ui->IplineEdit->text();
        if(!serverIP->setAddress(ip))
        {
            QMessageBox::information(this,tr("error"),tr("server ip address error!"));
            return;
        }

        if(ui->NamelineEdit->text()=="")
        {
            QMessageBox::information(this,tr("error"),tr("User name error!"));
            return;
        }

        userName=ui->NamelineEdit->text();

        tcpSocket = new QTcpSocket(this);
        connect(tcpSocket,SIGNAL(connected()),this,SLOT(slotConnected()));
        connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
        connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));


        tcpSocket->connectToHost(*serverIP,port);

        status=true;
    }
    else
    {
        int length=0;
        QString msg_demo=userName+tr("】断开服务器");
        QString msg="Z.【";
        msg=msg.append(msg_demo);

//        if((length=tcpSocket->write(msg.toUtf8(),msg.length()))!=msg. length())
//        {
//            return;
//        }

//        tcpSocket->disconnectFromHost();

//        status=false;


        QByteArray datasendEn=msg.toLocal8Bit();
        length=tcpSocket->write(datasendEn);

        tcpSocket->disconnectFromHost();
        status=false;
    }
}

void Widget::slotConnected()
{
    ui->AutoConsumepushButton->setEnabled(true);
    ui->OkConsumepushButton->setEnabled(true);
    ui->NamelineEdit->setEnabled(false);
    ui->IplineEdit->setEnabled(false);
    ui->PortlineEdit->setEnabled(false);

    ui->LinkSocketpushButton->setText(tr("断开服务器"));

    int length=0;
    QString msg_demo=userName+tr("】连接服务器");
    QString msg="Z.【";
    msg=msg.append(msg_demo);

    QByteArray datasendBg=msg.toLocal8Bit();
    if((length=tcpSocket->write(datasendBg))!=msg.length())
    {
        return;
    }
}

void Widget::slotSend()
{

}

void Widget::slotDisconnected()
{
    ui->NamelineEdit->setEnabled(true);
    ui->IplineEdit->setEnabled(true);
    ui->PortlineEdit->setEnabled(true);
    ui->AutoConsumepushButton->setEnabled(false);
    ui->OkConsumepushButton->setEnabled(false);
    ui->LinkSocketpushButton->setText(tr("连接服务器"));
}

void Widget::dataReceived()
{
    while(tcpSocket->bytesAvailable()>0)
    {
        QByteArray datagram;
        datagram.resize(tcpSocket->bytesAvailable());
        tcpSocket->read(datagram.data(),datagram.size());
        QString msg=QString::fromLocal8Bit(datagram);
        qDebug()<<msg;

        //判断协议
        char *msgTochTest;
        QByteArray ba = msg.toLocal8Bit();
        msgTochTest=ba.data();

        //服务器主动刷新Final
        if(msgTochTest[0]=='F')
        {
            QStringList listTemp = msg.split(".");
            QString msgupdate="服务器主动更新,数量为:";
            msgupdate=msgupdate.append(listTemp[1]);
            QListWidgetItem *msg_Item=new QListWidgetItem(msgupdate);
            ui->resourceNumlabel->setText(listTemp[1]);
            msg_Item->setTextColor(0x800080);
            ui->ContenListWidget->insertItem(0,msg_Item);
        }

        //头字母为C说明商品消耗失败
        if(msgTochTest[0]=='C')
        {
            QStringList listTemp = msg.split(".");
            QString msgFail=listTemp[1];
            QListWidgetItem *msg_Item=new QListWidgetItem(msgFail);
            msg_Item->setTextColor(0xffff0000);
            ui->ContenListWidget->insertItem(0,msg_Item);
        }


        //头字母为B说明商品消耗成功
        if(msgTochTest[0]=='B')
        {
            QStringList listTemp = msg.split(".");
            QString msgSucc=listTemp[1];
            QListWidgetItem *msg_Item=new QListWidgetItem(msgSucc);
            msg_Item->setTextColor(0xff0000ff);
            ui->ContenListWidget->insertItem(0,msg_Item);
        }

        //头字母为A说明刷新产品数量
        if(msgTochTest[0]=='A')
        {
            QStringList listTemp = msg.split(".");
            QString msgSucc=listTemp[1];
            ui->resourceNumlabel->setText(msgSucc);
            QString msgItem="获得服务器产品更新消息,产品更新后为:";
            msgItem=msgItem.append(msgSucc);
            ui->ContenListWidget->insertItem(0,msgItem);
        }


        //ui->ContenListWidget->addItem(msg.left(datagram.size()));
    }
}

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

此处和服务器很类似在此不再说明、


下面一节说数据共享,和互斥锁,和Qt中线程的创建

链接如下:http://blog.csdn.net/qq78442761/article/details/72819470


---------------------------------------------------------------------------------------------------------------------------------------------------------
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT1995

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

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

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

打赏作者

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

抵扣说明:

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

余额充值