2

問題は、2つではなく1つのクライアントにしか接続しないことです。誰かが私が理由を理解するのを手伝ってくれる?

サーバ:

#include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>

void sendInfo(void *UserData)
{
    sf::IPAddress* ip = static_cast<sf::IPAddress*>(UserData);
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Create bytes to send
        char Buffer[] = "sending info.";

        // Send data to "192.168.0.2" on port 4567
        if (Socket.Send(Buffer, sizeof(Buffer), *ip, 4444) != sf::Socket::Done)
        {
            // Error...
        }
    }
}

void receiveInfo(void *userData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        std::cout << Buffer << std::endl;

        Socket.Close();

    }
}

int main()
{
    sf::IPAddress client[2];
    int connected = 0;
    while(connected < 2){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        client[connected] = Sender;

        Socket.Close();

        sf::Thread* send = new sf::Thread(&sendInfo, &client[connected]);
        sf::Thread* receive = new sf::Thread(&receiveInfo, &client[connected]);
        // Start it !
        send->Launch();
        receive->Launch();
        connected++;
    }

    while(true){

    }

    return EXIT_SUCCESS;
}

クライアント:

#include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>

void sendInfo(void *UserData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Create bytes to send
        char Buffer[] = "client sending info.";

        // Send data to "192.168.0.2" on port 4567
        if (Socket.Send(Buffer, sizeof(Buffer),  "127.0.0.1", 4444) != sf::Socket::Done)
        {
            // Error...
        }
    }
}

void receiveInfo(void *userData)
{
    // Print something...
    while(true){
        // Create the UDP socket
        sf::SocketUDP Socket;

        // Bind it (listen) to the port 4567
        if (!Socket.Bind(4444))
        {
            // Error...
        }

        char Buffer[128];
        std::size_t Received;
        sf::IPAddress Sender;
        unsigned short Port;
        if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
        {
            // Error...
        }

        // Show the address / port of the sender
        std::cout << Buffer << std::endl;

        Socket.Close();

    }
}

int main()
{
    // Create the UDP socket
    sf::SocketUDP Socket;

    // Create bytes to send
    char Buffer[] = "Client Joined.";

    // Send data to "192.168.0.2" on port 4567
    if (Socket.Send(Buffer, sizeof(Buffer), "127.0.0.1", 4444) != sf::Socket::Done)
    {
        // Error...
    }

    sf::Thread* send = new sf::Thread(&sendInfo);
    sf::Thread* receive = new sf::Thread(&receiveInfo);
    // Start it !
    send->Launch();
    receive->Launch();


    while(true){

    }

    return EXIT_SUCCESS;
}
4

1 に答える 1

9

まず最初に:これはチャットサーバーですか、それとも「より一般的な」サーバーですか?

これがチャットサーバーの場合は、クライアントに接続されているソケットのリスト(呼び出しを使用してUDPソケットに接続できconnect()、非常に便利であり、なりすましピアの可能性を減らすのにも役立ちます)またはリストが必要です。またはに提供できるすべてのクライアントアドレスsendto()sendmsg()

より多くの「一般的な」サーバーは、最近リクエストを行ったサーバー以外のクライアントにメッセージを送信しようとはしません。これらのサーバーは通常、クライアントから何も保存せず、代わりに、で使用するピアのアドレスを使用recvfrom()または取得します。recvmsg()後でsendto()またはsendmsg()呼び出します。

また、ほとんどのプロトコルは1つのよく知られたポートのみに依存しています。サーバーは慣例により1つの特定のポートを使用しますが、クライアントは開いているポートと空いているポートを選択します。FTPは、クライアント側の既知のポートにも大きく依存しているため、ネットワークアドレス変換ファイアウォールをトンネリングするのは非常に困難です。

アカデミックなだけではありません。クライアントbind()サーバーの両方が移植を試みています4444。つまり、テストするには1台のマシンに少なくとも2つのIPアドレスが必要であるか、仮想化ソフトウェアを使用して同じハードウェア上で完全に別個のマシンを実行するか、2台のマシンを使用できるようにする必要があります。必要以上の作業であり、クライアントがローカルポート番号を気にする理由はありません。

サーバ:

    // Bind it (listen) to the port 4567
    if (!Socket.Bind(4444))
    {
        // Error...
    }

クライアント:

    // Bind it (listen) to the port 4567
    if (!Socket.Bind(4444))
    {
        // Error...
    }

プーフ!これらの2つは、重要なトリックがなければ同じホストで実行されることはありません。「1つに接続する」とは、おそらくサーバーまたはクライアントがそれ自体に接続していることだと思いますが、これらのブロックを埋めるためのコードがない// Errorと、確実に判断するのは難しいでしょう。

(そして、私たちがここにいる間、コメントについて話すために脇に置きたいと思います;コードが何をするかを単に言い換えるコメントはあまり役に立ちません。あなたのコメントのほとんどは実際には間違っていることに気付くでしょう、間違ったIPまたはポートを参照しています。情報を追加しないものもあります。

    // Create the UDP socket
    sf::SocketUDP Socket;

コメントを追加するように教えられていることは知っていますが、残念ながら、どのようなコメントを追加するかを常に教えられているわけではありません。両方のプログラムで、保持することをお勧めする唯一のコメントは、これを少し修正したものです。

    // udp doesn't require listen or accept
    if (!Socket.Bind(4444))

コードを読み取っても明らかではなく、ポート番号が環境変数、コマンドラインパラメーター、構成ファイル、またはレジストリから読み取られても間違いはありません。(ソケットAPIに精通している人々のチームでは冗長すぎるかもしれませんが、UDPとTCPの違いに精通していないプログラマーにとっては金になるかもしれません。)

良い関数名、変数名などは、ほぼ毎回コメントに勝ちます。脇の終わり。:)

そして今、よりマイナーなニトピッキング:スレッドハンドラーは次のようないくつかのタスクを実行しています:

while(1) {
    socket s;
    bind s;
    r = recv s;
    print r;
    close s;
}

この不必要な作成、バインド、および終了は、コンピューターのエネルギーと(さらに重要なことに)あなたのエネルギーの両方で、すべて無駄なエネルギーです。次の2つの書き直しを検討してください。

recv_thread() {
    socket s;
    bind s;
    while (1) {
        r = recv s;
        print r;
    }
    close s;
}

また

recv_thread(s) {
    while (1) {
        r = recv s;
        print r;
    }
}
/* ... */
socket s;
bind s;
sf::Thread* rt = new sf::Thread(&recv_thread);
rt->Launch(s);

最初のオプションは、既存のコードの単純なリファクタリングです。スレッド関数でソケットの作成と破棄を維持しますが、ループ不変条件をループの外に移動します。ループ内のコードは、必要なことだけを実行するようになりました。

2番目のオプションは、より抜本的な手直しです。これにより、ソケットの作成がメインスレッドに移動します。この場合、エラー処理はおそらくはるかに簡単であり、スレッド関数は、リモートピアがそのスレッドで実行する必要があることだけを実行します。(UDPからTCPに変更したい場合は、2番目のオプションの方がはるかに簡単に変更できます。スレッドコードを変更する必要がまったくない場合があります。)

これがお役に立てば幸いです。:)

于 2011-05-11T01:49:01.393 に答える