-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added a way for partial sends over non-blocking TcpSockets to be handled properly. #796
Conversation
Do you think someone else could get a partial send under Windows and if so, what would be a possible code example? |
No idea how one would go about testing this on Windows. The "standard" test example consists of sending a huge 40MiB sf::Packet and checking if it can be received intact. On Linux, the packet gets split into multiple partial sends. On Windows, it just "sends" (queues) it all at once. I even tried sending 10x 100MiB sf::Packets. It queues the whole first packet and just returns NotReady for the subsequent ones until they can entirely be queued as well. Remembering the issues that JungleToe was having, he mentioned that they only occurred on Ubuntu, which supports the theory that partial sends only happen on Linux (OS X/Unix?) systems. Maybe there is a reason high performance network servers all run Linux? 😏 |
@mantognini Any ideas about OS X? |
@eXpl0it3r I haven't had a look yet into it but it's on my todo list. I'm still trying to go through my stack of mail since I'm back. ^^' |
Here is some test code: #include <SFML/Network.hpp>
#include <iostream>
void runTcpServer(unsigned short port)
{
sf::TcpListener listener;
if (listener.listen(port) != sf::Socket::Done)
return;
sf::TcpSocket socket;
if (listener.accept(socket) != sf::Socket::Done)
return;
socket.setBlocking(false);
sf::Packet packet;
for (int i = 0; i < 10000000; i++)
packet << i;
while (socket.send(packet) == sf::Socket::NotReady)
{
}
}
void runTcpClient(unsigned short port)
{
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);
sf::TcpSocket socket;
if (socket.connect(server, port) != sf::Socket::Done)
return;
sf::Packet packet;
if (socket.receive(packet) != sf::Socket::Done)
return;
for (int i = 0; i < 10000000; i++)
{
int j;
packet >> j;
if (i != j)
{
std::cout << "Data corruption" << std::endl;
return;
}
}
}
int main()
{
const unsigned short port = 50001;
char who;
std::cout << "Do you want to be a server (s) or a client (c)? ";
std::cin >> who;
if (who == 's')
runTcpServer(port);
else
runTcpClient(port);
std::cout << "Press enter to exit..." << std::endl;
std::cin.ignore(10000, '\n');
std::cin.ignore(10000, '\n');
return 0;
} Like I said, on Windows it doesn't matter whether you are on master or this branch, Windows just doesn't perform partial sends for some reason. On Linux it will print "Data corruption" on master and won't print anything on this branch. |
Thanks for the test code. The new branch fixes things on OS X too. |
This PR has been added to my merge list, meaning it will be merged soon, unless someone raises any concerns. |
This PR adds a new public API. Has this been discussed, or do you feel like it's the only way to go about it? |
I've had some discussions with @binary1248 on IRC and the proper way to solve the issue would be API breaking. As such we're left with either ignoring the issue, which given that some people are quite hung up on it and the fact that it is really a bug, is not really a solution, or add another API and essentially mark the old one as deprecated. 😉 |
Well... essentially we have 2 constraints in this situation:
What follows is that we need to provide the user with more information than the current API does, and without changing the fact that they will need to try to send first anyway. This automatically means that we need to add an additional status. In order to not have to buffer ourselves, we return how many bytes were actually sent as well, which requires a reference parameter since we already return a status. Either we do it like the way it is in this PR, or we try to buffer the bytes that the send wasn't able to fully send in the first call. Either way, we need to return a new status so that the user knows they need to call send again, and this way we don't have to worry about exploding buffer sizes in case the user generates more data to send than their line speed can handle. These changes don't constitute an API break, merely an ABI break. People who just use sf::Packet won't notice anything except that their code behaves properly now on UNIX systems 😉. People who use the raw byte send will be happy that they have something they can work with now. |
Ok, thanks for the details. |
So... everything tested/reviewed? No more issues/remarks? 😁 |
I already said it, but since some concerns were raised, I'll just write it again: This PR has been re-added to my merge list, meaning it will be merged soon, unless someone raises any concerns. |
fb04115
to
d790114
Compare
Fixes #749.
Tested on Linux. Couldn't manage to get a partial send on Windows. OS X also needs testing.