SFML community forums

Help => Network => Topic started by: Jungletoe on March 30, 2013, 09:14:30 pm

Title: TCP Lag Reduction Techniques?
Post by: Jungletoe on March 30, 2013, 09:14:30 pm
Hi, I'm developing an sandbox ORPG called Colonies (http://jungletoe.com).

As my game has progressed, we have gotten upwards of 20 players constantly on at once. With this new traffic, there is also a lot of lag that has followed. After optimizing everything except the networking backend, I came to the realization that the lag was coming from sending packets.

The game can suddenly freeze for 5-20 seconds at worst. I profiled the threads and noticed that it was getting held up at "zwwaitforsingleobject" inside of the send packet function.

-What are some common techniques to reduce the sending lag?
-Would breaking down the packets into smaller ones reduce the lag or have no effect since SFML does this anyways (I've noticed that larger packets take longer to send)?
-Does adding a timeout to the selector effect the timeout of the send? Do I need to have the selector in a different thread from the "sendPacket" functions in order for the timeout to work?

Thanks,
Jungle
Title: Re: TCP Lag Reduction Techniques?
Post by: Varonth on March 30, 2013, 09:42:53 pm
May I ask what exactly are you sending there?
5-20s sounds like you are sending something huge.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on March 30, 2013, 10:13:25 pm
May I ask what exactly are you sending there?
5-20s sounds like you are sending something huge.

Map chunks (about 20x20 ints), object chunks (can be anything from 0 ints to 30x30-- although rarely that high), and player movements (player name string, int x, int y, bool running) in which a for loop adds multiple player positions to the same packet if the player has moved.

The player movement packets take the longest to send if there are 2+ players all putting in a movement request at once, which is where the problem arises.
Title: Re: TCP Lag Reduction Techniques?
Post by: Varonth on March 30, 2013, 10:35:22 pm
Int in like 32bit standard integers?
Because one of these 20*20 packets would be 20*20*32/8 = 1600 bytes.
The 30*30 packets would be 30*30*32/8 = 3600 bytes.

Also you are sending the character name with each move?
If so, that is alot of bandwidth wasted.

If you need a way to distinguish the different move request for each character, you should consider using a sf:Uint8 or sf:Uint16 as an ID number, and give every single player a unique ID for that playsession, which allows every client to know which character entity is moved by each move request.

You should try to make packets as small as possible, as 1kb packets send to 20 people, perhaps multiple times per seconds, takes alot of bandwidth pretty fast.

And while most home connections have a good downstreams, the upstreams are often limited in speed.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on March 30, 2013, 10:40:15 pm
Int in like 32bit standard integers?
Because one of these 20*20 packets would be 20*20*32/8 = 1600 bytes.
The 30*30 packets would be 30*30*32/8 = 3600 bytes.

Also you are sending the character name with each move?
If so, that is alot of bandwidth wasted.

If you need a way to distinguish the different move request for each character, you should consider using a sf:Uint8 or sf:Uint16 as an ID number, and give every single player a unique ID for that playsession, which allows every client to know which character entity is moved by each move request.

You should try to make packets as small as possible, as 1kb packets send to 20 people, perhaps multiple times per seconds, takes alot of bandwidth pretty fast.

And while most home connections have a good downstreams, the upstreams are often limited in speed.

Hmm ok... I already have IDs for animals, so Ill just switch that code over to the players. That should be a simple fix.

And yes-- the 32bit ints.

So should I break down these packets? Would that solve anything? And is it just due to the fact that Im hosting it from my house network, because games like Minecraft don't seem to have that problem with 11 people.
Title: Re: TCP Lag Reduction Techniques?
Post by: Varonth on March 30, 2013, 11:01:35 pm
I don't know how your code works exactly. For example I don't know how often you send the map and object data, who gets which data etc.

If it is just once per second per client, with the numbers above this would be 5200*20 = 104000 bytes in a worst case scenario. That is 101.5625 kbyte/s or in terms of upstreams, 812.5kbit/s, just for this data. If you map/world can be indexed with 65,536 different states you might also switch to sf:UInt16 for your map and object chunks. But would basically halve the size of these sends (not actually 50% because of overhead by Tcp and IP but it would be so close, that you could say it is halved).

On top of that you might consider data compression before you send the data.
The packet documentation has some informtions on that topic, and there are several open source libraries for compression, like zLib (http://www.zlib.net/).

It is probably one of the best advices I can give to you. Keep your packets as small as possible.
You often have to send this data multiple times, sometimes (or depending on the game always) to everyone connected to the game. Saving 20 bytes of data in a packet ends up being hundreds of bytes that you don't have to send.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 02, 2013, 03:16:59 am
Ok, I reduced the size of the packets. Still no luck. The server still pauses/freezes for 10-30 seconds on ZwWaitForSingleObject.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 02, 2013, 05:03:56 am
I really want to help, but without more insight into how your networking code looks like, I'm limited to making wild guesses and possibly providing unhelpful advice.

Maybe it might be a bit nit-picky but what you describe can't be classified as "lag" the colloquial term for network latency. Network latency is the the concept of the time that is needed for data to be transferred from one host to another. Low latency is good, high latency is bad for real-time applications but can be acceptable for bulk data transfers. If your server locks up for 10-30 seconds it isn't due to latency because no data is arriving at all at the clients in that time interval, and when it starts sending data again it all arrives at once.

From tests I conducted while I was very bored once, I can say that SFML doesn't have any significant network bottlenecks, especially at 20 connected clients. Therefore I am relatively sure it is something specific to your code/architecture. Because of this, if you expect useful advice, you need to provide more specifics other than the kind of data you send. The amount of data you send is also insignificant, otherwise you wouldn't be able to host Minecraft with 11 people. Making use of multiple threads is also a headache to debug as you found out. There are ways to do it but none of them are really reliable if you ask me, especially on Windows. WaitForSingleObject is a win32 synchronization function that is probably called when you make use of Mutexes or block on something.

Maybe you could provide code snippets. They would provide more information than the questions in your first post.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 02, 2013, 07:08:10 am
Maybe you could provide code snippets. They would provide more information than the questions in your first post.

Hmm ok. What snippets do you need exactly?

Here is my server-sided "receive packets" function:

void NetworkManager::recPackets()
{
        // Receive a message from the client
        while(running)
        {
                //      Wait for data on any incoming socket
                if(Selector.wait(sf::milliseconds(250)))
                {
                        //      Test the listener
                        if(Selector.isReady(listener))
                        {
                                maxConID++;
                                Connector* newConnector = new Connector;
                                newConnector->player = NULL;
                                newConnector->setID(maxConID);
                               
                                if(listener.accept(newConnector->socket) == sf::Socket::Done)
                                {
                                        mu::lockMutexes();
                                        mutex.lock();
                                        //      Adds a new client to client list
                                        ent::connectors.push_back(newConnector);

                                        //      Add a new client to the selector so we will be
                                        //      notified if we recieve something from it
                                        Selector.add(newConnector->socket);
                                        mutex.unlock();
                                        mu::unlockMutexes();
                                }

                                else
                                {
                                        //      Nothing we can do...
                                }
                        }

                        else
                        {
                                //      The listener socket is not ready, test all other sockets
                                for(sf::Int32 i = 0; i < ent::connectors.size(); i++)
                                {
                                        sf::TcpSocket &client = ent::connectors.at(i)->socket;
                                        if(Selector.isReady(client))
                                        {
                                                //      The client has some data, we can recieve it
                                                sf::Packet packet;
                                                if(client.receive(packet) == sf::Socket::Done)
                                                {
                                                        sf::Uint32 packetCode;
                                                        packet >> packetCode;

                                                        mu::lockMutexes();

                                                        handlePacket(packetCode, packet, ent::connectors[i]->getID());

                                                        mu::unlockMutexes();
                                                }

                                                else if(client.receive(packet) == sf::Socket::Disconnected)
                                                {
                                                        mu::lockMutexes();

                                                        mutex.lock();
                                                        ent::playerHandler.handleDisconnect(ent::connectors[i]->getID());
                                                        mutex.unlock();

                                                        mu::unlockMutexes();
                                                }

                                                else if(client.receive(packet) == sf::Socket::Error)
                                                {
                                                        std::cout << "NetworkingError";
                                                }
                                        }
                                }
                        }
                }
        }
}
 

The handlePacket() function takes a packet, unloads it, and does something with the data (puts in some movement requests, tile changes, etc).

The mu::lock/unlockMutexes() functions lock all the global mutexes to prevent deadlock and crashes. Yes, it's a pain to do multithreading. I have to lock everything every time a thread accesses some sort of data within the program. I'm beginning to wonder if multi-threading was even worth it.

The thing I don't understand is why it doesn't just freeze up completely (because of deadlock or failing to unlock a mutex)-- it always resumes normal work receiving packets/handling data/saving after about 10-30 seconds (in some bad cases, longer). I also tested to make sure that it didn't have to do with the loading/saving aspects of the program and it seemed to be normally timed. The thing is that it only freezes like this when there are multiple players online (all of them seem to be doing normal tasks in the game-- nothing out of the ordinary that would execute some long function).

If you want to have an extended look at it, you can add me on Skype (brady.welch).
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 03, 2013, 12:51:20 am
if(client.receive(packet) == sf::Socket::Done)
{
        ...
}

else if(client.receive(packet) == sf::Socket::Disconnected)
{
        ...
}

else if(client.receive(packet) == sf::Socket::Error)
{
        ...
}
 
This is bogus. You try to receive multiple times per "selector-ready-status" because you need to evaluate the if statements independently of one another. It means if the first .receive() doesn't return sf::Socket::Done you will try to .receive() again although there might not be data available. The same for the third case. What you want to do is receive once and save the return status to differentiate between the different cases that have to be handled.
sf::Socket::Status status = client.receive(packet);

if(status == sf::Socket::Done)
{
        ...
}

else if(status == sf::Socket::Disconnected)
{
        ...
}

else if(status == sf::Socket::Error)
{
        ...
}
 
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 04, 2013, 10:08:06 pm
Ok, I changed that, but I'm still getting spikes.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 05, 2013, 12:56:23 am
Well your usage of mutexes in that snippet is also really strange. Do you have multiple recPackets threads? That's not such a good idea since you only protect the Selector with a mutex when a new connection is accepted and when a connection disconnects. When you call wait() on the Selector it is also modified internally so that would also need to be protected with a mutex, but that would make your whole recPackets method single threaded anyway. I don't really see why you would need more than one thread doing the network processing since the majority of it takes place in the sections that you already guard with a mutex. Also try to avoid locking multiple mutexes at the same time. Mutexes are meant to protect against concurrent access to resources. Locking the client list mutex and handling a player disconnect isn't something I would do because there is a higher risk of deadlocks if too much happens in between a lock and unlock. The same applies for handlePacket. Move the locks and unlocks to the resource access sites and it'll be easier to debug deadlocks which I'm sure are happening.

If you ask me, I still say that the easiest way out of this problem is to make your networking code run in the same thread as the main application. All you have to do is get rid of the while(running) and just call recPackets() every frame, you can also lower the wait() time on the selector to some very small value so it won't even influence the framerate of the server. That way you can also get rid of all those complicated mutexes.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 05, 2013, 04:30:01 am
Well your usage of mutexes in that snippet is also really strange. Do you have multiple recPackets threads? That's not such a good idea since you only protect the Selector with a mutex when a new connection is accepted and when a connection disconnects.

There is only one recPackets thread. I actually have a global mutex which locks up the whole program every time it receives a packet it needs to handle or needs to execute the worker thread. It's extremely inefficient as it is, and I really need to redo it. Without the global mutex, everything kept getting deadlocked.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 05, 2013, 05:18:26 am
Well, I guess the first thing to do is rework how the program handles packets. Using global mutexes is "unhealthy" and would lead to maintainability problems sooner or later. Even if you didn't have this problem now, the larger your codebase becomes the more of a headache it will be to fix strange problems like this in the future.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 05, 2013, 06:33:04 am
What if I have a thread that handles the packets and another one that receives them from the selector? The selector thread would simply dump all of the unprocessed packets into a vector of packets while the handler thread would pick up those packets and do something with them.

That way, it wouldn't get frozen up and delay connections from being acknowledged and stuff.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 05, 2013, 07:11:48 am
It is a common misconception that you need to immediately handle incoming connection requests or pending data. If you don't do anything with them, they will just stay queued. And if your server loop operates at a decent framerate, it won't make a difference whether you handle them in the first possible frame or a few frames later. The player will not notice anything because anything under 20ms is barely noticeable anyway. The only time when it can get really laggy on servers is when they need to perform many database updates at the same time or read/write to disk a lot. Unless your server application is running at 100% CPU utilization I wouldn't worry too much about noticeable delays or freezes from the client perspective.

You must also be aware that your server as it is right now only handles a single game state, the "world" or whatever it is called. The data that represents this world is stored in data structures that are not meant to be used in a multithreading environment which forces you to use mutexes every time you want to alter it in some way. Since that is the only thing players do, you end up just blocking all threads except one. This is effectively a single-threaded server, just way more complicated. From my experience, I've seen servers employ threading in many ways. Most of them thread their database accesses because as I already said, at medium to high load, waiting for the DB calls to return takes too long to ensure a decent framerate. If you didn't notice yet, MMORPG developers also tend to separate their worlds into "zones" or "instances" in order to split the game state up into manageable pieces. I don't know the details but I'd say that it is a good idea to just have a single thread per game state. Any more than that and they would fight over resources to the point that you have no benefit and are left with the overhead of the extra idle thread.
Title: Re: TCP Lag Reduction Techniques?
Post by: K-Bal on April 05, 2013, 09:40:25 am
Make it single-threaded, it will save you a lot of headaches and you get rid of your spikes ;)
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 05, 2013, 10:17:46 pm
I've seen servers employ threading in many ways. Most of them thread their database accesses because as I already said, at medium to high load, waiting for the DB calls to return takes too long to ensure a decent framerate. If you didn't notice yet, MMORPG developers also tend to separate their worlds into "zones" or "instances" in order to split the game state up into manageable pieces. I don't know the details but I'd say that it is a good idea to just have a single thread per game state. Any more than that and they would fight over resources to the point that you have no benefit and are left with the overhead of the extra idle thread.

I broke up the game data into "chunks". There is a single "world" in my game, but it's broken down into chunks for easier loading. Object and map chunks are 20x20 tiles. Each map tile contains 1 short, and each object tile contains 2 shorts. They're loaded through the C binary loading functions. I also load various player files and crate/storage data, but not on a per-chunk basis (both are handled on a per-request basis).

The thing is, I want the server to be able to handle 1000+ players easily. I don't see how I could single-thread this without it lagging to hell every time I get over 20 people concurrently online.


get rid of your spikes ;)

