首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > QT开发 >

QTCreatot做一个多线程服务器,通信有有关问题

2012-04-02 
QTCreatot做一个多线程服务器,通信有问题QTCreator 做一个多线程TCP服务器,客户端以前别人就做好了,并定制

QTCreatot做一个多线程服务器,通信有问题
QTCreator 做一个多线程TCP服务器,客户端以前别人就做好了,并定制了通信协议,现在我要用QT做一个服务器,要求能同时和多个客户机按通信协议通信。遇到了点问题,我调了很久都没解决,大家帮我看看。
通信协议是:
1.服务器向客户机发送命令(GET);
2.客户机发送START确认开始通信;
3.服务器发送AGAIN允许客户机发送;
4.接收客户机发送过来的信息并保存到缓冲区(QString)中;
5.检索缓冲区有没有客户机发送过来的结束命令(FINISH);
6.有:发送接收成功标志(SUCCESS)结束通信,存文件;
7.没有:转3;
如此往复直到检索到缓冲区中有客户机发过来的结束标志。
服务器要求能同时向多个客户机发送GET命令,并发的接收客户机发过来的信息,并在收到结束标志后将收到的信息存文件,信息不能发生串扰。
我的思路:
1.利用QT的多线程;
2.并不是为每个连接上的用户开启一个线程,而是为每个需要通信的客户端开启一个线程;
3.程序有GUI界面,每当有一个客户机连接到服务器以后在GUI上显示出来,并提供复选框,由用户勾选需要通信的客户端,并通过点击Get按钮向所选择的客户端发送GET命令,进行通信和信息处理;
问题是:
当我向多个客户发送GET的时候,程序偶尔会崩溃,偶尔成功但是保存的数据发生串扰(比如本来是客户1的数据却保存到了客户2的文件中)。我调了很久,没解决。
在线程的run()函数中:connect(sockfd, SIGNAL(readyRead()), this,SLOT(recvData()),Qt::DirectConnection);指定为Qt::DirectConnectio,那么recvData(),是否是在子线程之中执行?但是如果我指定为Qt::BlockingQueuedConnection程序通信依然存在问题。请问大家我的设计思想有没有问题,实现方法有没有问题。
主要代码如下,另附上源码包,http://download.csdn.net/detail/toney_ho/4058244
,大家可以下载下来调试运行一下,看是哪儿的问题,可能下载下来在QTCreator中容易看清楚点。
可能程序较长,大家耐心看一下共同进步。

