Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Select function  (Read 4575 times)

0 Members and 1 Guest are viewing this topic.

addisonElliott

  • Newbie
  • *
  • Posts: 3
    • View Profile
Select function
« on: June 06, 2015, 06:43:48 am »
Hello,
I would like to request that a Select function be added to the TcpSocket class. The main benefit of this would be for programmers who are utilizing non-blocking.

In my case, I am working on a game that needs to be responsive at all times. This means that the connect, receive, or send functions cannot be blocking. Here is how I would like to do it:
while (running)
{
    sf::TcpClient::Status status = socket.Select();
    if (status & sf::TcpClient::Write)
    {
          socket.Send(buffer);
    }
   
    if (status & sf::TcpClient::Read)
    {
          socket.Recv(buffer);
    }

    if (status & sf::TcpClient::Error)
    {
          Error.
    }
    // Other game stuff
}

The current way to achieve this is to continually call connect, in which the host & port parameters are required everytime. Then once you are connected, you call send/recv in the hopes that data is there or that it is able to send data. The reason why I would like the select function is because it is one function that will tell you if there was an error, if its ready to send data, and if it has data waiting to be received.

A problem I see with my proposed implementation of the Select function is that you are 'doubling' up on error checking. Currently, WSAGetLastError is called in the connect, recv, and send functions to report any error. But then you are calling Select which will report if there is an error there as well. This doesn't have a huge affect on anything, but it is unnecessary to do this.

Since SFML does not offer what I need, I have my own wrapper which does the same thing effectively. I would prefer to use SFML's networking class just to be consistent since the rest of the game uses it.

G.

  • Hero Member
  • *****
  • Posts: 1592
    • View Profile
Re: Select function
« Reply #1 on: June 06, 2015, 03:07:19 pm »
Isn't  sf::SocketSelector good enough?

addisonElliott

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Select function
« Reply #2 on: June 06, 2015, 04:23:16 pm »
From as far as I can tell, the sf::SocketSelector class is meant for multiple sockets at once. Even then, the class only notifies you that it is ready to receive data. The select function would notify you if there was an error, if you are ready to receive, and if it is capable of writing.

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Select function
« Reply #3 on: June 06, 2015, 04:49:56 pm »
Quote
From as far as I can tell, the sf::SocketSelector class is meant for multiple sockets at once. Even then, the class only notifies you that it is ready to receive data. The select function would notify you if there was an error, if you are ready to receive, and if it is capable of writing.
That's correct. The only set passed to the select function is read. Write and except are passed as NULL. We also use posix's select instead of other APIs.
Back to C++ gamedev with SFML in May 2023

addisonElliott

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Select function
« Reply #4 on: June 07, 2015, 04:11:59 am »
I have implemented my own class that does what I would like. However, I will consider switching over to SFML's networking if they provide this Select function.

I will show my Select function code to demonstrate the usefulness.

void Socket::Select(double timeout)
{
    if (!connected && !isConnecting)
        return;

    fd_set readFds, writeFds, exceptFds;
        long tsecs = long(timeout);
        timeval timeoutVal = {tsecs, long((timeout - double(tsecs))*1000000)};

        FD_ZERO(&readFds);
        FD_ZERO(&writeFds);
        FD_ZERO(&exceptFds);

    FD_SET(impl->sock, &readFds);
    if (isConnecting || sendBuffer.length() > 0)
        FD_SET(impl->sock, &writeFds);
    FD_SET(impl->sock, &exceptFds);

        int result = select(impl->sock+1, &readFds, &writeFds, &exceptFds, &timeoutVal);
        if (result == SOCKET_ERROR)
            return;

        if (result > 0)
        {
                if (FD_ISSET(impl->sock, &exceptFds))
                {
                    // If attempting to connect, this means it failed
                    // Otherwise it means there was an error with the socket somehow
                    isConnecting = false;
                    EmitMainEvent(Event::NetworkConnectTerm);
                    return;
                }

                if (FD_ISSET(impl->sock, &writeFds))
                {
                    if (isConnecting)
            {
                // If attemtping to connect, this means that it succeeded
                connected = true;
                EmitMainEvent(Event::NetworkConnectOk);
                isConnecting = false;
                return;
            }
                        else if (!DoSend())
                        {
            // Otherwise it means you are able to write data without it blocking.
            // I have a string buffer for sending and receiving data, and so DoSend just removes some data from the buffer and sends it.  
           // At least 1 byte of data should be sent, otherwise it means there was an error of some sort.
                            Close();
                            EmitMainEvent(Event::NetworkConnectTerm);
                                return;
                        }
                }

                if (FD_ISSET(impl->sock, &readFds))
                {
                        if (!DoRecv())
                        {
               // This means there is data to be read. If no data is able to be read, this means that the socket was most likely closed.
                            Close();
                            EmitMainEvent(Event::NetworkConnectTerm);
                                return;
                        }
                }
        }
}

If you read the MSDN on the select function, there are special cases that indicate whether a connection was successful or was terminated in which I noted in the comments. I am not sure how this ports over to other operating systems.

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Select function
« Reply #5 on: June 07, 2015, 04:51:57 am »
To have select in socket makes no sense, you select on multiple sockets, not one (even if that multiple sockets is a single socket). If there is just one socket you want to select on, that's fine too for selector, but without the selector, as soon as you have more than one socket, you are deaf to every one of them except the one you listen to, that's really bad if you want responsiveness. If this feature was in SFML I'd say it'd definitely go into selector, not socket.

I'd say we should not complicate much and have a selector operation type, that'd determine if selector is selecting on write, read or except. But that'd make a lot of API names a bit stupid - like 'isReady' when using errors means ready for what? Ready for an error?.. maybe ready to handle an error by user but still...
It'd also make you need 3 selectors, not 1. But that's kinda OK, compared to alternative that is complicating selector API a LOT.
However - Laurent will probably say no and will kill us all with a stiff french baguette in defense of 'cleanliness' of the API.
« Last Edit: June 07, 2015, 04:55:39 am by FRex »
Back to C++ gamedev with SFML in May 2023

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Select function
« Reply #6 on: June 08, 2015, 09:50:07 am »
Quote
However - Laurent will probably say no and will kill us all with a stiff french baguette in defense of 'cleanliness' of the API.
I can't express how unrelated and useless that statement is.

@addisonElliott
There's an important difference between select() failing and recv() failing. The latter is well covered by SFML. If the socket you are selecting is ready to be read, select() returns. That doesn't imply that there's data waiting, though. A socket is ready if it fails, as well, like a disconnection.

I think you can do what you are looking for with SFML just perfectly fine. :)

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 878
    • View Profile
Re: Select function
« Reply #7 on: June 08, 2015, 12:59:51 pm »
Not sure I 100% understood what you're trying to do. :)

What prevents you from doing the whole network thing in its own thread? Network by default is asynchronous anyway, so couldn't you just connect in a blocking way in a second thread?