How would that help? Wouldn't it just be running the same processes?

EDIT:
Wouldn't I also have to make it non-blocking?
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 05, 2013, 11:16:11 pm
The reason it is locking up now is because of your threading model. Ironically, it would perform much better if you used a single thread to process your networking. You don't need more than 1 thread to handle rudimentary networking tasks.

If you check out http://en.wikipedia.org/wiki/C10k_problem you will see that there is a lot of widely known software that can handle 10000+ connections and the way they handle it is to either use asynchronous IO or some form of event-driven design. If they offloaded the network handling to multiple threads, the overhead would kill them at some point and that would make the software unscalable.

If you think that your server already can't handle 20 people concurrently, you should look into what the bottleneck is and resolve that before threading everything to oblivion. On the client side, the only common bottleneck is normally the rendering, but since your server only handles networking and game state updates it should have an easy time handling a large amount of players. If not, there is something wrong with your design.

If you read this: http://redmine.lighttpd.net/projects/1/wiki/DevelProblemAndSolution#Problem-5 you will see that the problems that the designer of lighttpd ran into are exactly those that I already mentioned before. That is probably the only place where you would need threading.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 06, 2013, 01:25:48 am
Ok, one quick question before I actually go ahead and do all of this:
Is there an example of a non-blocking setup? I've seen the example of a blocking setup in the selector documentation, but I can't find one for non-blocking.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 06, 2013, 01:41:22 am
No, unfortunately there is no example using a non-blocking socket. There is not much difference anyway. You just try to receive from the socket and if receive() returns NotReady, then you know that there was nothing waiting. I still recommend to use selectors even with non-blocking sockets. It might seem counter-intuitive but the selector strictly speaking only gives you a "hint" as to whether a resource is ready or not. In performance critical situations, it might just happen that a selector flags a socket as ready but you will still block for a small amount of time on the receive(). Since we want all the performance we can get we need to take this into consideration as well.

