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

Author Topic: Three general questions about networking in SFML  (Read 30456 times)

0 Members and 1 Guest are viewing this topic.

Shump

  • Newbie
  • *
  • Posts: 8
    • View Profile
Three general questions about networking in SFML
« on: January 20, 2009, 11:52:29 am »
Hi!
I'm working on a simple multiplayer game, a school project. The game is based on the server-client model and it is using both TCP and UPD for different connections. I started out doing a simple chat-program, consisting of a server program and a client program, and the result was quite successful. I can start the server program on one computer, connect with several clients which can send and receive both TCP and UDP messages.

However, I've got some basic features that I would like to implement. Firstly, is it possible to have both a client program and a server program running on the same computer? I'm new to network coding, but I think it has to be something with the ports you use. At present, both my client and server program are using two ports, one for TCP listening and sending and one for UDP listening and sending. Maybe it is possible to achieve the wanted effect by using different ports for listening and sending, so that the send socket at the client program is using the same port as the listening socket at the server program and vice versa.

Secondly, in most network programs (e.g. multiplayer games) it is only necessary to open the ports for the server program, not the client. Although, when I tried my program over internet (I've previously only tested on LAN) I needed to open up ports for my client as well. HAs this something to do with the fact that I'm using a selector in the client program as well (which is not necessary, however, abit easier)?

Lastly, I'm also thinking of how to detect when a client is detected. It can be easily done by letting the disconnecting client send a "goodbye"-message and then the server can remove it from the list containing connected clients. However, if the client just cuts its connection with the server (e.g. it crashes) the connection will not be closed properly and I've experienced that the selector's timeout-function goes crazy and behaves as if it's set on a very short time (like 0.0...01 seconds). Is it possible to have the TCP socket nature of a constant connection to tackle this problem (which is the reason why I've been using it in the first plce)?

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Three general questions about networking in SFML
« Reply #1 on: January 20, 2009, 12:55:45 pm »
Quote
Firstly, is it possible to have both a client program and a server program running on the same computer?

Sure. You only specify a port when you setup a listening socket. When you connect to a host, your operating system chooses a port number by itself, mostly just a free one. This means serving and connecting on the same machine is absolutely no problem.

Quote
I'm new to network coding, but I think it has to be something with the ports you use. At present, both my client and server program are using two ports, one for TCP listening and sending and one for UDP listening and sending.

I guess you've an error in your design. Clients normally don't listen on any ports for connections coming from a server. Just open the two ports (TCP and UDP) at the server. Then connect via TCP to the remote host and send an UDP packet to the server. When you're using a router to connect to the internet, it will take care of routing the UDP packets straight to your computer in case the server sends something back. This is called Network Address Translation (NAT).

Quote
HAs this something to do with the fact that I'm using a selector in the client program as well (which is not necessary, however, abit easier)?

Nope, Selectors only tell you if a socket is ready for reading and/or writing, nothing else.

Quote
However, if the client just cuts its connection with the server (e.g. it crashes) the connection will not be closed properly and I've experienced that the selector's timeout-function goes crazy and behaves as if it's set on a very short time (like 0.0...01 seconds). Is it possible to have the TCP socket nature of a constant connection to tackle this problem (which is the reason why I've been using it in the first plce)?

Even if TCP is connection-oriented (that means, when a peer disconnects, it sends a last packet stating that he's going to shutdown the connection), you'll experience problems when a machine just crashes, thus not able to shutdown the connection gracefully. You surely have read something like "UserXYZ timed out." in a game. That occurs because the client wasn't responding in a specific period of time. You can implement such a mechanism by yourself, for example a simple PING/PONG.
The reason why your Selector nearly immediately returns is because he may have detected a connection shutdown -- or better: a problem. :) As soon as a connection interrupts or a socket gets invalid for any reason, the Selector will return immediately. You should call IsValid() on your Socket object to check if it's valid anymore, see http://www.sfml-dev.org/documentation/1.4/classsf_1_1SocketTCP.htm#134bc86320cc3f4c1ed9075e1c713082 .

Shump

  • Newbie
  • *
  • Posts: 8
    • View Profile
Three general questions about networking in SFML
« Reply #2 on: January 20, 2009, 05:45:17 pm »
Quote from: "Tank"
I guess you've an error in your design. Clients normally don't listen on any ports for connections coming from a server. Just open the two ports (TCP and UDP) at the server. Then connect via TCP to the remote host and send an UDP packet to the server. When you're using a router to connect to the internet, it will take care of routing the UDP packets straight to your computer in case the server sends something back. This is called Network Address Translation (NAT).


Thanks alot for your answer! I realise that I've misunderstood a few things about the basic mechanics about sockets.

My client program consists of three threads and six sockets. The main thread handles keyboard inputs and sends messages to the server via one TCP socket or one UDP socket. The second thread has one listening TCP socket, constantly listening for new connections since I'm using a selector, and one socket handling incoming messages through the selector. The third thread works almost exactly in the same way as the second, although with UDP sockets rather than TCP.

If I got this right, while a TCP socket is connected to the server, is can be used both for sending and receiving messages, although not in the same time. Do I have to use two separate sockets, one sending and one receiving, connected to the same address and port?

It seems even more problematic with UDP sockets since it needs to be bound to a port, and (according to the SFML 1.3 tutorial) it is the same thing as listening to the port (written in parenthesis commented in the code).

Daazku

  • Hero Member
  • *****
  • Posts: 896
    • View Profile
Three general questions about networking in SFML
« Reply #3 on: January 20, 2009, 05:47:23 pm »
If you are planning to make a multiplayer game selector are a bad idea!

You should do something like that:

Code: [Select]
   std::queue<PlayerPacket> SocketHandler::listen()
    {
        std::queue<PlayerPacket> receivedPacket;

        sf::Socket::Status status;
        sf::IPAddress address;
        sf::SocketTCP newClientSocket;

        // Listener
status = myListener.Accept(newClientSocket, &address);
        if(status == sf::Socket::Done)
        {
            newClientSocket.SetBlocking(false);

            myUnauthentifiedClients.insert(std::make_pair(unauthentifiedClientsId, Client(newClientSocket, address, "Unauthentified")));
            SocketHandler::unauthentifiedClientsId--;

            std::cout << "Client connected ! " << address << std::endl;
        }
        else if (status == sf::Socket::Error)
        {
            std::cout << "Error with listener!" << std::endl;
        }

        // Authentified client.
        for (std::map<int, Client>::iterator it = myClients.begin(); it != myClients.end(); it++)
        {
            sf::Packet packet;

            status = it->second.socket.Receive(packet);

            if (status == sf::Socket::Done)
            {
                receivedPacket.push(PlayerPacket(it->first, packet));
            }
            else if (status == sf::Socket::Error)
            {
                std::cout << "Socket status = ERROR for socket: " << it->second.address << std::endl;
                myClients.erase(it);
            }
            else if (status == sf::Socket::Disconnected)
            {
                std::cout << "Socket status = DISCONNECTED for socket: " << it->second.address << std::endl;
                myClients.erase(it);
            }
        }

        // Unauthentified client.
        for (std::map<int, Client>::iterator it = myUnauthentifiedClients.begin(); it != myUnauthentifiedClients.end(); it++)
        {
            sf::Packet packet;

            status = it->second.socket.Receive(packet);

            if (status == sf::Socket::Done)
            {
                receivedPacket.push(PlayerPacket(it->first, packet));
            }
            else if (status == sf::Socket::Error)
            {
                std::cout << "Socket status = ERROR for socket: " << it->second.address << std::endl;
                //improve it (Should clean client things)
                myUnauthentifiedClients.erase(it);
            }
            else if (status == sf::Socket::Disconnected)
            {
                std::cout << "Socket status = DISCONNECTED for socket: " << it->second.address << std::endl;
                //improve it (Should clean client things)
                myUnauthentifiedClients.erase(it);
            }
        }

        return receivedPacket;
    }
Pensez à mettre le tag [Résolu] une fois la réponse à votre question trouvée.
Remember to add the tag [Solved] when you got an answer to your question.

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Three general questions about networking in SFML
« Reply #4 on: January 20, 2009, 06:35:05 pm »
Quote from: "Shump"
My client program consists of three threads and six sockets

Ouch! :)