C/C++ code
//tcpserver.h#ifndef TCPSERVER_H#define TCPSERVER_H#include "tcpthread.h"#include <QTcpServer>class tcpserver : public QTcpServer{    Q_OBJECTpublic:    explicit tcpserver(QObject *parent = 0);    //QList<int> clientDescriptorList;signals:    void newRow(int);    void displayInfo(int,QString, int);    void updateBar(int, qint64);    void signal_send_command(int,int);public slots:    void incomingConnection(int socketDescriptor);    void slot_send_command(int,int);};#endif//tcpthread.h#ifndef TCPTHREAD_H#define TCPTHREAD_H#include <QThread>#include <QTcpSocket>#include <QtNetwork>#define SEND_AGIAN         2#define FOUND_FINISH       3#define SEND_SUCCESS       4#define SEND_GET           10#define FOUND_START        11#define TEST_OTHER         98#define DISPLAY_BUFFER     97#define RECV_FILE_SUCCESS  99#define SAVE_FILE_SUCCESS  100#define GET        "GET"#define START      "START"#define AGAIN      "AGIAN"#define FINISH     "FINISH"#define SUCCESS    "SUCCESS"class QFile;class QTcpSocket;class TcpThread : public QThread{    Q_OBJECTpublic:    TcpThread(int socketDescriptor,int command,  QObject *parent = 0);    void run();    QTcpSocket *sockfd;    int socketDescriptor;    int command;signals:    void error(QTcpSocket::SocketError socketError);    void displayInfo(int,QString, int);    void updateBar(int, qint64);public slots:    int processBuffer(QString );    void recvData();};#endif // TCPTHREAD_H//widget.h#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include "tcpthread.h"#include "tcpserver.h"class QDialogButtonBox;class QTcpSocket;namespace Ui {    class Widget;}class Widget : public QWidget{    Q_OBJECTpublic:    explicit Widget(QWidget *parent = 0);    ~Widget();     TcpThread *thread ;private:    Ui::Widget *ui;    tcpserver tcpServer;    QTime *currenttime;private slots:    void on_pushButtonClearLog_clicked();    void on_pushButtonSaveLog_clicked();    void on_quitButton_clicked();    void on_pushButtonGET_clicked();    void on_OkButton_clicked();    void addNewRow(int);    void displayInfo(int,QString, int);    void updateBar(int, qint64);signals:    void signal_send_command(int, int);};#endif // WIDGET_H//tcpserver.cpp#include "tcpserver.h"//构造函数tcpserver::tcpserver(QObject *parent) :    QTcpServer(parent){}//重新定义了incomingConnection这个虚函数,//开辟一个新的tcpsocket线程,从TcpServer获得socketDescriptor,//并完成相应的信号连接void tcpserver::incomingConnection(int socketDescriptor){        qDebug() <<socketDescriptor;        //clientDescriptorList.append(socketDescriptor);        emit newRow(socketDescriptor); //display new client        connect(this,SIGNAL(signal_send_command(int ,int)),this,SLOT(slot_send_command(int ,int)));}void tcpserver::slot_send_command(int sockDescriptor, int cmd){    TcpThread *thread = new TcpThread(sockDescriptor,cmd, this);    thread->start();    connect(thread,SIGNAL(finished()),this,SLOT(deleteLater()));    connect(thread,SIGNAL(displayInfo(int,QString,int)),this,SIGNAL(displayInfo(int,QString,int)));    connect(thread,SIGNAL(updateBar(int,qint64)),this,SIGNAL(updateBar(int,qint64)));}//tcpthread.cpp#include "tcpthread.h"#include <QtGui>#include <QtNetwork>//构造函数完成简单的赋值/TcpThread::TcpThread(int socketDescriptor,int command,  QObject *parent):        QThread(parent),socketDescriptor(socketDescriptor),command(command){}void TcpThread::run(){    sockfd = new QTcpSocket;     if (!sockfd->setSocketDescriptor(socketDescriptor))     {             emit error(sockfd->error());             return;     }     sockfd -> write(GET);     emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_GET);     connect(sockfd, SIGNAL(readyRead()), this,SLOT(recvData()),Qt::DirectConnection);     exec();}void TcpThread::recvData()  //接收数据{    static qint64 bytesReceived = 0 ; //收到的总字节    static QString buffer  = "";   //数据缓冲区    qint64 available = 0;    if ( ( available =  sockfd ->bytesAvailable() ) > 0 )    {        bytesReceived += available;        buffer.append(sockfd ->readAll());        //更新进度条        emit this->updateBar(socketDescriptor, bytesReceived);        //display buffer        emit this->displayInfo(socketDescriptor,buffer, DISPLAY_BUFFER);        if ( processBuffer(buffer) == 1)        {            emit this->updateBar(socketDescriptor, 100);  //更新进度条            goto GOTO_SEND_SUCCESS;        }        goto  GOTO_SEND_AGAIN;    GOTO_SEND_SUCCESS:        //send SUCCESS        sockfd -> write(SUCCESS);        emit this-> displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_SUCCESS);        return;    GOTO_SEND_AGAIN:        //send AGIAN        emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_AGIAN);        if (-1 == sockfd -> write(AGAIN))        {                qDebug() << "write error";                return;        }    }}int TcpThread::processBuffer(QString buffer){    static int END_FLAG_FINISH = 0 ;    static int positionSTART  = 0;    static int positionFINISH  = 0;    static QHostAddress fileName ;    static QFile *localFile ;    static QString  fileBuffer  = ""; //file buffer    END_FLAG_FINISH = 0;    if (-1 != (positionSTART  =  buffer.indexOf(START)) )  //found START     {           if (-1 != (positionFINISH  = buffer.indexOf(FINISH)))   //found FINISH            {                 emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), FOUND_FINISH);                 END_FLAG_FINISH = 1;                 fileBuffer = buffer.mid(positionSTART+36,positionFINISH -positionSTART - 36);                 fileName = sockfd->peerAddress();                 quint16 port = sockfd->peerPort();                 localFile = new QFile(fileName.toString()+(tr(".%1").arg(port))); //用户端的IP地址作为保存文件名                 if(!localFile->open(QFile::WriteOnly))                 {                     qDebug() << "open file error!";                      return -1;                 }                 QByteArray filetemp = fileBuffer.toLatin1();                if ( -1 == ( localFile->write(filetemp.data(),qstrlen(filetemp.data()))))                 {                     qDebug() <<"write file error!";                }                localFile->close();                fileBuffer.clear();                emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SAVE_FILE_SUCCESS);                return END_FLAG_FINISH;             }     }   return END_FLAG_FINISH;} 