If you want to read more you can check this out: http://www.kegel.com/c10k.html
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 06, 2013, 08:52:08 am
Ok, I changed the networking around and people are having a lot of problems. Other people seem to have their clients not getting packets, and my packets that are sent to me are either distorted or not coming through.

Here is my receive packets function which is called every frame:
void NetworkManager::recPackets()
{
        Connector* newConnector = new Connector;
        newConnector->socket.setBlocking(false);
        newConnector->player = NULL;

        if(listener.accept(newConnector->socket) == sf::Socket::Done)
        {
                //      Adds a new client to client list
                maxConID++;
                newConnector->setID(maxConID);
                ent::connectors.push_back(newConnector);
        }

        else
        {
                //      Nothing we can do...
                delete newConnector;
        }


        //      The listener socket is not ready, test all other sockets
        for(sf::Int32 i = 0; i < ent::connectors.size(); i++)
        {
                sf::Packet packet;
                sf::Socket::Status status = ent::connectors[i]->socket.receive(packet);

                if(status == sf::Socket::Done)
                {
                        sf::Uint32 packetCode;
                        packet >> packetCode;

                        handlePacket(packetCode, packet, ent::connectors[i]->getID());
                }

                else if(status == sf::Socket::Disconnected)
                {
                        ent::playerHandler.handleDisconnect(ent::connectors[i]->getID());
                }

                else if(status == sf::Socket::Error)
                {
                        std::cout << "NetworkingError";
                }

                else if(status == sf::Socket::NotReady)
                {
                        //      Not ready
                }
        }
}
 