Quote
The second thread has one listening TCP socket, constantly listening for new connections since I'm using a selector, and one socket handling incoming messages through the selector. The third thread works almost exactly in the same way as the second, although with UDP sockets rather than TCP.

Why do you still listen for connections at your client? A connection works this way:
Server listens on a port, using a socket. Take it as the "door to the outside world".
Client connects to server on a port, again using a socket.

So all you need is ONE socket for each side. If you use both TCP and UDP, you need of course two of them, because a socket can only handle one protocol at a time. But you don't need to listen at your client. Once your *client socket* is successfully connected to the *server socket*, you can send AND receive data on the same socket.

Quote
If I got this right, while a TCP socket is connected to the server, is can be used both for sending and receiving messages, although not in the same time.

Exactly, it's bi-directional. Well, what do you mean by "same time"? You can read data and immediately send data through the socket in a row.

Quote
Do I have to use two separate sockets, one sending and one receiving, connected to the same address and port?

No. Connect your TCP socket to the server, then you can read from and send through it.

Quote
It seems even more problematic with UDP sockets since it needs to be bound to a port, and (according to the SFML 1.3 tutorial) it is the same thing as listening to the port (written in parenthesis commented in the code).

That's only partially true. UDP, but also TCP!, sockets must all be bound to a port. In case of a server, you choose the port yourself (webservers for example use TCP port 80). Now if a client connects, it binds its socket to a local port, mostly random, and given by the operating system. If the connection is established, both sockets can communicate with each other through the ports.

