【问题标题】:Blocking connection between server-client using socket communication使用套接字通信阻止服务器-客户端之间的连接
【发布时间】:2017-01-19 02:07:34
【问题描述】:

我在使用 UDP 和 SFML 的客户端-服务器连接方面遇到了一些奇怪的问题,我慢慢地想不出可能出了什么问题,所以也许有人可以帮助我。

目前我可以将客户端连接到服务器并将消息从服​​务器发送到客户端。当我杀死客户端应用程序并再次重新启动它(在同一台机器上)时,提供相同的服务器连接参数,没有任何反应。看起来好像没有建立连接。客户端只是在等待消息表单服务器,而服务器同时不断地发送消息。

我的服务器端是这样的:

std::shared_ptr<sf::UdpSocket> startUdpServer()
{
    std::cout << "Local address: ";
    std::cout << sf::IpAddress::getLocalAddress().toString() << std::endl;
    std::cout << "Public address: ";
    std::cout << sf::IpAddress::getPublicAddress().toString() << std::endl;
    std::shared_ptr<sf::UdpSocket> socket(new sf::UdpSocket());

    if(socket->bind(sf::Socket::AnyPort) != sf::Socket::Done)
        return nullptr;
    std::cout << "Server is listening to port " << socket->getLocalPort() << ", waiting for a message... " << std::endl;
    return socket;
}

std::pair<sf::IpAddress, unsigned short> runUdpServer(std::shared_ptr<sf::UdpSocket> socket)
{
    // Wait for a message
    char in[128];
    std::size_t received;
    sf::IpAddress sender;
    unsigned short senderPort;
    if (socket->receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done)
    {
        std::cout << "Connection error" << std::endl;
        return std::make_pair(sender, senderPort);
    }
    std::cout << "Message received from client " << sender << ": \"" << in << "\"" << std::endl;
    return std::make_pair(sender, senderPort);
}

void sendUdpMessage(std::shared_ptr<sf::UdpSocket> socket, std::pair<sf::IpAddress, unsigned short> config,
                    std::string message)
{
    const char* out = message.c_str();
    if (socket->send(out, sizeof(char) * message.length(), config.first, config.second) != sf::Socket::Done)
    {
        std::cout << "Message not send" << std::endl;
        return;
    }
}

void sendMessages(std::shared_ptr<sf::UdpSocket> socket, std::pair<sf::IpAddress, unsigned short> config)
{
    if(config.first != sf::IpAddress::None)
    {
        while(true)
            sendUdpMessage(socket,config,"Test msg");
    }
    else
        std::cout << "Message sending error" << std::endl;
}

auto socket = startUdpServer();
auto config = runUdpServer(socket);
std::thread messages_thread(sendMessages,socket,config);

和我的客户:

std::shared_ptr<sf::UdpSocket> startUdpClient()
{
    sf::IpAddress server;
    do
    {
        std::cout << "Type the address or name of the server to connect to: ";
        std::cin  >> server;
    }
    while (server == sf::IpAddress::None);

    unsigned short port;
    std::cout << "Type the port number: ";
    std::cin >> port;

    std::shared_ptr<sf::UdpSocket> socket(new sf::UdpSocket());
    sf::Packet packet;
    const char out[] = "Hi, I'm a client";
    if (socket->send(out, sizeof(out), server, port) != sf::Socket::Done)
        return nullptr;
    return socket;
}

void runUdpClient(std::shared_ptr<sf::UdpSocket> socket)
{
    char in[256];
    std::size_t received;
    sf::IpAddress sender;
    unsigned short senderPort;
    if (socket->receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done)
        return;
    std::cout << "Message received: \"" << in << "\"" << std::endl;
}

auto socketUDP = startUdpClient();
std::thread messagesThread(receiveMessages,socketUDP);

void receiveMessages(std::shared_ptr<sf::UdpSocket> socket)
{
    while(true)
        runUdpClient(socket);
}