and my send packets function:
void NetworkManager::sendPacket(sf::Packet* pack, sf::Int32 connectorID, bool delPacket)
{
        if(getConnectorWithID(connectorID) != NULL)
        {
                Connector* c = getConnectorWithID(connectorID);
                if(c != NULL)
                        c->sendPacket(pack);
        }

        if(delPacket)
        {
                delete pack;
        }
}
 

Why are some packets distorted/missing and why can people from other computers not get all of the packets I'm sending them (I have no problem connecting with 2 clients at the same time on my PC besides the occasional distortion or packet drop).
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 06, 2013, 02:40:54 pm
Your code looks fine to me, you can perform optimizations at a later time. Right now you need to check if the server is receiving data from the clients and correctly accepting incoming connection requests. If the server only sends data as a result of receiving data from clients, there are 2 possible error sources. Either it isn't receiving data correctly, or it isn't sending data correctly. From your code I can't tell which one (or maybe even both) it is. You need to either step through your code and see whether it does what you expect, or add some verbose std::cout debugging output which you can switch off later. You might also want to try out Wireshark to see whether the data corruption is happening in the server or during transmission. Unless you explicitly check what is in the Packets before you send them, there is no way to know whether they contain correct data or not.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 06, 2013, 10:19:52 pm
The thing is that all of the packets are being sent, just some aren't being received. I asked around, and it seems that the worse connection that somebody has, the more packets don't get sent to them.

