2

问题是它只连接到一个客户端而不是两个。谁能帮我弄清楚为什么?

服务器:

#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()致电。

此外,大多数协议只依赖于一个众所周知的端口;服务器按照惯例使用一个特定端口,但客户端选择任何开放且空闲的端口。FTP 也严重依赖客户端上的知名端口,因此通过网络地址转换防火墙隧道是一个巨大的痛苦。

这不仅仅是学术性的:您的客户端服务器都在尝试bind()移植4444. 这意味着您需要在一台机器上至少有两个IP 地址来进行测试,或者使用虚拟化软件在同一硬件上运行完全独立的机器,或者只有两台机器可用。这比需要做的工作更多,客户没有理由关心他们的本地端口号:

服务器:

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

客户:

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

噗!如果没有重大技巧,这两个永远不会在同一主机上运行。我希望您的“它连接到一个”可能只是连接到自身的服务器或客户端,但是如果没有一些代码来填充这些// 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;
}

这种不必要的创建、绑定和关闭都是浪费的能量,包括计算机的能量和(更重要的是)你的能量。考虑以下两个重写:

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);

第一个选项是对现有代码的简单重构;它在线程函数中保留套接字的创建和销毁,但将循环不变量移出循环。循环内的代码现在只做必要的事情。

第二个选项是一个更彻底的改造:它将套接字创建移动到主线程,在那里错误处理可能容易,并且线程函数只执行远程对等点需要该线程执行的操作。(如果您想从 UDP 更改为 TCP,则第二种选择会更容易更改——您的线程代码可能根本不需要任何修改。)

我希望这有帮助。:)

于 2011-05-11T01:49:01.393 回答