When sending UDP packets, you kinda initiate a connection -- not really, because UDP doesn't use "real" connections like TCP. The important part is that your router keeps that in mind, i.e. it will store the port YOU used to send the UDP packet and source IP (your LAN IP). Now when a packet from the server comes back, addressed to your local port, it gets routed to your interface.

In short words: You don't need to forward/open ports on the client-side when talking to a server, whether it's TCP or UDP.

Edit: Look at the diagram, it shows how a connection roughly looks like: *click*

Daazku:
That depends on what you're developing. Server applications should be written to block, thus not consuming all the CPU time. Therefore you should use a selector, so that your application only runs when needed.

Actually the same goes for client applications (games). One solution could be to separate the networking part into an own thread, just for reading from sockets. This way you can both limit your framerate (takes pressure off the CPU) and keep your networking routines in a blocking state, which consumes no CPU time at all. Polling is generally a bad idea.

Shump

  • Newbie
  • *
  • Posts: 8
    • View Profile
Three general questions about networking in SFML
« Reply #5 on: January 20, 2009, 07:50:55 pm »
Ok, I think I'm getting the hang of it. To put it in a fewer words; you create a TCP and/or UDP, connect it to a server, and then let each socket handle both the sending and receiving messages, preferably in its own thread.

Although, what is the best way of sending and receiving messages simultaneously? Is it to set the sockets on non-blocking mode and let them switch between:
   checking if there is anything to send and send it.
and...
   checking if there is any messages to receive?

Or is it possible to copy an already connected socket and to let one of them send data, and the other (in another thread) receive data?

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Three general questions about networking in SFML
« Reply #6 on: January 20, 2009, 08:55:27 pm »
Quote
Ok, I think I'm getting the hang of it. To put it in a fewer words; you create a TCP and/or UDP, connect it to a server, and then let each socket handle both the sending and receiving messages, preferably in its own thread.

Exactly. :)

Quote
Although, what is the best way of sending and receiving messages simultaneously?

To answer this it's probably good to know that sending data mostly doesn't block. Therefore you'd only have to put your receive mechanism into an own thread. Sending can be theoretically done from everywhere at any time. Since a socket is nothing else than a simple file descriptor, you can't really access it simultaneously, but your operating system keeps track of that. You can also copy your socket, but that's normally not needed and doesn't bring you any advantages.

So, my proposal is to put reading data from a socket into an own thread, using a selector. Sending can be done from anywhere.