1

新しい接続が入ったときにスレッドを開始するこのサーバークラスを作成しました。場合によっては問題なく動作しますが、あまり安定していません。私はそれがどこで壊れているかを解決しようとしています。私のデバッガーはqmutexについて何かを教えてくれます。誰かが問題を見つけることができれば。ty

それはsignal&slotsで親と接続し、データも取り戻します。

ヘッダーは次のとおりです。

#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H

#include <QStringList>
#include <QTcpServer>
#include <QThread>
#include <QTcpSocket>
#include <string>
using namespace  std;


class FortuneServer : public QTcpServer
{
    Q_OBJECT

 public:
    FortuneServer(QObject *parent = 0);

public slots:


void procesServerString(string serverString);
void getStringToThread(string serverString);

protected:
void incomingConnection(int socketDescriptor);

private:
QStringList fortunes;

signals:

void procesServerStringToParent(string serverString);
void getStringToThreadSignal(string serverString);
};


class FortuneThread : public QObject
 {
Q_OBJECT

public:
FortuneThread(int socketDescriptor, QObject *parent);

public slots:

void getString();
void sendString(string sendoutString);

signals:

void error(QTcpSocket::SocketError socketError);
void fromThreadString(string serverString);
void finished();


private:
int socketDescriptor;
QString text;
QTcpSocket tcpSocket;
};

#endif

およびcc:

#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"

using namespace  std;

FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{

}

void FortuneServer::procesServerString(string serverString){

emit procesServerStringToParent(serverString);

}
void FortuneServer::getStringToThread(string serverString){

emit getStringToThreadSignal(serverString);

}

void FortuneServer::incomingConnection(int socketDescriptor)
{


FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));


QThread* thread = new QThread;

serverthread->moveToThread(thread);

connect(thread, SIGNAL(started()), serverthread, SLOT(getString()));
connect(serverthread, SIGNAL(fromThreadString(string)), this,        SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));

connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));

thread->start();

}



FortuneThread::FortuneThread(int socketDescriptor, QObject *parent)
: QObject(parent), socketDescriptor(socketDescriptor)
{



}

void FortuneThread::getString()
{

if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
    emit error(tcpSocket.error());
    cout<<"socket error"<<endl;
    return;
}
//in part
if(!tcpSocket.waitForReadyRead(10000)){

    emit finished();
    return;
}
int joj = tcpSocket.bytesAvailable();
char inbuffer[1024];
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);

emit fromThreadString(instring);

}   


void FortuneThread::sendString(string sendoutString)
{       


//out part
char buffer[1024];
int buffer_len = 1024;
int bytecount;

memset(buffer, '\0', buffer_len);


string outstring = sendoutString;



int TempNumOne= (int)outstring.size();

for (int a=0;a<TempNumOne;a++)
    {
        buffer[a]=outstring[a];
    }

QByteArray block;
block = buffer;



tcpSocket.write(block);
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
}

これは親からのものです:

//server start

QHostAddress adr;
adr.setAddress( QString("127.0.0.1") );
adr.toIPv4Address();
quint16 port = 1101;

if (!server.listen( adr, port)) {
  QMessageBox::critical(this, tr("CR_bypasser"),
      tr("Unable to start the server: %1.")
      .arg(server.errorString()));
  close();
  return;
}

QString ipAddress;
ipAddress = server.serverAddress().toString();
statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
  "Run the Fortune Client example now.")
  .arg(ipAddress).arg(server.serverPort()));

connect (&server, SIGNAL(procesServerStringToParent(string)), this,  SLOT(procesServerString(string))); 
connect (this, SIGNAL(StringToServer(string)), &server, SLOT(getStringToThread(string))); 

編集:私がやろうとしていること:

