しばらくの間、私はQtを使用してきました(英語で申し訳ありません)。現在、QtNetworkを使用しています。
システムのようなクライアントサーバーを作成する必要があります。基本的な考え方は次のとおりです。
両方(クライアントとサーバー)のIPは変更される可能性があります。サーバーIPをクライアントの構成ファイルに保存する方法はありません。したがって、サーバーは何らかの方法でIPを公開する必要があり、クライアントは接続するサーバーIPを検出できる必要があります。
クライアントは、サーバーがなくても動作し、サーバーを(接続すると)動作を更新できる必要があります。
クライアントとサーバーは同じWLANネットワークで実行されているため、インターネット経由で接続する必要はありません。
この新しいシステムは、古いシステム(また、クライアントとサーバー)を置き換えることを目的としています。古いサーバーはUDPブロードキャストメッセージのみを使用してクライアントと通信するため、メッセージが失われることがあります。
したがって、最初のポイントとして、サーバーにタイマーを実装しました。このタイマーは、X回ごとに、「サーバーに接続」というメッセージを含むブロードキャストメッセージをUDP経由で送信します。このメッセージの考え方は、クライアントがそのようなメッセージを検出し、サーバーIPを接続させることです。
サーバーはQTcpServerを使用してクライアント接続を検出し、クライアントごとに、通信するための新しいQTcpSocketを格納します。
クライアントは、サーバーIPを取得したら、TcpSocketを使用して接続します。
シンプルに聞こえます。
接続システムを実装しましたが、ローカルマシン(サーバーと同じマシン上で実行されている一部のクライアント)では、システムは問題なく動作します。接続が可能で、通信が機能します。クライアントとサーバーの両方が、gcc4.7.2を使用してChakraLinux64ビットでコンパイルされています。システムにインストールされているQtバージョンは4.8.4です。
ここで、2台の実マシン(Chakra Linux 64ビットで実行されているサーバーとWindows7(一部の32ビットと別の64ビット)および2台のWindows 8(32ビットのみ)で実行されているクライアント)でシステムをテストしました。クライアントのWindowsバージョンは、Qt4.8.3およびMingW4.6.2でWindows732ビットを実行している仮想マシン(VirtualBox 4.2.4)でコンパイルされています。Windowsクライアントは、すべての32ビットコンパイル済みバージョンのクライアント(すべての32ビットWindowsマシンと64ビットWindowsマシン)を実行します。
クライアントはブロードキャストメッセージ(UDP、ポート45454で動作)を検出して読み取ることができ、サーバーIPを取得すると(検出されたIPは正しい)、接続を試みます。
問題は、7つのクライアントから、1つのWindows8だけが一度接続したことです。すべてのクライアントが接続を試み、そのすべてのQTcpSocketが「接続済み」信号を送信しますが、サーバーは接続を検出しません。
1つまたは2つのクライアントを接続できる場合があるため、これは完全にランダムです。それ以外の場合はありません。
このコードは、クライアントブロードキャストの初期化用です。
broadcast_socket = new QUdpSocket ();
server_connection = new QTcpSocket ();
if ( broadcast_socket->bind ( QHostAddress::Any , cfg->networkControl ()->udpPort () ) == false )
{
cout << ">> NetworkManager: Error setting port " << cfg->networkControl ()->udpPort () << " for UDP broadcast listen." << endl;
}
connect ( broadcast_socket , SIGNAL ( readyRead () ) , this , SLOT ( broadcastMessageReceived () ) );
connect ( server_connection , SIGNAL ( readyRead () ) , this , SLOT ( serverMessageReceived () ) );
connect ( server_connection , SIGNAL ( disconnected () ) , this , SLOT ( serverConnectionLost () ) );
connect ( server_connection , SIGNAL ( connected () ) , this , SLOT ( serverConnected () ) );
このコードは、サーバーブロードキャストメッセージを受信するときのクライアントからのものです。
void NetworkManager::broadcastMessageReceived ( void )
{
while ( broadcast_socket->hasPendingDatagrams () )
{
QByteArray datagram;
QHostAddress sender_adress;
datagram.resize ( broadcast_socket->pendingDatagramSize () );
broadcast_socket->readDatagram ( datagram.data () , datagram.size () , &sender_adress );
server_ip = sender_adress;
QString message ( datagram.data () );
cout << ">> NetworkManager: Broadcast message received: " << message.toStdString () << endl;
if ( message == QString ( "0:%1" ).arg ( NetworkMessages::ConnectToServer ) )
{
if ( connection_status == 0 ) // If 0, there is no connection still.
{
cout << endl << " Server asking for connection." << endl;
cout << " Server IP: " << sender_adress.toString ().toStdString () << endl;
cout << " Trying to connect." << endl;
server_connection->connectToHost ( sender_adress , cfg->networkControl ()->tcpPort () );
cout << " Waiting for responce." << endl;
connection_status = 1;
break;
}
}
}
return;
}
サーバー側では、次のコードを初期化する必要があります。
connection_server = new QTcpServer ();
connect ( connection_server , SIGNAL ( newConnection () ) , this , SLOT ( connectClient () ) );
if ( connection_server->listen ( QHostAddress::Any , cfg->networkControl ()->tcpPort () ) == false )
{
cout << ">> NetworkManager: Error setting port " << cfg->networkControl ()->tcpPort () << " for TCP server use" << endl;
}
そして、これは新しいクライアントを接続するためのコードの一部です。
void NetworkManager::connectClient ( void )
{
cout << ">> NetworkManager: New client request." << endl;
QTcpSocket* new_client = connection_server->nextPendingConnection ();
connection_sockets[ i ] = new_client; // This is a QList of QTcpSocket pointers, the value of 'i' is calculated previously
connect ( new_client , SIGNAL ( readyRead () ) , connection_listeners[ i ] , SLOT ( readyRead () ) );
connect ( new_client , SIGNAL ( disconnected () ) , connection_listeners[ i ] , SLOT ( disconnected () ) );
connection_state[ i ] = 1;
cout << " Asking to client with IP " << new_client->peerAddress ().toString ().toStdString () << " identify." << endl;
connection_state[ i ] = 1;
sendToClient ( QObject::tr ( "0:%1" ).arg ( NetworkMessages::IdentifyYourself ) , i );
}
「sendToClient」メンバー関数は、接続されるとクライアントにメッセージを送信するだけです。
したがって、問題は、なぜそれほどランダムな接続があるのかわからないことです。ほとんどの場合、誤った接続です(クライアントも接続された信号を送信しますが、サーバー側では、クライアントが送信されたときに信号が送信されません。接続します...)。
何が間違っているのかわかりません...
クライアントとサーバーの両方がUDPポート45454とTCPポート8888で動作します。私はすでにTCPポート値(クライアント、サーバー、および両方)を変更しており、同じ話をしています。TCPポートを変更しましたが、両方に同じポートがない場合、メッセージはクライアントに到達しません...
長いテキストで申し訳ありませんが、私が何をしたかをお伝えするのが最善だと思います...
私は、1つが私の問題になる可能性があるか、または私のコードに問題があるかどうかのアドバイスを探しています...しかし、時々(そして時々)1つまたは2つのクライアントが実際に接続できることを覚えておいてください...