Solved

Urgent please, Iterators and Sockets

Posted on 2003-11-11
19
320 Views
Last Modified: 2013-11-20
Hi,

I have a server app that listens for clients to connect, when they do so they are entered into a CTypedPtrList with their socket and username.

When this is done during the login, i want to be able to send all the clients a list of who is online which i have tried to do here but it only sends one username to each client which is their own name.

if you need more info or more code just ask.

help please.

void CSockServerDlg::doLogin(CConnUser* theUser, CString dataRcvd)
{
      char *pBuf = new char[1025];
      char seps[]   = " ";
      char *token;
      pBuf = dataRcvd.GetBuffer(1025);

      token = strtok(pBuf, seps );

      CString UserName = token;
      token = strtok(NULL,seps);
      CString password = token;

      if (true)//test for something
      {
            theUser->SetUserName(UserName);
            theUser->sendMessage("LOGIN-OK");
      }
      else {

      }

      CConnUser *iterator = NULL; //added

      POSITION pos = m_ConnUserList.GetHeadPosition();

      if ( pos == NULL )
            ; // do something
      else
      {
            while (pos)
            {

                  iterator = m_ConnUserList.GetAt(pos); //added

                  CString m_pViewFind = (iterator->Username()); //added

                  //m_pViewFind added inplace of UserName
                  CString buddyList = " BUDDYLIST " + m_pViewFind ;
                        
                  //send one message to everyone
                  m_ConnUserList.GetNext(pos)->Socket()->Send(buddyList, buddyList.GetLength());
            }
      }

}
0
Comment
Question by:nikon70
  • 9
  • 8
  • 2
19 Comments
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Hi nikon70,

Problem is in th e loop:

------------------
...
              CString m_pViewFind = (iterator->Username()); //added

               //m_pViewFind added inplace of UserName
               CString buddyList = " BUDDYLIST " + m_pViewFind ;
...
------------------

This creates a new string buddylist everytime of form ' BUSSYLIST username' where username is the name of
the user you send it to.

IMO you'll have to step through the list twice, the first time you build a string containing all usernames,
in the second loop you send this string to all users.

hope that helps,

ZOPPO
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 50 total points
Comment Utility
You have to use two loops: one to build the buddy list and a second loop to send the list to all connected users.

E. g.

     CString buddyList = " BUDDYLIST ";
     CConnUser *iterator = NULL; //added

     // First iteration builds buddylist
     POSITION pos = m_ConnUserList.GetHeadPosition();
   
     if ( pos == NULL )
          ; // do something
     else
     {
          while (pos)
          {

               iterator = m_ConnUserList.GetNext(pos); //added

               CString m_pViewFind = (iterator->Username()); //added

               //m_pViewFind added inplace of UserName
               buddyList += " " + m_pViewFind;
                   
          }
     }

   // Second iteration sends list to users
   pos = m_ConnUserList.GetHeadPosition();
    if ( pos == NULL )
          ; // do something
     else
     {
          while (pos)
          {

               iterator = m_ConnUserList.GetNext(pos); //added
               //send one message to everyone
               iterator->Socket()->Send(buddyList, buddyList.GetLength());
         }
     }
    ...


Be aware that all variables defined within a scope { } become invalid after.

Regards, Alex
0
 

Author Comment

by:nikon70
Comment Utility
itsmeandnobodyelse:

Have had a play with the loops but im still getting the same result.

I have made the variables globally defined now too and still not working as it should.

???

Thanks;
 tom
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Please check the following statement in the first loop:

               //m_pViewFind added inplace of UserName
               buddyList += " " + m_pViewFind;

First, buddylist MUST be defined  outside of the loop. If you have

               CString buddylist += "m_pViewFind;

then a second variable buddylist is defined which is valid only within {} of the loop (scope);

Second, did you see the "+=" operator? That makes that your names get appended.

If you set a break point before the second loop you should see that 'buddylist' is properly filled with all names.


Hope, that helps

Alex
 
0
 

Author Comment

