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

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - TheRabbit

Pages: [1] 2
1
General / Re: How can I lock one thread from another?
« on: November 12, 2013, 09:04:52 pm »
Hey that's an even better idea, just pass in the accepted socket instead of an entire blank client that only has a socket. I'll have to add a client function that moves a socket into the client.socket though.

Edit: Now that I'm at my computer, here's how it does it:

class Client
{
public:
    //Default constructor-- should probably never be used...
        Client()
    : socket(new sf::TcpSocket)
    {
                ID = -1;
    }

        //Move constructor
    Client(Client&& source)
    : socket(std::move(source.socket))
    {
                ID = source.ID;
                name = source.name; //client's user name
    }

    //Move assignment operator
    Client& operator= (Client&& source)
    {
        ID = -1;
        socket = std::move(source.socket);
        return *this;
    }

        int loadClient(const std::string &clientName); //looks up a user based on their name

public:
        std::unique_ptr<sf::TcpSocket> socket; //socket handling communication to this client
        std::string name; //client's user name
        int ID; //client's unique ID
};
class Server
{
public:
        Server(): mWindow(sf::VideoMode(400, 400), "Server Connection", sf::Style::Close){};//force window to certain size
        void run();
        sf::RenderWindow mWindow;

public:

private:
        void processEvents(); //handles input
        void loginAttempt(std::unique_ptr<sf::TcpSocket> &inputSocket); //handles login attempts

private:
        std::list<Client> clientList; //sockets we will use to communicate
        sf::SocketSelector selector; //used to handle multiple clients
        sf::TcpListener listener; //will listen on a specific port for an incoming connection

};
void Server::run()
{
        if (listener.listen(port) != sf::Socket::Done) {
                std::cout << "Failed to listen to port " + std::to_string(port) + ". Something else already attached to port?\n";
                return;}
        std::cout << "Currently listening to port " + std::to_string(port) + ". Waiting for first connection...\n";

        selector.add(listener); //put the new connection listener into our selector
        listener.setBlocking(false); //we no longer want to block when checking for new connections

        while(mWindow.isOpen()) //main server hosting loop
        {      
                if (selector.wait(sf::seconds(1.f))) //server sits and waits for an event to occur on the selector
                {
                        if(selector.isReady(listener)) //we have a new incoming connection
                        {
                               
                                std::unique_ptr<sf::TcpSocket> tempSocket(new sf::TcpSocket);
                                if (listener.accept(*tempSocket) == sf::Socket::Done)
                                        std::thread (&Server::loginAttempt, this, std::move(tempSocket)).detach();//create a new thread to handle the login events
                        }
...
void Server::loginAttempt(std::unique_ptr<sf::TcpSocket> &inputSocket)
{
        Client *client = new Client();
        client->socket = std::move(inputSocket);
        bool success = false; //indicates login success or failure
...
        if (!success) //client failed to pass all conditions to connect to the server, so we remove them
        {
                selector.remove(*(client->socket)); //stop listening to the client that disconnected
                std::cout << "Client failed to login.\n";
                return;
        }
        else
        {
                //client passed all conditions to connect to server
                clientList.push_back(std::move(*client)); //push the client we made into the list!
                return;
        }
}

2
General / Re: How can I lock one thread from another?
« on: November 12, 2013, 08:58:05 pm »
It's really weird to check if the listener has an incoming connection and then accept it in another thread. To me, the obvious solution is the following:

if (selector.isReady(listener))
{
    if (listener.accept(client->socket) == sf::Socket::Done)
        std::thread(login, this).detach();
}
Well, the reason I wasn't doing it this way is because of that client pointer. I store a client list, and use iterators to access them, and I don't want to add a client to the list until they've been authenticated (so that I'm not sending chat messages intermingled with the hand shake). What I could do is generate the blank client right there and do the socket connection, but keep the push inside my login function by just passing it in. It adds an extra copy function to the whole process, but that isn't significant in the scheme of things.
Thanks, I'll give this a shot.

3
General / How can I lock one thread from another?
« on: November 12, 2013, 04:44:06 pm »
I'm trying to combat a race condition that occurs when a user connects to my server. Because the connection function is spawned in a thread, the main thread continues to see an "incoming connection" until one of the threads latches, which often results in 4 or 5 extra threads being created. I thought three fix for this would be fairly simple, just lock a mutex right before the thread launches, and then unlock the mutex add soon as a connection latches. But, mutexs throw an exception when you try and unlock in a different thread than you locked in....

So instead I did something like this: (please ignore syntax, I'm typing this up on my phone from memory)
 bool connLock = false;
...
if (selector.isReady(listener) && !connLock)
{
    std::thread(login,this);
    connLock = true;
}

...
login()
{

    if (listener.accept(client->socket) != sf::Socket::Done)
    {
      connLock = false;
      Return;
    }
    connLock = false;
....
 
I probably messed up some of those lines of code, sorry.
Anyway, this works pretty good, it lets my sever thread keep chugging away on other things while the login thread does it thing, but there has to be a better way to do this, right?

Any suggestions?

4
General discussions / Re: Converted server to running multithreaded today
« on: November 11, 2013, 06:20:01 pm »
Congrats! :)

Now for round two and three: Getting rid of all the hidden race conditions and make your server as secure as possible! ;D
Yep, I have one race where the threads try and latch the incoming connection, those 2 incoming connections spawned like 11 threads. I'm thinking I'll lock the main thread from continuing until the connection is latched.

5
General discussions / Converted server to running multithreaded today
« on: November 11, 2013, 07:35:18 am »
Sorry, I just wanted to brag really quick because there's nobody that I can show this to that would care, but I spent all day today converting my server software to run multithreaded and not break or explode.



There was a lot of frustration at the end, every time my thread returned it would cause a debug error. I had forgotten to .detach the threads and so they were disappearing and throwing exceptions. But now it all works! Time to make the rest of the server functions threaded!

6
General / Re: opinions: how to handle resource errors?
« on: November 10, 2013, 09:39:34 am »
I use a (admittedly Windows specific) error box if font loading fails:
MessageBox(HWND_DESKTOP,"Failed loading arial.ttf","Fatal Error",MB_OK);
But once I have a font loaded I created a little (ugly) method of popping up a error message in the middle of the screen:
//pushes message 'stringIn' to the top of the rendering window
//automatically word wraps and centers text
void Game::centerScreenMessage(const std::string &stringIn)
{
        sf::RectangleShape rect;
        rect.setSize(sf::Vector2f(500.f,200.f));//setup a rectangle thats 500 pixels wide and 200 tall
        rect.setFillColor(sf::Color(0,0,51)); //fill it with dark blue
        rect.setPosition(float(int(mWindow.getSize().x/2.f - rect.getSize().x/2.f)),float(int(mWindow.getSize().y/2.f - rect.getSize().y/2.f))); //center the rectangle
       
        sf::Text myText; //setup a text object to hold all the drawing information
        myText.setFont(loginFont); //load the font
        myText.setCharacterSize(24); //set size
        myText.setColor(sf::Color(204,204,204)); //text color = light grey
        myText.setString(stringIn); //set the string

        sf::IntRect cSize = loginFont.getGlyph('a',myText.getCharacterSize(),false).bounds; //used to determine font height, character 'a' doesn't matter
       
        std::deque<int> textSize; //container to hold widths of individual lines
       
        textSize = wrapText(myText,rect); //fill container and linebreak myText

        std::deque<sf::Text> texts; //make a container to hold all the new lines of text
        int j=0;
        mWindow.draw(rect); //draw the text box
        for (unsigned int i = 0; i < textSize.size(); i++)
        {
                texts.push_back(sf::Text(myText)); //put a new element on
                //set position so that the text is center justified:
                texts.back().setPosition(float(int(mWindow.getSize().x/2 - textSize[i]/2)),float(int(mWindow.getSize().y/2 - (cSize.height*textSize.size()) / 2 + (i * cSize.height))));
                j = myText.getString().toAnsiString().find('\n',j); //find the line break
                if (j < 0)
                        texts.back().setString(myText.getString().toAnsiString()); //this is probably redundant
                else
                {
                        texts.back().setString(myText.getString().toAnsiString().substr(0,j)); //break the text string up and then continue processing
                        myText.setString(myText.getString().toAnsiString().substr(j+1));
                }
                mWindow.draw(texts.back()); //draw the text on top of the textbox
        }
        mWindow.display();
}

//wraps text 'textIn' by inserting line breaks based on the width of 'boundingBox' and the font and size of 'textIn'
//returns a deque containing the line width of every line that was segmented
std::deque<int> wrapText(sf::Text &textIn, const sf::RectangleShape &boundingBox)
{
        int lineSize = 0; //counter used to see how long our current line is
        int lastSpace = -1; //counter to keep track of the location of the last space we found
        int maxWidth = 0;
        std::deque<int> output;
        sf::String outputString = "";
        sf::String lastLine;
        sf::Font fontIn = *textIn.getFont();
        lastLine = textIn.getString();

        for (unsigned int i = 0; i < lastLine.getSize(); i++)
        {
                sf::Glyph glyph = fontIn.getGlyph(lastLine[i], textIn.getCharacterSize(),false); //get the current character
                if ((lineSize + glyph.advance) > boundingBox.getSize().x) //check for runoff
                {
                        if (lastSpace != -1)
                        {
                                output.push_back(maxWidth- fontIn.getGlyph(' ',textIn.getCharacterSize(),false).advance); //put a new element in, but remove the size of the last space
                                outputString = outputString + lastLine.toAnsiString().substr(0,lastSpace) + '\n'; //put the current length of string onto the first line
                                lastLine = lastLine.toAnsiString().substr(lastSpace+1); //put the remainder back into lastLine to continue processing
                        }
                        else //processing one really long string with no spaces...
                        {
                                output.push_back(lineSize); //put the size of the current string of characters
                                outputString = outputString + lastLine.toAnsiString().substr(0,i) + '\n'; //put the current length of string onto the first line
                                lastLine = lastLine.toAnsiString().substr(i); //put the remainder back into lastLine to continue processing
                        }                      
                        i=-1; //reset our counters -- time to find out if you like overflow
                        lastSpace = -1;
                        maxWidth = 0;
                        lineSize = 0;
                }
                else
                {
                        lineSize += glyph.advance; //increment linesize by the texture size of the current character
                       
                        if (lastLine[i] == '\n') //if we come accross a natural line break
                        {
                                maxWidth = lineSize - glyph.advance; //ignore the size of the linebreak
                                output.push_back(maxWidth);
                                maxWidth = 0;//reset counters
                                lineSize = 0;
                                lastSpace = -1;
                        }
                        if (lastLine[i] == ' ') //track where the space is
                        {
                                lastSpace = i;
                                maxWidth = lineSize; //set string width to cutoff point
                        }
                }
        }
        output.push_back(lineSize);
        outputString = outputString + lastLine; //put the remainder of the string back in
        textIn.setString(outputString);
        return output;
}

I think I've done too much coding today, I just tried to preview this post by hitting F7 (build)...

7
General / Re: A Thread and RenderWindow issue - My poor Super Mario
« on: November 08, 2013, 06:04:46 pm »
It's probably only because you spelled tutorial wrong  ;D

Tigre, if you do multi-threading the way you explained it in your main post, you'll end up with several race conditions and probably some bad synchronization problems. I'd strongly suggest reading up some more on multi-threading once you get it running (specifically on the two topics I just mentioned).

8
Network / Re: Container(list) of TcpSockets not behaving (NonCopyable)
« on: November 08, 2013, 03:45:54 am »
Thanks for the pointers!

I read this article: http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html and it helped a lot with explaining the new rvalues and the way temporary things work now.

The code is very convoluted because I'm copying and pasting out of other things that I've written, or things written for me (like your code). I'll clean it up once it's working, but my first priority was to get it working.

Thanks again, your posts help a lot.

Here's the final (for now) chat server code:
int hostConnect()
{
        std::list<Client> clientList; //sockets we will use to communicate
        sf::TcpListener listener; //will listen on a specific port for an incoming connection
        sf::Packet dataPacketIn; //data packet used to receive data from a client
        sf::Packet dataPacketOut; //data packet used to transmit data to a client
        sf::SocketSelector selector; //used to handle multiple clients
        unsigned short port=53000; //default port
        std::string str_in; //string used for communication


        if (listener.listen(port) != sf::Socket::Done) {
                std::cout << "Failed to listen to port " + std::to_string(port) + ". Something else already attached to port?\n";
                return 1;}
        else
                std::cout << "Currently listening to port " + std::to_string(port) + ". Waiting for first connection...\n";

        selector.add(listener); //put the new connection listener into our selector
        listener.setBlocking(false); //now that we have our first connection, we no longer want to block when checking for new connections

        while (true) //main connection loop
        {      
                if (selector.wait()) //server sits and waits for an event to occur on the selector
                {
                        if(selector.isReady(listener)) //we have a new incoming connection
                        {
                                clientList.push_back(std::move(Client())); //put a new client into the list
                                if (listener.accept(*clientList.back().socket) != sf::Socket::Done)
                                {
                                        std::cout << "Incoming connection failed.\n"; //error handler... kind of
                                        clientList.pop_back(); //remove the failed connection from the list
                                }
                                else
                                {
                                        selector.add(*clientList.back().socket); //add the new connection into the selector
                                        (*clientList.back().socket).setBlocking(false); //make sure it is non-blocking
                                        std::cout << "Remote client " + (*clientList.back().socket).getRemoteAddress().toString() + " has connected to the server.\n";
                                }
                        }
                        else //if it wasn't an incoming connection it was either an incoming message or a disconnect
                        {
                                dataPacketIn.clear(); //empty our data packet to prepare it for receives
                                bool FLAGDC = true; //use this to break out of loops when we get a disconnect
                                for(auto it = clientList.begin(); ((it != clientList.end()) && (FLAGDC));) //cycle through the available sockets, or break when we hit a DC'd client
                                {
                                        if(selector.isReady(*(*it).socket))
                                        {
                                                switch ((*(*it).socket).receive(dataPacketIn))
                                                {
                                                case sf::Socket::NotReady:
                                                        //this is not the socket you were looking for
                                                        break;
                                                case sf::Socket::Disconnected:
                                                case sf::Socket::Error:
                                                        //lost the connection to this socket
                                                        selector.remove(*(*it).socket); //stop listening to the client that disconnected
                                                        clientList.erase(it); //pop the DC'd client out of the list
                                                        std::cout << "Remote client disconnected.\n";
                                                        FLAGDC = false; //set our breakout flag
                                                        break;
                                                case sf::Socket::Done:
                                                        //a socket is sending us a message!
                                                        dataPacketIn >> str_in; //load the packet into the string
                                                        std::cout << str_in  + "\n"; //print it on the server
                                                        dataPacketOut.clear(); //load the string into a packet to send out
                                                        dataPacketOut << str_in;
                                                        for(auto it2 = clientList.begin(); it2 != clientList.end(); it2++)
                                                                if ((*(*it2).socket).send(dataPacketOut) != sf::Socket::Done) //error catcher... kind of
                                                                        std::cout << "ERROR!! Failed to send data to client!\n";
                                                        break;
                                                }
                                        } //end if(selector.isReady(*it))
                                        if (FLAGDC) //if we didn't hit any dead clients
                                                it++;
                                        else //if we did hit a dead client our iterator is invalid and we need to start the 'for' over again
                                                break;
                                } //end for(auto it = clientList.begin(); it != clientList.end(); it++) //cycle through the available sockets
                        }//end else //if it wasn't an incoming connection it was either an incoming message or a disconnect
                }//selector's if loop
        }//end main while loop
    return 0; //should theoretically never get here...
}
Maybe it will help someone else out.

9
Network / Re: Container(list) of TcpSockets not behaving (NonCopyable)
« on: November 07, 2013, 05:53:28 am »
Just make sure you don't copy when dereferencing. Obtaining a reference to the object doesn't require copyability.

One possibility to do it is std::unique_ptr. In contrast to raw pointers, it automatically frees the memory. If you find that the identifiers become too long, use typedef.
std::list<std::unique_ptr<sf::TcpSocket>> host;
...
std::unique_ptr<sf::TcpSocket> socket(new sf::TcpSocket);
host.push_back(std::move(socket));

Just had a chance to try this and the code written your way works fine, but I can't figure out how to put the socket into a class, everything I've tried either fails at Noncopyable or I get an error about the private member function std::unique_ptr<_Ty>.

I was able to get it working using a regular pointer, but then I was having memory problems because it wasn't letting me delete them...

So here's what the Client class looks like:
class Client
{
public:
        Client();
        ~Client();
        void loadClient(const std::string &clientName); //looks up a user based on their name


public:
        std::unique_ptr<sf::TcpSocket> socket; //socket handling communication to this client
        std::string name; //client's user name
        int ID; //client's unique ID

private:


private:

};
And then running this code:
        std::list<Client> clientList; //sockets we will use to communicate
        clientList.push_back(Client()); //put a new client into the list
Gives this error:
Code: [Select]
1>  main.cpp
1>c:\documents\visual studio 2012\projects\game server\blank sfml\main.cpp(67): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1>          with
1>          [
1>              _Ty=sf::TcpSocket
1>          ]
1>          c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1>          with
1>          [
1>              _Ty=sf::TcpSocket
1>          ]
1>          This diagnostic occurred in the compiler generated function 'Client::Client(const Client &)'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

I tried putting the std::move code into the constructor, but that didn't work either (got the same error).

More help would be appreciated!

Edit: I kind of fixed it. I don't know if this makes any sense, but here's what I did:
class Client
{
public:
        Client();
        Client(const Client &tempC); //added a copy constructor
        ~Client();
        void loadClient(const std::string &clientName); //looks up a user based on their name


public:
        std::unique_ptr<sf::TcpSocket> socket; //socket handling communication to this client
        std::string name; //client's user name
        int ID; //client's unique ID

private:


private:

};

Client::Client()
{
        socket = std::move(std::unique_ptr<sf::TcpSocket> (new sf::TcpSocket));
}

Client::Client(const Client &tempC)
{
        socket = std::move(std::unique_ptr<sf::TcpSocket> (new sf::TcpSocket));
}

Client::~Client()
{
}

int hostConnect()
{
        std::list<Client> clientList; //sockets we will use to communicate
       
        Client newClient;
        clientList.push_back(newClient); //put a new client into the list
        ...
                        if(selector.isReady(listener)) //we have a new incoming connection
                        {
                                Client tempClient;
                                clientList.push_back(Client(tempClient)); //put a new client into the list
                                if (listener.accept(*clientList.back().socket) != sf::Socket::Done)
                                {
                                        printf("Incoming connection failed.\n"); //error handler... kind of
                                        clientList.pop_back(); //remove the failed connection from the list
                                }
                                selector.add(*clientList.back().socket); //add the new connection into the selector listener thing
                                printf(std::string("Remote client " + (*clientList.back().socket).getRemoteAddress().toString() + " has connected to the server.\n").c_str());
                                (*clientList.back().socket).setBlocking(false); //make sure it is non-blocking
                        }
...
                                bool FLAGDC = true; //use this to break out of loops when we get a disconnect
                                for(auto it = clientList.begin(); ((it != clientList.end()) && (FLAGDC));) //cycle through the available sockets
                                {
...
                                                case sf::Socket::Disconnected:
                                                case sf::Socket::Error:
                                                        //lost the connection to this socket
                                                        selector.remove(*(*it).socket); //stop listening to the client that disconnected
                                                        clientList.erase(it);
                                                        printf(std::string("Remote client disconnected.\n").c_str());
                                                        FLAGDC = false;
                                                        break;
...
                                        if (FLAGDC)
                                                it++;
                                        else
                                                break;
 
One problem that I found with my code, and the reason there is that if/else at the very bottom is that if you delete a client in the middle of an auto for loop, it totally breaks the iterator and crashes the program. So instead I made the for loop the way it is.

Tested the server with a few dozen connects and disconnects and had 15 concurrent users at one point, haven't seen any issues at all.

10
Network / Re: SFML 2.1 - Sockets example not working
« on: November 06, 2013, 10:07:12 pm »
Are you port forwarding from your router to the server?

11
General / Re: Execution does not terminate when window closes (VS2010)
« on: November 06, 2013, 10:05:36 pm »
I can almost guarantee it's a problem with your project settings.

I notice you're using main, what's your console doing?

12
Network / Re: Problems with simple chat program.
« on: November 06, 2013, 09:07:26 pm »
Do you get each message exactly twice, or does it just keep repeating the very first message forever?

Try changing [code =cpp] socket.receive(pakiet_receive);[/code] to
 if (socket.receive(pakiet_receive) == sf::Socket::Done)
on the client code.

13
Network / Re: SFML 2.1 - Sockets example not working
« on: November 06, 2013, 08:30:52 pm »
Are you typing Localhost on the two separate computers? Local host basically tells the computer to connect to itself, so obviously that won't work when using 2 different computers.

14
Network / Re: Problems with simple chat program.
« on: November 06, 2013, 08:26:59 pm »
I deleted non-blocking socket , and I change a line with receive function to :
    if ( socket.receive(pakiet_receive)==sf::Socket::Done)
        {
 

And as I can see status is always "done" because client write old message, so receive function works (but not as I want :D ). Anybody can help me ?? :)

Try emptying the packet before putting the new string back into it, both on  client and server.

15
Network / Re: Container(list) of TcpSockets not behaving (NonCopyable)
« on: November 06, 2013, 04:40:19 pm »
Thanks for the reply Nexus. I was thinking that might have been what Laurent meant, but I wasn't sure.

2 quick questions, it seems like unique pointers are smart, in that I don't need to manually clean up after them, so there wouldn't be any additional steps I would need to do when I pop/erase a list element, right?]
 host.erase(*it)
Would handle the erase and cleanup? edit: just remembered I would still need to release it out of the listener, so it isn't totally automated.

Second, why would I do this:
 std::unique_ptr<sf::TcpSocket> socket(new sf::TcpSocket);
host.push_back(std::move(socket));
Instead of:
host.push_back(new *std::unique_ptr<sf::TcpSocket>)

(sorry for the crappy code, doing this from my phone)

Pages: [1] 2