Could bad connections cause packets to be dropped with non-blocking networking setups?\

EDIT:
I found this topic and it seems to have a similar problem... http://en.sfml-dev.org/forums/index.php?topic=3363.0
I don't understand what "polling" the socket means though.

EDIT 2:
So I think on the send() function, I should handle NOTREADY somehow? What should I do? I'm 99% sure that this is the root of my problems.
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 06, 2013, 10:37:00 pm
If you use TCP then this should be taken care of at the transport layer. You shouldn't have to care about "lossy" connections. As long as some bits can get through TCP will make sure it recovers from errors eventually. This all occurs transparently and you as the application developer shouldn't have to care one single bit. If TCP determines that it cannot recover from an error it will just terminate the connection and SFML will notify you of this.

You can monitor this with Wireshark. You will see TCP retransmits marked as such if sending data over a crappy connection.

You also shouldn't forget that because TCP connections are full duplex, when you set them to non-blocking, it also affects sending. I don't know what your sending code looks like, but you are required to check the status code returned from the send() method as well. If it returns NotReady, you need to reattempt to send the data at a later time. If it returns Error or anything other than Done... well... you need to handle it as well ;).
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 06, 2013, 10:41:39 pm
I don't know what your sending code looks like, but you are required to check the status code returned from the send() method as well. If it returns NotReady, you need to reattempt to send the data at a later time. If it returns Error or anything other than Done... well... you need to handle it as well ;).