以前に提供したリンクで行われたようなソケットを使用して、ゲーム内の座標の文字列やその他のものを送信するために作成したクライアント(ゲームエンジン(Cryengine)の一部)があります。これは問題なく動作します。「127.0.0.1」ポート1101でデータを取得します。このデータを自分のプログラムで評価する必要があります。このTableViewクラスでは、文字列から取得した座標を収集し、座標からデータを呼び出して、次に、この新しい文字列をサーバー経由でgameengineに返します。ゲームでは、オブジェクトをクリックしてcoorを取得し、それから文字列(coor、entityidなどを含む)を作成し、この文字列をサーバーに送信します。サーバーは、TableViewから計算された情報を返します。この一方向のフローが必要なのは、文字列を送信している1つのクライアントだけです。recv(hsock、buffer、buffer_len、0)についてはよくわかりません、ゲームで文字列の送信を担当するノードは、文字列が返されるのを待つと思いますか?これは私の最初のプログラムの1つです。私は本当に混乱しています...

4

2 に答える 2

6

あなたが提示するコードは、カーゴカルトコーディングの典型です。明らかに問題を解決することを期待して、さまざまな不要なことを行います。

可能性のあるクラッシャー..。

コードには多くの問題がありますが、クラッシュの原因は次のとおりだと思いますtcpSocket.write(block)。ゼロで終了する文字列をネットワークに送信しません。ブロックはゼロで終了しますが、バイト配列への割り当ては、このゼロ終了をsize()QByteArrayのに追加しません。次のコードは、バイト配列の内容の内部にゼロの終了バイトがある場合でも、1を出力します。

QByteArray arr = "a";
qDebug("len=%d", arr.size());

受信コードはゼロ終了を期待していますが、受信することはありません。次に、ゼロで終了していないバッファをstd::string次の場所に割り当てます。

string instring;
instring = inbuffer;
instring.resize(joj);

std::string & std::string::operator=(const char*)その後のサイズ変更はカーゴカルトです。おそらく、バッファを通過した後で問題を修正しようとしています。

これを、それを修正することが正しい方法であると解釈しないでください。全くない。続行する正しい方法は、あなたが書いたコードを削除し、それを正しく行うことです。助けにならない不必要な呪文を大量に使わずに。

...そして他のすべての問題

あなたは魔法を信じるという罠に陥り、さまざまなフォーラムで際限なく永続しています。

スレッドは魔法のオブジェクトではなく、問題が解決することを期待して、そこにある問題に適用することができます。スレッドが魔法であると人々に思わせる理由はわかりませんが、経験則では、誰かが「ああ、スレッドを試してみるべきだ」と言った場合、それらはおそらく間違っています。彼らがネットワーキングに関して、彼らはほとんど正しくない、役に立たない、そしてあなたの問題をまったく理解していないと言った場合(あなたもそうではないようです)。多くの場合、問題を明確に理解しない限り、スレッドは役に立ちません。Qtのネットワークシステムは非同期です。関数を使用しない場合でも、コードの実行がブロックされることはありませんwaitxxxx()。ちなみに、それらを使用するべきではないので、ここではすべてが良好です。バジリオンスレッドは必要ありません。

したがって、着信接続ごとに新しいスレッドを開始する必要はまったくありません。サーバーのパフォーマンスが低下します。特に、コンテキストの切り替えとスレッドの作成/破棄のオーバーヘッドが各接続に追加されるため、サーバーが単純な処理を行う場合はそうです。システムの各コアあたり2スレッド未満が必要なのでQThread::idealThreadCount()、プール内のスレッド数を使用することから始めるとよいでしょう。

また、ネットワークスレッドを使用してデータを受信し、fromThreadString(string)信号を送信するため、スレッド化のメリットも失われます。シグナルはアプリケーションのメインスレッドに送信されると思います。ネットワークソケットから大量のバイトを受信するのは非常に簡単なので、これはばかげています。スレッドは何の作業も行いません。スレッドが行うすべての作業は、作成と削除に無駄になります。

以下のコードは、Qt APIを正しく使用して、ラウンドロビン方式で物理コア全体に作業を分散するクライアントサーバーシステムを実装する方法の簡単な例です。それはかなりうまくいくはずです。Qtに含まれているFortuneクライアントの例は、実際には非常に残念です。なぜなら、それは物事を進めるための正確に間違った方法だからです。

