3

個人プロジェクト(マルチプレイヤーチェスゲーム)にC ++/QtTcpSocketを使用して認証システムを実装しようとしています。

私の友人はユーザーを確認する方法を提案しましたが、私はもっと簡単な方法があるのか​​、それとももっと良い方法があるのか​​尋ねたかったのです。Pythonのバックグラウンドから来て、主にこのプロジェクトを実行して、C++の理解を深めます。

私は私の友人が提案した方法を投稿し、おそらくより良い解決策を求めます。

彼はそれを一種の擬似コード方式で構築しました。サーバーはほとんど構築されています、私は今認証を実装したいと思っています

*乾杯

void process_packet(PACKET *pkt)
{
    switch(pkt->PacketID)
    {
        case 0: // let's say packet id 0 is the logon packet; packet contents are username and password
        {
            //let's say packet size is 101 bytes; packet id was already received, so get the other 100 bytes
            unsigned char BUFFER[101] = {0}; // i always add an extra byte to the end of the buffer to allow for off-by-one errors ^_^

            int result = recv_packet(pkt->cSocket, 100, BUFFER);

            if(result <= 0)
                return; // connection error; no packet data was received

            unsigned char *UserName = BUFFER+0; //+0 is not neccessary, but the username starts at the beginning. just getting the point across.
            unsigned char *PassWord = BUFFER+50;

            //side note: if we did "unsigned long *blah = BUFFER+4" or something, we would have to make sure the byte order is right. network byte order is BIG ENDIAN
            //  WINDOWS byte order is LITTLE ENDIAN

            result = QueryDatabase("SELECT username, password FROM chess_players WHERE username = '%s'", FILTER_INVALID_CHARS(UserName));

            // check result

            unsigned char ServerResponse[2] = {0};

            if(result['password'] == PassWord)
            {
                ServerResponse[0] = 1; // packet id will be 1. the next byte can be 1 or 0 to indicate logon success or failure.
                ServerResponse[1] = true; // so packet 0x0101 mean logon success, packet 0x0100 means logon failure
                send_packet(pkt->cSocket, ServerResponse, 2);
            } else {

                ServerResponse[0] = 1;
                ServerResponse[1] = false;
                send_packet(pkt->cSocket, ServerResponse, 2);
            }
        }
        break;

        default:
        {
            // received an unknown packet id; send a packet to the client that indicates an error_status_t

            unsigned char *ServerResponse[2] = {0};
            ServerResponse[0] = 2; // packet id 2 means server error
            ServerResponse[1] = 0; // error code 0 means 'unknown packet id'

            send_packet(pkt_cSocket, ServerResponse, 2);
        }
        break;
    }

    delete pkt; // must delete pkt, was created with 'new' in get_client_packets()
}
4

2 に答える 2

1

これはどちらかというと C スタイルのようで、Qt のやり方とは異なります。あなたの質問に対する一般的な答えはありませんが、私の提案は次のとおりです。

newConnection()の信号を聞いてくださいQTcpServer。ハンドラーはnextPendingConnection()、キューで待機している次のクライアントを取得するために を呼び出す必要があります。最初に行うことは、おそらくユーザー認証です。認証されたら、QTcpSocket をアクティブな接続のリストに保持します。

実際にパケットを読み書きする方法については、fortune クライアント/サーバーの例を参照してください。また、オブジェクトをシリアル化するためにストリーム オペレーターを調べることもでき<<ます。これは、投稿した低レベルの方法よりもはるかに簡単で、エラーが発生しにくいです。また、QDataStream はホストとネットワークのバイト オーダーを自動的に処理します。

于 2012-11-02T23:35:49.467 に答える
0

Fortune クライアント/サーバーの例に従った場合、QTcpSocket ( Rfserver) を含む QThread サブクラス ( Rfdevice、そのインスタンス変数はthread次のコードで呼び出されます) を持つ QTcpServer ( listenSocket) が必要です。

サーバークラスで、着信​​接続をリッスンすると、私のセットアップは次のようになります。

void Rfserver::incomingConnection(int socketDescriptor){
    if(thread){ //if thread exists, there is probably still an open connection
        if(thread->listenSocket){//if thread exists and the listenSocket is filled, there is definately an open connection
            if(thread->listenSocket->state() == QAbstractSocket::UnconnectedState){ 
                //but alas, it could just be in the unconnected state, if so kill it.
                this->disconnect();
                thread->terminate();
                thread=0;
                connected=false;
            }//otherwise, do nothing, because the software is happily connected to a device
        }
    }
    if(!thread){    //if no thread exists, we are by no means connected
        thread = new rfdevice(socketDescriptor, this); //set up a new thread
        //this first connection communicates the string from your socket to the server parent...use it if you want.
        connect( thread, SIGNAL(RemoteButton(QString)),this,SLOT(remoteButton(QString)),Qt::BlockingQueuedConnection); 
        connect( thread, SIGNAL(error(QTcpSocket::SocketError)),this,SLOT(tcpError(QTcpSocket::SocketError)),Qt::AutoConnection);
        connect( thread, SIGNAL(finished()), this, SLOT(threadZero())); //I have a threadZero function that deletes all the data then schedules the socket for deletion.
        thread->start(); 
        connected=true;
        QString *welcome = new QString("Enter your password:\r\n");
        echoCommand(welcome); //this is a function you will implement that sends the welcome message to the pending device.
    }
}

さて、デバイスがサーバーに接続しようとすると、デバイスには が表示され"Enter your password:\r\n"ます。デバイスはおそらくパスワードとユーザー名でこれに応答します。しかし、Qt 側は次のようになります。

/*
 FUNCTION:read
    this is a polling runloop that listens for data as long as the socket is connected or connecting.  If a
 write is ever scheduled, it will be called from this runloop..
 */
void Rfdevice::read(void){
    while((listenSocket->state() == QAbstractSocket::ConnectedState) || (listenSocket->state() == QAbstractSocket::ConnectingState)){
        //if there is data available to send write it to the socket
        if(dataToSend) this->write();
        if(listenSocket->waitForReadyRead(50)) readBytes(); 
        //wait for 50ms for data from the device
        //if there is ever data available to be read, read it.
    }
}

デバイスは、形式のユーザー名/パスワードで応答しますusername---password\r\n。次に、ソケットはこれを行います:

/*
FUNCTION:readBytes
this is like a callback function because it only gets called when there is data available for read.
It basically converts the data to a string.
 */
void Rfdevice::readBytes(void){
    QByteArray newData;
    newData = listenSocket->readAll();
    QString *recieved = new QString(newData);
    QStringList userAndPass = recieved.split("---");//this is your delimiter
    QString username = userAndPass.at(0);
    QString password = userAndPass.at(1);

    //NOW, check the username and password vs your SQL or wherever it's saved.

}

詳細については、疑似コードはかなり完全です。うまくいけば、それをすべてまとめることができます!さらにコードが必要な場合はお知らせください。

于 2012-11-03T00:19:11.730 に答える