【问题讨论】:

  • 当你重新启动客户端时,你是在告诉它监听它之前监听的同一个端口吗?
  • 你在哪里绑定你客户端的端口?
  • @Eddge 1. 是的,我为它提供了相同的数据。 2.我才意识到,我只是绑定服务器。然而,客户正在工作(不过只有一次)。
  • 我敢打赌,如果您在客户端上绑定了端口,并且每次都将其重新绑定到同一个端口,因为您正在创建一个 UdpSocket。当您关闭客户端时,请确保它也删除了与端口的绑定。
  • 如果您在同一服务器端口上向服务器发送消息,则无法保证您的客户端在创建其 udpsocket 时每次都使用相同的端口(客户端),您可以通过打印进行测试发送数据时使用的端口。

标签: c++ sockets sfml


【解决方案1】:

对于网络,UDP 和 TCP 需要记住 2 个非常重要的关键注意事项。

TCP - 是基于连接的,这意味着每次它尝试向它发送一条消息时都需要有人在另一端。

UDP - 基于无连接,这意味着他会将信息发送到您希望他发送到的任何地方。他可以发送和接收信息,但是为了接收数据,他需要绑定到一个端口。因此,在您的服务器中,您每次都将他绑定到同一个端口。

在您的客户端中,您给他一个端口来发送信息,而不是将他绑定到特定端口。每当您关闭客户端时,他应该释放端口,并且每当您重新启动客户端时,他应该绑定到同一个端口,如果您希望他能够从您的服务器接收数据。如果您根本不知道发生了什么,那么数据会到达它发送到的 IP 和端口,但是没有与该端口关联的应用程序,因此数据包会丢失。

【讨论】:

    【解决方案2】:

    为了通过 UDP 在两方之间进行通信,每一方都需要一个唯一的(地址、端口)对。通常,服务器将其套接字绑定到一个固定/众所周知的端口,并且客户端端口因客户端而异——因此允许多个客户端与同一服务器通信,每个客户端都在自己的端口上。

    如果您的客户端,如这里,没有显式地将其套接字绑定到端口,操作系统将动态为其分配一个“随机”未使用的端口,并在第一次使用时自动绑定它用于发送数据的端口。只要该客户端继续使用同一个套接字,端口号就是固定的。

    但是,当您重新启动客户端时,它会获得一个 new 套接字,并且在使用新套接字首次发送时,操作系统会将套接字绑定到新端口。但是,您的服务器假设客户端端口号仍然是它从第一个客户端接收到的数字。

    由于这种工作方式,对于 UDP 服务器,通常的模式是每条消息都是独立的。服务器每次收到消息时都会记录客户端的地址和端口号,然后响应该地址/端口。服务器通常不会假设任何两个连续消息将来自同一个客户端,因为它无法知道任何给定客户端何时消失。

    (您可以在 UDP 上构建自己的更持久的“连接”概念——例如 NFS 传统上所做的那样——但这是大量工作,需要在设计你的协议。它在上述相同的基本模型中工作。)

    您的客户端也可以始终显式绑定到您选择的端口。但是,这将限制您在任何一台机器上运行一个客户端实例(实际上是在任何一个网络地址上)。

    【讨论】:

    • 感谢您的详细解释。所以要明确一点 - 每当我想从服务器接收新信息时,我应该从我的客户端发送带有接收端口号的请求,并且在一个数据包重复之后?对吗?
    • 是的。好吧,您最好为您正在尝试做的事情创建一个 TCP 连接。您希望客户端有效地签入一次并说“我在这里”,然后服务器将(仅)发送给客户端。 UDP 不太适合这种用例。在客户端离开之前它会正常工作,但服务器无法知道何时发生这种情况。如果您想使用 UDP 执行此操作,您至少需要让客户端定期再次签入,如果客户端没有签入,则让服务器超时。(或者,是的,您可以让客户端发送一个新签入每个数据包。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-30
    • 2012-11-02
    • 2016-02-07
    • 2016-10-26
    • 1970-01-01
    • 2016-07-21
    相关资源
    最近更新 更多