気付くのは:

  1. それは完全に些細なことではありません。Qtの方が役立つかもしれませんが、そうではありません。

  2. クライアントと送信者の両方がスレッドプールからスレッドに移動されます。

  3. 切断されたクライアントは削除されず、トレッドプールによって保持されているクライアントのリストに戻されるだけです。クライアントが呼び出されたときに再利用されます。

  4. QThreadはから派生していません。QTcpServerは、ソケットハンドルにアクセスするためにのみ派生しています。

  5. 名前がで始まる関数wait()は使用されません。すべてが非同期で処理されます。

  6. newConnection(int)ThreadPoolは、クライアントのスロットに対してルックアップされたQMetaMethodを保持します。QMetaObject::invokeMethod()毎回調べなければならないので、これは使用するよりも高速です。

  7. メインスレッドで実行されているタイマーは、最初の送信者を削除することにより、シグナルスロットチェーンを開始します。各送信者が削除されると、次の送信者が削除されます。最終的に、最後の送信者はquit()スレッドプールのスロットを開始します。後者は、finished()すべてのスレッドが実際に終了したときにシグナルを発行します。

#include <QtCore/QCoreApplication>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QMetaMethod>

// Processes data on a socket connection
class Client : public QObject
{
    Q_OBJECT
public:
    Client(QObject* parent = 0) : QObject(parent), socket(new QTcpSocket(this))
    {
        connect(socket, SIGNAL(readyRead()), SLOT(newData()));
        connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                SLOT(newState(QAbstractSocket::SocketState)));
        qDebug("Client()");
    }
    ~Client() { qDebug("~Client()"); }
signals:
    void done();
public slots:
    void newConnection(int descriptor) {
        socket->setSocketDescriptor(descriptor);
    }
private slots:
    void newData() {
        QByteArray data = socket->readAll();
        if (0) qDebug("got %d bytes", data.size());
        if (0) qDebug("got a string %s", data.constData());
        // here we can process the data
    }
    void newState(QAbstractSocket::SocketState state) {
        qDebug("client new state %d", state);
        if (state == QAbstractSocket::UnconnectedState) { emit done(); }
    }
protected:
    QTcpSocket* socket;
    int descriptor;
};

// Connects to a client and sends data to it
class Sender : public QObject
{
    Q_OBJECT
public:
    Sender(const QString & address, quint16 port, QObject * parent = 0) :
        QObject(parent), socket(new QTcpSocket(this)),
        bytesInFlight(0), maxBytesInFlight(65536*8)
    {
        connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                SLOT(newState(QAbstractSocket::SocketState)));
        connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(sentData(qint64)));
        socket->connectToHost(address, port);
        qDebug("Sender()");
    }
    ~Sender() { qDebug("~Sender()"); }
protected:
    // sends enough data to keep a maximum number of bytes in flight
    void sendData() {
        qint64 n = maxBytesInFlight - bytesInFlight;
        if (n <= 0) return;
        bytesInFlight += n;
        socket->write(QByteArray(n, 44)); // 44 is the answer, after all
    }
protected slots:
    void sentData(qint64 n) {
        bytesInFlight -= n;
        Q_ASSERT(bytesInFlight >= 0);
        sendData();
    }
    void newState(QAbstractSocket::SocketState state) {
        qDebug("sender new state %d", state);
        if (state == QAbstractSocket::ConnectedState) sendData();
    }
protected:
    QTcpSocket* socket;
    qint64 bytesInFlight;
    qint64 maxBytesInFlight;
};