by:nikon70
Comment Utility
Have checked through the code and it is not doing what you think it does,

client 1 connects and gets its name,
client 2 connects and gets client 1's name, and client 1 gets its own name again.
then if client 3 connects it gets client 1's name and client 1 get its own and client 2 gets client 1 name...

see the pattern?



0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Maybe the following part of your program is wrong:

     if (true)//test for something
     {
          theUser->SetUserName(UserName);
          theUser->sendMessage("LOGIN-OK");
     }
 
What i am missing is that you append the given pointer "theUser" to the list

       m_ConnUserList.Append(theUser);

I assumed that it has be done before calling doLogin(...).

Note, that you may not delete 'theUser' after calling 'doLogin' if you append the pointer to the list.

Regards, Alex

     

     
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Maybe you could show your code again as it is now after your modifications?
0
 

Author Comment

by:nikon70
Comment Utility
The OnAccept code:

void CSockServerDlg::OnAccept()
{
      //MessageBox("Now accepting...", "Accepting", MB_OK);
      CConnUser *user = new CConnUser();
      CConnSocket *socket = new CConnSocket();
      user->setSocket(socket);
      socket->SetParent(this); // was remout
      socket->SetUser(user);

      m_ConnUserList.AddTail(user);
      m_sListenSocket.Accept(*socket);

}


The doLogin code:

void CSockServerDlg::doLogin(CConnUser* theUser, CString dataRcvd)
{
      char *pBuf = new char[1025];
      char seps[]   = " ";
      char *token;
      pBuf = dataRcvd.GetBuffer(1025);

      token = strtok(pBuf, seps );

      CString UserName = token;
      token = strtok(NULL,seps);
      CString password = token;

      if (true)// fix later
      {
            theUser->SetUserName(UserName);
            theUser->sendMessage("LOGIN-OK");
      }
      else
      {
      }

     buddyList = " BUDDYLIST ";
     CConnUser *iterator = NULL; //added

     // First iteration builds buddylist
     POSITION pos = m_ConnUserList.GetHeadPosition();
   
     if ( pos == NULL )
          ; // do something
     else
     {
          while (pos)
          {

               iterator = m_ConnUserList.GetNext(pos); //added

               m_pViewFind = (iterator->Username()); //added

               //m_pViewFind added inplace of UserName
               buddyList += " " + m_pViewFind;
                   
          }
     }

   // Second iteration sends list to users
   pos = m_ConnUserList.GetHeadPosition();
    if ( pos == NULL )
          ; // do something
     else
     {
          while (pos)
          {

               iterator = m_ConnUserList.GetNext(pos); //added
               //send one message to everyone
               iterator->Socket()->Send(buddyList, buddyList.GetLength());
         }
     }

           
}


When doLogin is called....

the buddyList is meant to send all the users , all the users in the m_ConnUserList.

But all it does is send every user logged in the first user in the list.

I have also tried nesting the if in the while and it still produces the same result (ie send every user logged in the only first user in the list not all the users)

Tom
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Can you give us the code of class CConnUser?
Do you know how many iterations are done for the 3rd client login?
Did you get a message for each client?

Regards, Alex
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:nikon70
Comment Utility
CConnUser.h

// ConnUser.h: interface for the CConnUser class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_CONNUSER_H__B176695C_A075_4910_BD73_49FA31E33F93__INCLUDED_)
#define AFX_CONNUSER_H__B176695C_A075_4910_BD73_49FA31E33F93__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "ConnSocket1.h"

class CConnUser  
{
public:
      void sendMessage(CString);
      void SetUserName(CString);
      CString Username();
      CConnSocket* Socket();
      void setSocket(CConnSocket*);
      CConnUser();
      virtual ~CConnUser();

private:
      CConnSocket *m_socket;
      CString m_sUserName;

};

#endif // !defined(AFX_CONNUSER_H__B176695C_A075_4910_BD73_49FA31E33F93__INCLUDED_)




CConnUser.cpp