THAT IS THE PROBLEM! I KNEW IT!

Okay... I'll try to fix this. I guess I'll put all "NotReady" packets inside of a vector and have the server cycle through it and attempt to resend it every frame.
Title: Re: TCP Lag Reduction Techniques?
Post by: K-Bal on April 06, 2013, 11:44:01 pm
The essence of this thread should be condensed and put somewhere into the SFML networking tutorials ;)
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 06, 2013, 11:57:50 pm
The essence of this thread should be condensed and put somewhere into the SFML networking tutorials ;)

Hahaha agreed!

Anyways, with my new solution, it won't send the packets in order, resulting in a lot of problems. I'm going to have to put a std::list inside of the "droppedPackets" struct. I'll first check if the specific connector has any pending packets, and if it does, I'll add a packet into the list of packets it has to send first, that way it's always in order.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 10, 2013, 04:31:21 am
Ok, having more problems. It's now sending the packets in order, but it's really buggy (it corrupts the packets). Also, the receive is also not working sometimes for those with bad ping and connection times.

How I handle dropped packets:

struct droppedPacket
{
        std::list<sf::Packet*> spack;
        sf::Int32 connectorID;
};

void NetworkManager::addDroppedPacket(sf::Packet* spack, sf::Int32 connectorID)
{
        bool found = false;
        for(sf::Int32 i = 0; i < droppedPackets.size(); i++)
        {
                if(droppedPackets[i].connectorID == connectorID)
                {
                        found = true;
                        droppedPackets[i].spack.push_front(spack);
                }
        }

        if(!found)
        {
                droppedPacket dp;
                dp.spack.push_back(spack);
                dp.connectorID = connectorID;
                droppedPackets.push_back(dp);
        }
}

void NetworkManager::retryDroppedPackets()
{
        for(sf::Int32 i = 0; i < droppedPackets.size(); i++)
        {
                Connector* con = getConnectorWithID(droppedPackets[i].connectorID);

                if(con != NULL)         //      client still online
                {
                        sf::Socket::Status status = con->socket.send(*droppedPackets[i].spack.back());

                        if(status == sf::Socket::Done)
                        {
                                //      packet sent correctly!
                                delete droppedPackets[i].spack.back();
                                droppedPackets[i].spack.pop_back();

                                if(droppedPackets[i].spack.empty())
                                        droppedPackets.erase(droppedPackets.begin() + i);
                        }

                        else if(status == sf::Socket::Disconnected)
                        {
                                ent::playerHandler.handleDisconnect(ent::connectors[i]->getID());
                        }

                        else
                        {
                                //      Packet did not send correctly
                        }
                }

                else    //      client disconnected
                {
                        while(!droppedPackets[i].spack.empty())
                        {
                                delete droppedPackets[i].spack.back();
                                droppedPackets[i].spack.pop_back();
                        }

                        droppedPackets.erase(droppedPackets.begin() + i);
                }
        }
}

bool NetworkManager::doesConnectorHaveDroppedPacket(sf::Int32 connectorID)
{
        for(sf::Int32 i = 0; i < droppedPackets.size(); i++)
        {
                if(droppedPackets[i].connectorID == connectorID)
                        return true;
        }
        return false;
}
 


How I handle the initial send:

bool Connector::sendPacket(sf::Packet* spack)
{
        if(!ent::networkManager.doesConnectorHaveDroppedPacket(ID))
        {
                sf::Socket::Status status = socket.send(*spack);

                if(status == sf::Socket::Done)
                {
                        //      packet sent correctly!
                        return true;
                }

                else
                {
                        sf::Packet* spack2 = new sf::Packet(*spack);
                        ent::networkManager.addDroppedPacket(spack2, ID);
                        return false;
                }
        }

        else
        {
                sf::Packet* spack2 = new sf::Packet(*spack);
                ent::networkManager.addDroppedPacket(spack2, ID);
                return false;
        }
}
 


How I handle receives:

void NetworkManager::recPackets()
{
        Connector* newConnector = new Connector;
        newConnector->socket.setBlocking(false);
        newConnector->player = NULL;

        if(listener.accept(newConnector->socket) == sf::Socket::Done)
        {
                //      Adds a new client to client list
                maxConID += 1;
                newConnector->setID(maxConID);
                ent::connectors.push_back(newConnector);
        }

        else
        {
                //      Nothing we can do...
                delete newConnector;
        }


        //      The listener socket is not ready, test all other sockets
        for(sf::Int32 i = 0; i < ent::connectors.size(); i++)
        {
                sf::Packet packet;
                sf::Socket::Status status = ent::connectors[i]->socket.receive(packet);

                if(status == sf::Socket::Done)
                {
                        ent::connectors[i]->logClock.restart();

                        sf::Uint32 packetCode;
                        packet >> packetCode;

                        handlePacket(packetCode, packet, ent::connectors[i]->getID());
                }

                else if(status == sf::Socket::Disconnected)
                {
                        ent::playerHandler.handleDisconnect(ent::connectors[i]->getID());
                }

                else if(status == sf::Socket::Error)
                {
                        std::cout << "NetworkingError";
                }

                else if(status == sf::Socket::NotReady)
                {
                        //      Not ready
                        //std::cout << "not ready";
                }
        }
}
 


Again, this (mostly) only occurs when the other connector has a bad connection and high ping times. Know of any problems with this?
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 10, 2013, 10:25:30 am
What do you mean by "corrupts the packets"? If you mean data corruption, how do you determine this? Did you check whether this corresponds to what the server is sending?

As for the receive not working, it might also be the client code that is not sending/functioning properly. If you want to make robust software that functions even over bad connections, you need to make sure that both sides are able to handle such a scenario.

Maybe you could show the client send and receive code as well.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 11, 2013, 01:46:30 am
What do you mean by "corrupts the packets"? If you mean data corruption, how do you determine this? Did you check whether this corresponds to what the server is sending?

As for the receive not working, it might also be the client code that is not sending/functioning properly. If you want to make robust software that functions even over bad connections, you need to make sure that both sides are able to handle such a scenario.

Maybe you could show the client send and receive code as well.

Ok. Here is the client receive code (it is multi threaded and blocking still since I haven't had any problems with it).

while(running)
        {
                while(gl::Vars::connected)
                {
                        sf::Packet pack;
                        socket.receive(pack);

                        gl::mutex.lock();
                        sf::Int32 packetType;           //      Determines what the packet contains, different packet types contain different info

                        if(pack >> packetType)
                        {
                                       /*
                                                      HANDLE PACKETS

                                        */

            }

                        else
                        {
                                std::cout << "Junk Packet";
                        }

                        gl::mutex.unlock();
                        sf::sleep(sf::milliseconds(10));
                }
                sf::sleep(sf::seconds(.1));
        }
}
 

I really don't think this is a client sided issue since it was receiving the packets correctly before I changed it all around.

By the way, I also simplified the server sends:
void Connector::sendPacket(sf::Packet* spack)
{
        if(packets.size() <= 0)
        {
                sf::Socket::Status status = socket.send(*spack);

                if(status == sf::Socket::NotReady)
                {
                        packets.push(*spack);
                }
        }

        else
        {
                packets.push(*spack);
        }
}
 