// Keeps track of threads and client objects
class ThreadPool : public QTcpServer
{
    Q_OBJECT
public:
    ThreadPool(QObject* parent = 0) : QTcpServer(parent), nextThread(0) {
        for (int i=0; i < QThread::idealThreadCount(); ++i) {
            QThread * thread = new QThread(this);
            connect(thread, SIGNAL(finished()), SLOT(threadDone()));
            thread->start();
            threads << thread;
        }
        const QMetaObject & mo = Client::staticMetaObject;
        int idx = mo.indexOfMethod("newConnection(int)");
        Q_ASSERT(idx>=0);
        method = mo.method(idx);
    }
    void poolObject(QObject* obj) const {
        if (nextThread >= threads.count()) nextThread = 0;
        QThread* thread = threads.at(nextThread);
        obj->moveToThread(thread);
    }
protected:
    void incomingConnection(int descriptor) {
        Client * client;
        if (threads.isEmpty()) return;
        if (! clients.isEmpty()) {
            client = clients.dequeue();
        } else {
            client = new Client();
            connect(client, SIGNAL(done()), SLOT(clientDone()));
        }
        poolObject(client);
        method.invoke(client, Q_ARG(int, descriptor));
    }
signals:
    void finished();
public slots:
    void quit() {
        foreach (QThread * thread, threads) { thread->quit(); }
    }
private slots:
    void clientDone() {
        clients.removeAll(qobject_cast<Client*>(sender()));
    }
    void threadDone() {
        QThread * thread = qobject_cast<QThread*>(sender());
        if (threads.removeAll(thread)) delete thread;
        if (threads.isEmpty()) emit finished();
    }
private:
    QList<QThread*> threads;
    QQueue<Client*> clients;
    QMetaMethod method;
    mutable int nextThread;
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ThreadPool server;
    if (!server.listen(QHostAddress::Any, 1101)) qCritical("cannot establish a listening server");
    const int senderCount = 10;
    Sender *prevSender = 0, *firstSender = 0;
    for (int i = 0; i < senderCount; ++ i) {
        Sender * sender = new Sender("localhost", server.serverPort());
        server.poolObject(sender);
        if (!firstSender) firstSender = sender;
        if (prevSender) sender->connect(prevSender, SIGNAL(destroyed()), SLOT(deleteLater()));
        prevSender = sender;
    }
    QTimer::singleShot(3000, firstSender, SLOT(deleteLater())); // run for 3s
    server.connect(prevSender, SIGNAL(destroyed()), SLOT(quit()));
    qApp->connect(&server, SIGNAL(finished()), SLOT(quit()));
    // Deletion chain: timeout deletes first sender, then subsequent senders are deleted,
    // finally the last sender tells the thread pool to quit. Finally, the thread pool
    // quits the application.
    return a.exec();
}

#include "main.moc"

説明すると、ゲームエンジンが起動し、ローカルホスト上のポートへの接続が作成されます。Qtプログラムは、ポート1101でその接続を受け入れ、いくつかの文字列を受信し、それらを処理してから送り返すことになっています。

コードは、固定ポート番号での接続を受け入れるように変更されます。応答の返送を含むすべてのデータ処理は、newData()スロットから実行する必要があります。計算が非常に複雑な場合は、そのデータを別のスレッドに渡すこともできます。複雑とは、加算や乗算などの数万の演算、または数千の三角関数を意味します。

Senderクラスは例としてそこにあります。もちろん、ゲームエンジンが送信を行うため、Senderクラスは必要ありません。

于 2012-06-11T11:21:20.697 に答える
0

古い「間違った方法」のコードを機能させました。この部分はエラーがあった場所だったと思います:

//removed
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();

..。

#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"

using namespace  std;

FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{

}

void FortuneServer::procesServerString(string serverString){

emit procesServerStringToParent(serverString);

}
void FortuneServer::getStringToThread(string serverString){

emit getStringToThreadSignal(serverString);

}

void FortuneServer::incomingConnection(int socketDescriptor)
{


FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));


QThread* thread = new QThread;

serverthread->moveToThread(thread);




connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));

thread->start();

}



FortuneThread::FortuneThread(int socketDescriptor, QObject *parent): QObject(parent),   socketDescriptor(socketDescriptor)

{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
    emit error(tcpSocket.error());
    cout<<"socket error"<<endl;
    emit finished();
    return;
}




connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(getString()));
//connect(&tcpSocket, SIGNAL(disconnected()), this, SLOT(ondisconnected()));

}

void FortuneThread::getString()
{

int joj = tcpSocket.bytesAvailable();
if(joj==0){
    tcpSocket.disconnectFromHost();
    emit finished();
    return;
}
char inbuffer[1024];
int buffer_len = 1024;
memset(inbuffer, '\0', buffer_len);
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);

emit fromThreadString(instring);

}   


void FortuneThread::sendString(string sendoutString)
{   
char buffer2[1024];
int buffer_len = 1024;
memset(buffer2, '\0', buffer_len);
strcat(buffer2,sendoutString.c_str());

tcpSocket.write(buffer2,buffer_len);

 }

void FortuneThread::ondisconnected()
{


emit finished();


}
于 2012-06-13T15:25:56.757 に答える