// ConnUser.cpp: implementation of the CConnUser class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SockServer.h"
#include "ConnUser.h"


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CConnUser::CConnUser()
{

}

CConnUser::~CConnUser()
{

}

void CConnUser::setSocket(CConnSocket *tempSocket)
{
      m_socket = tempSocket;

}

CConnSocket* CConnUser::Socket()
{
      return m_socket;
}

CString CConnUser::Username()
{
return m_sUserName;
}

void CConnUser::SetUserName(CString tempUserName)
{
m_sUserName = tempUserName;
}

void CConnUser::sendMessage(CString theString)
{
      m_socket->Send(theString, theString.GetLength());
}


Yes, each client gets a message but only one.
0
 

Author Comment

by:nikon70
Comment Utility
Ah ha,

Found the errors in my way.. its the way i am handling it on the receiving side by the clients... they receive:

BUDDYLIST username username username ... etc


cheers for your help Alex.

will let you know how i get on ...

Tom
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Seems all ok to me.

First, you should debug the server and check the contents of BuddyList just before sending.

Maybe the problem is on the client's side? Perhaps the receive buffer is too short?

Regards, Alex
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
Great!

Seems you've reached the end of mystery  :-)

Regards, Alex



0
 

Author Comment

by:nikon70
Comment Utility
Just one question before I close this question for you Alex,

how do i delete users and the associtated socket from my CTypedPtrList???
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
In OnClose() you may want to delete a single client from the list by calling a new member function
of CSockServerDlg, e. g.   CSockServerDlg::logout(CConnSocket* pSock);


To implement this member function make another iteration and stop if the user that has disconnected is found (you could compare the user names of psock and iterator).

Then, you may delete the user by

     m_ConnUserList.RemoveAt(pos);
     delete iterator;

In the destructor of CSockServerDlg you may iterate again and delete all elements still left in list.

Hope that is clear enough, Alex



0
 

Author Comment

by:nikon70
Comment Utility
yeah i get the concept but not sure how to code it as such, at the moment i have read the msdn:

   CObList list;
   POSITION pos1, pos2;
   CObject* pa;
   
   list.AddHead( new CAge( 21 ) );
   list.AddHead( new CAge( 40 ) );
   list.AddHead( new CAge( 65 ) ); // List now contains (65 40, 21).
   for( pos1 = list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; )
   {
       if( *(CAge*) list.GetNext( pos1 ) == CAge( 40 ) )
       {
           pa = list.GetAt( pos2 ); // Save the old pointer for
                                  //deletion.
           list.RemoveAt( pos2 );
           delete pa; // Deletion avoids memory leak.
       }
   }


and cant workout how to get it to work for myself, this is what i got:

      if (command == "BYE") //delete theUser from the array.
      {
            CConnUser *iterator = NULL;

            POSITION pos = m_ConnUserList.GetHeadPosition();
            
            while(pos != NULL)
            {
                  iterator = m_ConnUserList.GetNext(pos);

                  if(iterator == theUser)
                  {
                        iterator = 0L;
                        
                        //m_ConnUserList.RemoveAt(pos); //not sure on this...
                        //delete iterator; //not sure on this...

                        break;
                  }      
            }

            delete theUser->Socket();
            delete theUser;
      }

suggestions ? =)
0
 

Author Comment

by:nikon70
Comment Utility
Done some more fiddling about ..

here's the code:

      if (command == "BYE") //delete theUser from the array.
      {
            CConnUser *iterator = NULL;

            POSITION pos = m_ConnUserList.GetHeadPosition();
            
            while( pos != NULL )
            {
                  iterator = m_ConnUserList.GetNext(pos);

                  if(iterator == theUser)
                  {
                        delete iterator; //not sure what this does

                        // need some code here to
                        // delete the user
                        // from the list as the user
                        // is now offline
                  }
            }
            m_ConnUserList.RemoveAll(); // clears all users from the list

      }
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 50 total points
Comment Utility
Ok, let's start from the beginning:

In OnAccept() you had this code:

void CSockServerDlg::OnAccept()
{
     //MessageBox("Now accepting...", "Accepting", MB_OK);
     CConnUser *user = new CConnUser();
     CConnSocket *socket = new CConnSocket();     user->setSocket(socket);
     socket->SetParent(this); // was remout
     socket->SetUser(user);

     m_ConnUserList.AddTail(user);
     m_sListenSocket.Accept(*socket);

}

That means you create a new instance of CConnUser and CConnSocket and that should be deleted if the user makes a logout. Then, the user pointer is stored to the list and this entry should be removed from list when logout (in order to show a correct buddylist if you get another login).

I will assume that CConnSocket is derived from CASyncSocket and that you have a handler CConnSocket::OnClose() that gets called when the user logouts. Then, you may get the pointer to the associated user and the pointer of CSockServerDlg where the m_ConnUserList is a member from your socket.  Then, you may call the logout function of the dialog.

void CConnSocket::OnClose()
{
      ....
      CConnUser*        pUser = GetUser();  
      CSockServerDlg* pDlg   = GetParent();
      pDlg->logout(pUser);    
}

By the way, you shouldn't call the user pointer 'theUser' because the prefix 'the' normally is used only if there is one and only one instance of a class, e. g. theApplication or theServer.

Now, the deletion of user and socket:

void CSockServerDlg::logout(CConnUser* pUser)
{

          CConnUser *iterator = NULL;

          POSITION pos = m_ConnUserList.GetHeadPosition();
         
          while( pos != NULL )
          {
               iterator = m_ConnUserList.GetNext(pos);

               if(iterator == pUser)
               {
                    // delete user
                    delete pUser; // that frees the storage of the CConnUser instance you have created in OnAccept()

                    m_ConnUserList.RemoveAt(pos);
                    break;
               }
           }

           // Here you may show the buddylist to all remaining users
           // You may copy the code from logon() or - BETTER - make a function  showBuddylist(...)
}

Last, you should close the socket and delete the socket instance.

void CConnSocket::OnClose()
{
      ....
      CConnUser*        pUser = GetUser();  
      CSockServerDlg* pDlg   = GetParent();
      pDlg->logout(pUser);  

     // Note, the delete must be last statement because this becomes invalid after
     Close();
     delete this;
}

----------------------------------------------------------------------------------

If you do not use CConnSocket::OnClose()  but get a 'BYE' command somehow then you may do the same this way:


void CSockServerDlg::OnCommand(CString command, CConnSocket* pSock)
{
     // Note, the second argument should be the socket where the command comes from
     CConnUser* pUser = pSock->GetUser();
     if (command == "BYE")
     {
          CConnUser *iterator = NULL;

          POSITION pos = m_ConnUserList.GetHeadPosition();
         
          while( pos != NULL )
          {
               iterator = m_ConnUserList.GetNext(pos);

               if(iterator == pUser)
               {
                    // delete user
                    delete pUser; // that frees the storage of the CConnUser instance you have created in OnAccept()

                    // remove the deleted entry from list
                    m_ConnUserList.RemoveAt(pos);

                    // close and delete socket
                    pSock->Close();
                    delete pSock;
                    break;
               }
           }
     }
}

----------------------------------------------------------------------------------------------------------------

Very last, the destructor of CSockServerDlg:

CSockServerDlg::~CSockServerDlg
{
          CConnUser *iterator = NULL;
          CConnSocket* socket = NULL;
          POSITION pos = m_ConnUserList.GetHeadPosition();
         
          while( pos != NULL )
          {
               iterator = m_ConnUserList.GetNext(pos);
               socket   = iterator->Socket();

               delete iterator; // frees storage
               socket->Close();
               delete socket;
          }
          m_ConnUserList.RemoveAll(); // clears all users from the list
}

OK, that it is.

Regards, Alex

0
 

Author Comment

by:nikon70
Comment Utility
Hi Cheers alex thats all sorted it out.

Nice one thanks alot.

I uped the points too for you.

Tom
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now