void NetworkManager::retryDroppedPackets()
{
        for(sf::Int32 i = 0; i < ent::connectors.size(); i++)
        {
                if(ent::connectors[i]->packets.size() > 0)
                {
                        sf::Socket::Status status = ent::connectors[i]->socket.send(ent::connectors[i]->packets.front());

                        if(status != sf::Socket::NotReady)
                        {
                                ent::connectors[i]->packets.pop();
                        }
                }
        }
}
 
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 11, 2013, 02:31:27 am
You should handle the case that send() returns sf::Socket::Error as well. TCP does not complain about little things that can be corrected over time so whenever an error really occurs you should take it seriously and try to resolve it. It is different from sf::Socket::Disconnected, sf::Socket::NotReady or sf::Socket::Done in that it indicates something that is not supposed to happen, even over bad connections. Like I said before, if TCP determines that it doesn't have a possibility to recover from errors, it will RST the connection and you will get sf::Socket::Disconnected back from SFML, not sf::Socket::Error.

I see that in your client code, you don't handle the return value of the receive(). Even if the client code works for now, adding some sort of handling or logging for the case that errors might happen will save you a lot of headaches in the future.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 12, 2013, 02:52:32 am
You should handle the case that send() returns sf::Socket::Error as well. TCP does not complain about little things that can be corrected over time so whenever an error really occurs you should take it seriously and try to resolve it.

How do I "resolve it" exactly?
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 12, 2013, 03:41:49 am
Check if sf::Socket::Error is returned. If you are indeed getting socket errors, you can check what kind of errors you are getting using the operating system specific function WSAGetLastError() in Windows or checking errno in Linux.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 12, 2013, 04:34:59 am
Check if sf::Socket::Error is returned. If you are indeed getting socket errors, you can check what kind of errors you are getting using the operating system specific function WSAGetLastError() in Windows or checking errno in Linux.

Oh ok. Thank you! I think it was actually a client issue, because after I switched that client recv() function around, nobody has crashed yet (I have my fingers crossed).
Title: Re: TCP Lag Reduction Techniques?
Post by: Jungletoe on April 13, 2013, 01:22:49 am
Yeah, people are still getting deformed packets. I still can't test them since it only happens with people who have bad connections. The client nor the server is returning any errors on receive.

EDIT:
It seems to be because the client is now getting deadlocked. Is it even possible to have a single threaded client with a loading screen and all?

Client send code:
void NetworkManager::sendPacket(sf::Packet* pack)
{
        if(gl::Vars::connected)
        {
                socket.send(*pack);
                delete pack;
        }
}
 
Title: Re: TCP Lag Reduction Techniques?
Post by: The Terminator on April 13, 2013, 05:34:44 am
I can't really help you on the networking stuff, but I would recommend avoiding raw pointers that you use. A lot of memory problems can arise from using that, and switching everything from things like

sf::Packet* packet

to

std::unique_ptr<Packet> packet

Isn't hard :P
Don't forget to use std::move :)
Title: Re: TCP Lag Reduction Techniques?
Post by: binary1248 on April 13, 2013, 05:53:01 am
It is always possible to do things in a single thread. After all, on single core CPUs there is only one execution context and the operating system just "pretends" that multiple threads can run "simultaneously".

If you want your client to be single threaded, all you have to do is make sure that you use polling and make all socket/file access asynchronous (non-blocking). That way you just have to initiate them and periodically check if the operation has been completed. That way you will never have to block on anything. Some things however cannot be done asynchronously, such as database access in some cases. In those cases you have no other choice than to thread the access itself and transform it into an asynchronous operation for the rest of your application to use.
Title: Re: TCP Lag Reduction Techniques?
Post by: Jesper Juhl on June 19, 2014, 09:44:07 pm
I know this is an old thread, but I think I have something useful to add for future readers, so here goes.

Reducing lag is often a matter of reducing latency, so on TCP sockets you often want to disable Nagle's algorithm (http://en.m.wikipedia.org/wiki/Nagle's_algorithm). You can do this with the setsockopt() function and the TCP_NODELAY option.
Title: Re: TCP Lag Reduction Techniques?
Post by: Laurent on June 19, 2014, 09:50:00 pm
https://github.com/SFML/SFML/blob/master/src/SFML/Network/Socket.cpp#L106
Title: Re: TCP Lag Reduction Techniques?
Post by: Jesper Juhl on June 19, 2014, 09:51:53 pm
Ohh. Thanks Laurent. I guess I should have checked the code first  :D