[解决办法]
这个 goto语句一般不用的,你得意思是不是把所有客户端的信息都存到一起了 然后在存起来?我觉得多线程的时候设定的缓存puff多个线程同时写的时候会崩溃,觉得可能是这个缓存没设计好

探讨

没人啊????

[解决办法]
参照一下官方的例子,
void FortuneServer::incomingConnection(int socketDescriptor)
{
QString fortune = fortunes.at(qrand() % fortunes.size());
FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}

官方的例子在examples\network\threadedfortuneserver

这里为每一个连接启动一个新的线程。
[解决办法]
是你的static变量不正确使用导致,你将buffer定义成thread类的成员变量,不要用static变量,就不会造成数据串扰的问题了。

已测试过。

如下方法

qint64 bytesReceived = 0 ;
QString strtemp;
strtemp.sprintf("%d",socketDescriptor);
qint64 available = 0;
available = sockfd->bytesAvailable();
if(available>0)
{

buffer.append(sockfd->readAll());
buffer.append(strtemp);
qDebug()<<buffer;
}

显示结果: 

"15608156081560815608156081560815608156081560815608156081560815608156081560815608156081560815608156081560815608156081560815608156081560815608156081560815608" 
"05548055480554805548055480554805548055480554805548055480554805548055480554805548055480554805548055480554805548055480554805548055480554805548055480554805548"

我发送的数据为一个客户端发0,一个客户端发1

个人建议,尽量不要使用自己不是很熟练的编程技巧。
[解决办法]
你定义一个类的成员变量bytesReceived ,然后在构造函数中将bytesReceived = 0。就如同你的socketDescriptor,command一样。

我明白你的意思是想要bytesReceived变量记录收到的记录长度,成员变量在类中作用域是整个类而不是只局限于recvData函数中。


以下内容摘自网上:

全局变量在类实例化一个对象以后,在每个对象内都会产生一个对象的变量;
而静态变量在类实例化一个对象以后,他是属于类的变量,并不会随着对象的增加而增加,任何方式改变了类的静态变量后,其他的对象访问到(通过一定方式)的静态变量都是改变以后的值。

所以说,静态变量只是类的变量!

经试验也证明,在实例化二次时(二个客户端连接),你的static buffer内容会串在一起。

可能我上一帖的语气欠妥,抱歉:)
[解决办法]
C++类的成员函数中使用静态变量的陷阱

C++类的成员函数中,使用static变量时,该变量是放在类存储区,而不是对象存储区,所以所有该类的对象共享该静态变量。

如以下代码:

#include <stdio.h>
#include <iostream>
using namespace std;

class Cs
{
public:
 void t(void)
 {
static i=0;
cout<<i++<<endl;
 }
};


int main(int argc, char* argv[])
{
 Cs c1;
 Cs c2;
 Cs c3;
 c1.t();
 c2.t();
 c3.t();
 return 0;
}

 



最后输出的数字不是0,而是2

[解决办法]
如何才能达到向某个或者某几个客户端发送命令的目的呢?

相当于要在界面中控制线程的工作状态,从这个思路来做,可以将thread类做成tcpserver类的public成员变量,然后在界面中tcpserver->thread->get()这样的方式来实现看看,当然也可以用类似信号的方式。

热点排行