AmbikaChetan
asked on
need help on multithreading of proxyserver developed in VC++ with MFC
Hi,
I am working on remote desktop application,based RDP(proxy server )concepts.I want to put the this code in seperate thread.
// Send the packet to all connected clients
void CRDPDlg::SendToAllConnecte dClients(C Packet & Packet)
{
std::vector<CString> vAliases;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
// Locate the server
if (!itAccept->m_bRDV && Packet.m_csServer == itAccept->m_csAlias)
{
// Locate the viewers of this server
std::set<CString>::iterato r itView = itAccept->m_Viewers.begin( );
for (;itView != itAccept->m_Viewers.end(); ++itView)
{
// Locate each viewers connection and send the update
CString csViewer = *itView;
for (std::set<sAccept>::iterat or itAccept2 = m_setAccept.begin();itAcce pt2 != m_setAccept.end();++itAcce pt2)
{
//This has t be in seperate thread.
if (itAccept2->m_bRDV && itAccept2->m_csAlias == csViewer)
{
CTCPSocket & Alias = *(itAccept2->m_pAccept);
Alias << Packet;
}
}
}
break;
}
}
}
I am working on remote desktop application,based RDP(proxy server )concepts.I want to put the this code in seperate thread.
// Send the packet to all connected clients
void CRDPDlg::SendToAllConnecte
{
std::vector<CString> vAliases;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
// Locate the server
if (!itAccept->m_bRDV && Packet.m_csServer == itAccept->m_csAlias)
{
// Locate the viewers of this server
std::set<CString>::iterato
for (;itView != itAccept->m_Viewers.end();
{
// Locate each viewers connection and send the update
CString csViewer = *itView;
for (std::set<sAccept>::iterat
{
//This has t be in seperate thread.
if (itAccept2->m_bRDV && itAccept2->m_csAlias == csViewer)
{
CTCPSocket & Alias = *(itAccept2->m_pAccept);
Alias << Packet;
}
}
}
break;
}
}
}
ASKER
Thanks Sara,i am trying to implement what you have suggested.
Many thanks :)
Ambika
Many thanks :)
Ambika
ASKER
I implemented ,but at viewer end (client machine) screen is not displaying .Not facing any issues.
Thanks
Ambika
Thanks
Ambika
how are the m_setAccept and m_Viewers filled? you may set a breakpoint to the SendToAllConnectedClients whether the containers are filled correctly. you may post some code for me better understanding.
if your dialog already accepted the clients in a former step you would need to keep all relevant information stored in member variables as they now need to be used by the server thread which can safely access all member variables but not reading from dialog or from controls because the message queue runs asynchronously in the main thread and it would be dangerous to access the message queue asynchronously from thread. generally the communication from server thread to the main thread should only be made by member variables which are exclusively written by the server thread or by a thread-safe way using a critical section or by PostMessage calls using the 'pthis' pointer passed as argument. the communication from main thread to server thread only should by writing to members exclusively for write of the main thread (like the stop flag), or by writing in a thread-safe manner to a queue which solely is designated for passing requests from main thread (dialog) to server thread.
the code i posted would only move the call of SendToAllConnectedClients to a thread. when the function returns the thread will end. if that is not what you want you would need to put an infinite loop into the thread-function which only would break when the user closes the dialog or stops the communication to the clients actively.
the general design of such client server application would be a little bit different to the above. the worker thread would be the server. the dialog would be the user interface of the server (only). then, the server (thread) would wait for clients to connect. it could do so without blocking by calling select with timeout on the port where the clients connect to. when the select call succeeds the next accept call would not block. the server could create a new thread (server-client thread) which then handles the connection. you would pass a pointer to structure from server thread to server-client thread where you could pass the pointer to the dialog (this) and a queue member which should be used to pass requests from server to server-client. also the server-client threads would do an infinite loop and wait either for new requests from server thread (sent to the queue) or get response from the client view. to do both without blocking they normally would poll on their queue for new requests and would call select with timeout on idle time. also on idle time they would check for the stop flag (or better a stop request in the queue).
Sara
if your dialog already accepted the clients in a former step you would need to keep all relevant information stored in member variables as they now need to be used by the server thread which can safely access all member variables but not reading from dialog or from controls because the message queue runs asynchronously in the main thread and it would be dangerous to access the message queue asynchronously from thread. generally the communication from server thread to the main thread should only be made by member variables which are exclusively written by the server thread or by a thread-safe way using a critical section or by PostMessage calls using the 'pthis' pointer passed as argument. the communication from main thread to server thread only should by writing to members exclusively for write of the main thread (like the stop flag), or by writing in a thread-safe manner to a queue which solely is designated for passing requests from main thread (dialog) to server thread.
the code i posted would only move the call of SendToAllConnectedClients to a thread. when the function returns the thread will end. if that is not what you want you would need to put an infinite loop into the thread-function which only would break when the user closes the dialog or stops the communication to the clients actively.
the general design of such client server application would be a little bit different to the above. the worker thread would be the server. the dialog would be the user interface of the server (only). then, the server (thread) would wait for clients to connect. it could do so without blocking by calling select with timeout on the port where the clients connect to. when the select call succeeds the next accept call would not block. the server could create a new thread (server-client thread) which then handles the connection. you would pass a pointer to structure from server thread to server-client thread where you could pass the pointer to the dialog (this) and a queue member which should be used to pass requests from server to server-client. also the server-client threads would do an infinite loop and wait either for new requests from server thread (sent to the queue) or get response from the client view. to do both without blocking they normally would poll on their queue for new requests and would call select with timeout on idle time. also on idle time they would check for the stop flag (or better a stop request in the queue).
Sara
ASKER
Thanks for the detailed explaination.Here i am attaching the code.
RDP.zip
RDP.zip
unfortunately I can't open a foreign zip file in my current environment. you may post selected code as text in a code box.
Sara
Sara
ASKER
Here is the RDPDlg class
#include "stdafx.h"
#include "RDP.h"
#include "RDPDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
extern CRDPApp theApp;
static CTCPSocket * g_pAccept = NULL;
// CRDPDlg dialog
CRDPDlg::CRDPDlg(CWnd* pParent /*=NULL*/)
: CDialog(CRDPDlg::IDD, pParent)
, m_TrayIcon(IDR_MAINFRAME)
,m_iPort(8370)
,m_iTimerId(1)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_ MAINFRAME) ;
}
void CRDPDlg::DoDataExchange(CD ataExchang e* pDX)
{
CDialog::DoDataExchange(pD X);
DDX_Control(pDX, IDC_START, m_Start);
DDX_Control(pDX, IDC_STOP, m_Stop);
DDV_MinMaxInt(pDX, m_iPort, 1025, 65535);
DDX_Control(pDX, IDC_CLIENT_LIST, m_ClientList);
}
BEGIN_MESSAGE_MAP(CRDPDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_NOTIFY_TRAY, &CRDPDlg::OnNotifyTray)
ON_COMMAND(ID_RDS_RESTORE, &CRDPDlg::OnRestore)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_START, &CRDPDlg::OnStart)
ON_BN_CLICKED(IDC_STOP, &CRDPDlg::OnStop)
ON_MESSAGE(WM_ACCEPTCONN, &CRDPDlg::OnAcceptConn)
ON_MESSAGE(WM_RECEIVEDATA, &CRDPDlg::OnReceiveData)
ON_MESSAGE(WM_CLOSECONN, &CRDPDlg::OnCloseConn)
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CRDPDlg message handlers
BOOL CRDPDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Enable the controls
EnableControls();
// Set up tray icon
m_TrayIcon.SetNotification Wnd(this,W M_NOTIFY_T RAY);
m_TrayIcon.SetIcon(IDR_MAI NFRAME);
// Get the server information
CString csIp,csPort,csAlias;
BOOL bColors;
LoadSettings(csIp,csPort,c sAlias,bCo lors);
if (csPort.GetLength())
m_iPort = atoi(csPort);
return TRUE; // return TRUE unless you set the focus to a control
}
// Handle notification from tray icon
LRESULT CRDPDlg::OnNotifyTray(WPAR AM uID,LPARAM lEvent)
{
// Let tray icon do default stuff
return m_TrayIcon.OnNotifyTray(uI D,lEvent);
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CRDPDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBK GND, reinterpret_cast<WPARAM>(d c.GetSafeH dc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON );
int cyIcon = GetSystemMetrics(SM_CYICON );
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CRDPDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIc on);
}
// Send the packet to all connected clients
void CRDPDlg::SendToAllConnecte dClients(C Packet & Packet)
{
std::vector<CString> vAliases;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
// Locate the server
if (!itAccept->m_bRDV && Packet.m_csServer == itAccept->m_csAlias)
{
// Locate the viewers of this server
std::set<CString>::iterato r itView = itAccept->m_Viewers.begin( );
for (;itView != itAccept->m_Viewers.end(); ++itView)
{
// Locate each viewers connection and send the update
CString csViewer = *itView;
for (std::set<sAccept>::iterat or itAccept2 = m_setAccept.begin();itAcce pt2 != m_setAccept.end();++itAcce pt2)
{
if (itAccept2->m_bRDV && itAccept2->m_csAlias == csViewer)
{
// This really needs to be in its own thread...
CTCPSocket & Alias = *(itAccept2->m_pAccept);
Alias << Packet;
}
}
}
break;
}
}
}
// Set the state of the UI
void CRDPDlg::EnableControls(BO OL bOffline)
{
// Update the controls
m_Start.EnableWindow(bOffl ine);
m_Stop.EnableWindow(!bOffl ine);
m_StaticPort.EnableWindow( bOffline);
m_Port.EnableWindow(bOffli ne);
m_ClientList.EnableWindow( !bOffline) ;
}
// Hide the window for close notifications
void CRDPDlg::OnClose()
{
ShowWindow(SW_HIDE);
}
// Show the window for the "restore" menu item
void CRDPDlg::OnRestore()
{
ShowWindow(SW_SHOW);
}
// Start the listener
void CRDPDlg::OnStart()
{
// Update the UI entries
UpdateData();
// Disable the UI
EnableControls(FALSE);
// Create a listener
m_Operator.Create(m_iPort) ;
m_Operator.SetParent(GetSa feHwnd());
// Hide the dialog
OnClose();
// Update the settings
CString csPort;
csPort.Format("%d",m_iPort );
//UpdateSettings(CString(" localhost" ),csPort,m _csPasswor d,CString( "RDP Server"),FALSE);
UpdateSettings(CString("lo calhost"), csPort,CSt ring("RDP Server"),FALSE);
}
// Stop the listener
void CRDPDlg::OnStop()
{
// Remove the inactive connection
std::set<sAccept>::iterato r itAccept;
for (itAccept = m_setAccept.begin();itAcce pt != m_setAccept.end();++itAcce pt)
{
// Get the connection
CTCPSocket * pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
// Shutdown and close the connection
Accept.ShutDown();
Accept.Close();
// Clean up the connection
delete pAccept;
}
// Remove all the closed connections
m_setAccept.clear();
m_ClientList.ResetContent( );
// Shutdown and close the operator
m_Operator.ShutDown();
m_Operator.Close();
// Enable the controls
EnableControls();
}
#if defined(_DEBUG)
#else
#if !defined(X64)
#pragma comment(lib,"..\\pxp1.lib" )
#else
#pragma comment(lib,"..\\pxp2.lib" )
#endif
#endif
// Operator telling us that there is a pending connection to accept
LRESULT CRDPDlg::OnAcceptConn(WPAR AM wParam,LPARAM lParam)
{
// Temporarily stop accepting connections until this one can be verified
m_Operator.AsyncSelect(FD_ READ | FD_CLOSE);
// Accept connections again after 10 seconds
SetTimer(m_iTimerId + 1,10000,NULL);
// Allow a new connection
g_pAccept = new CTCPSocket;
CTCPSocket & Accept = *g_pAccept;
// Set the parent for receiving socket notifications
Accept.SetParent(GetSafeHw nd());
// Accept the remote connection
m_Operator.Accept(Accept);
if (!Accept.InitTP())
return 0;
// Generate the session id
CPacket Packet(theApp.m_iSessionId ++);
Accept << Packet;
// Set a timer to flush this connection after 5 seconds
SetTimer(m_iTimerId,5000,N ULL);
return 1;
}
// Connection telling us that there is pending data to receive
LRESULT CRDPDlg::OnReceiveData(WPA RAM wParam,LPARAM lParam)
{
// Get the connection
CTCPSocket * pAccept = (CTCPSocket *)wParam;
CTCPSocket & Accept = *pAccept;
// Receive a packet of data
CPacket Packet;
Accept >> Packet;
if (Packet.m_ucPacketType == 7 || Packet.m_ucPacketType == 5 || Packet.m_ucPacketType == 10)
{
// Send the packet to all connected clients
// SendToAllConnectedClients( Packet);
//m_packet = CreatePacket(...); // assign packet to member
CWinThread* pthread = AfxBeginThread(&ThreadFunc SendToAllC onnectedCl ients, this);
}
else if (Packet.m_ucPacketType == 9)
{
// Get the record number
USHORT nbBytes = 0,nbRec = 0;
UINT uiLen = 0;
nbBytes = Packet.m_nbBytes;
BYTE * pBuffer = (BYTE *)Packet.m_pBuffer;
CMemFile PacketCache(pBuffer,nbByte s);
PacketCache.Read(&uiLen,si zeof(uiLen ));
PacketCache.Read(&nbRec,si zeof(nbRec ));
if (nbRec == 1) // RecNb 1 - Request Control - Send names of client and server, 4 parameters, UINT, BYTE[], UINT, BYTE[]
{
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csServer. GetBufferS etLength(n Len),nLen) ;
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csViewer. GetBufferS etLength(n Len),nLen) ;
csViewer.ReleaseBuffer();
}
// Locate the servers connection that the client wants to control
bool bFound = false;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (itAccept->m_csAlias.Compa re(csServe r) == 0)
{
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
bFound = true;
break;
}
}
if (!bFound)
{
// Drop the client since the server they wanted dropped in the interim
Accept.ShutDown();
Accept.Close();
}
}
else if (nbRec == 2) // Response of request to be taken control of, the desktop dimensions
{
// Find viewer and forward the dimensions
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csServer. GetBufferS etLength(n Len),nLen) ;
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csViewer. GetBufferS etLength(n Len),nLen) ;
csViewer.ReleaseBuffer();
}
bool bFound = false;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (itAccept->m_bRDV && itAccept->m_csAlias.Compar e(csViewer ) == 0)
{
itAccept->m_Viewers.insert (csServer) ;
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
else if (nbRec == 3) // RecNb 3 - Request screen updates - Pass client and server names - 4 parameters, UINT, BYTE[], UINT, BYTE[]
{
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csServer. GetBufferS etLength(n Len),nLen) ;
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz eof(nLen)) ;
if (nLen)
{
PacketCache.Read(csViewer. GetBufferS etLength(n Len),nLen) ;
csViewer.ReleaseBuffer();
}
bool bFound = false;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (itAccept->m_csAlias.Compa re(csServe r) == 0)
{
itAccept->m_Viewers.insert (csViewer) ;
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
}
else if (Packet.m_ucPacketType == 6)
{
// Create the verification packet
//CPacket Packet2(m_csPassword);
// Test the submitted password hash
//if (Packet.m_csPasswordHash == Packet2.m_csPasswordHash)
//{
// Prevent the connection from being dropped
KillTimer(m_iTimerId);
// Store the connection
sAccept Client;
Client.m_pAccept = pAccept;
Client.m_csAlias = Packet.m_csAlias;
Client.m_bRDV = Packet.m_bRDV;
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (itAccept->m_bRDV)
{
// Don't allow duplicate connections to the same server
if (itAccept->m_csAlias == Packet.m_csAlias)
{
Accept.ShutDown();
Accept.Close();
return 0;
}
}
}
// Don't allow duplicate servers
bool bAccept = true;
for (std::set<sAccept>::iterat or itAccept = m_setAccept.begin();itAcce pt != m_setAccept.end();++itAcce pt)
{
if (!Client.m_bRDV && Client.m_csAlias == itAccept->m_csAlias)
{
Accept.ShutDown();
Accept.Close();
bAccept = false;
}
}
if (bAccept)
{
// The set in this client will be either viewer or server connections depending on type of connection, client amd server respectively)
Client.m_csIp = pAccept->GetIp();
m_setAccept.insert(Client) ;
// Track the connections
TrackConnections(Packet.m_ bRDV,Clien t.m_csIp,C lient.m_cs Alias);
// Test for viewer or server
if (!Packet.m_bRDV)
{
// Add an entry to the servers list of connections
m_ClientList.AddString(Cli ent.m_csAl ias);
UpdateData(FALSE);
}
else
{
// Send back the list of available connections
std::vector<CString> vAliases;
itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (!itAccept->m_bRDV)
vAliases.push_back(itAccep t->m_csAli as);
}
CPacket Packet2(vAliases);
Accept << Packet2;
}
}
//}
//else
//{
// Accept.ShutDown();
// Accept.Close();
//}
}
else if (Packet.m_ucPacketType == 4)
{
// The packet contains the intended destination server
std::set<sAccept>::iterato r itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce pt)
{
if (!itAccept->m_bRDV && itAccept->m_csAlias == Packet.m_csAlias)
{
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
return 1;
}
// Client connection has been closed
LRESULT CRDPDlg::OnCloseConn(WPARA M wParam,LPARAM lParam)
{
// Get the connection
CTCPSocket * pAccept = (CTCPSocket *)wParam;
// Clean up and remove the closed connection
sAccept Accept;
Accept.m_pAccept = pAccept;
std::set<sAccept>::iterato r itAccept = m_setAccept.find(Accept);
if (itAccept != m_setAccept.end())
{
// Delete up the closed connection
BOOL bRDV = itAccept->m_bRDV;
CTCPSocket * pAccept = itAccept->m_pAccept;
CString csAlias = itAccept->m_csAlias;
std::set<CString> Viewers = itAccept->m_Viewers;
delete pAccept;
// Track the connection
TrackConnections(itAccept- >m_bRDV,it Accept->m_ csIp,itAcc ept->m_csA lias,TRUE) ;
// Remove the closed connection
m_setAccept.erase(itAccept );
// Remove the server entry from the list
if (!bRDV)
{
int iPos = m_ClientList.FindString(-1 ,csAlias);
if (iPos != -1)
{
m_ClientList.DeleteString( iPos);
UpdateData(FALSE);
}
// Drop all viewers connected to this dropped server
for (std::set<CString>::iterat or itView = Viewers.begin();itView != Viewers.end();++itView)
{
CString csView = *itView;
for (itAccept = m_setAccept.begin();itAcce pt != m_setAccept.end();++itAcce pt)
{
if (itAccept->m_csAlias == csView)
{
CTCPSocket * pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
Accept.ShutDown();
Accept.Close();
delete pAccept;
// Remove the closed connection
m_setAccept.erase(itAccept );
break;
}
}
}
}
else
{
// Tell connected servers that the client closed
for (itAccept = m_setAccept.begin();itAcce pt != m_setAccept.end();++itAcce pt)
{
// Not a viewer
if (!itAccept->m_bRDV)
{
bool bErase;
do
{
// Look at the viewers of this server
bErase = false;
std::set<CString>::iterato r itAlias;
for (itAlias = itAccept->m_Viewers.begin( );itAlias != itAccept->m_Viewers.end(); )
{
// Are the servers the same
if (csAlias == *itAlias)
{
itAccept->m_Viewers.erase( itAlias);
if (itAccept->m_Viewers.empty ())
{
pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
CPacket Packet2((USHORT)0,csAlias) ;
Accept << Packet2;
}
bErase = true;
break;
}
if (!bErase)
++itAlias;
}
} while (bErase);
}
}
}
}
// If the server pool was not accepting then they now can with an opened slot
SetTimer(m_iTimerId + 1,0,NULL);
return 1;
}
// Update the DIBs with the current screen content
void CRDPDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == m_iTimerId)
{
// Client failed to authenticate
g_pAccept->Close();
// Clean up the closed connection
delete g_pAccept;
g_pAccept = NULL;
// Accept connections again
SetTimer(m_iTimerId + 1,0,NULL);
// Timer nased command events don't persist
KillTimer(nIDEvent);
}
else if (nIDEvent == m_iTimerId + 1)
{
// Test for limiting connections to the server
if (m_setAccept.size() < MAXCONNECTIONS)
{
// Accept connections again
m_Operator.AsyncSelect(FD_ ACCEPT | FD_READ | FD_CLOSE);
}
// Timer based command events don't persist
KillTimer(nIDEvent);
}
}
// Track the connections
void CRDPDlg::TrackConnections( BOOL bRDV,CString csIp,CString csAlias,BOOL bRemove)
{
// Set the sub-key of the main key
CString csSection = _T("Connection");
// Create/Update/Remove the key
if (bRemove)
WritePrivateProfileStruct( csIp,NULL, NULL,0,Afx GetApp()-> m_pszProfi leName);
else
{
if (bRDV)
AfxGetApp()->WriteProfileS tring(csIp ,CString(_ T("Viewer Name")),csAlias);
else
AfxGetApp()->WriteProfileS tring(csIp ,CString(_ T("Server Name")),csAlias);
}
}
#include "stdafx.h"
#include "RDP.h"
#include "RDPDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
extern CRDPApp theApp;
static CTCPSocket * g_pAccept = NULL;
// CRDPDlg dialog
CRDPDlg::CRDPDlg(CWnd* pParent /*=NULL*/)
: CDialog(CRDPDlg::IDD, pParent)
, m_TrayIcon(IDR_MAINFRAME)
,m_iPort(8370)
,m_iTimerId(1)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_
}
void CRDPDlg::DoDataExchange(CD
{
CDialog::DoDataExchange(pD
DDX_Control(pDX, IDC_START, m_Start);
DDX_Control(pDX, IDC_STOP, m_Stop);
DDV_MinMaxInt(pDX, m_iPort, 1025, 65535);
DDX_Control(pDX, IDC_CLIENT_LIST, m_ClientList);
}
BEGIN_MESSAGE_MAP(CRDPDlg,
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_NOTIFY_TRAY,
ON_COMMAND(ID_RDS_RESTORE,
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_START, &CRDPDlg::OnStart)
ON_BN_CLICKED(IDC_STOP, &CRDPDlg::OnStop)
ON_MESSAGE(WM_ACCEPTCONN, &CRDPDlg::OnAcceptConn)
ON_MESSAGE(WM_RECEIVEDATA,
ON_MESSAGE(WM_CLOSECONN, &CRDPDlg::OnCloseConn)
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CRDPDlg message handlers
BOOL CRDPDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Enable the controls
EnableControls();
// Set up tray icon
m_TrayIcon.SetNotification
m_TrayIcon.SetIcon(IDR_MAI
// Get the server information
CString csIp,csPort,csAlias;
BOOL bColors;
LoadSettings(csIp,csPort,c
if (csPort.GetLength())
m_iPort = atoi(csPort);
return TRUE; // return TRUE unless you set the focus to a control
}
// Handle notification from tray icon
LRESULT CRDPDlg::OnNotifyTray(WPAR
{
// Let tray icon do default stuff
return m_TrayIcon.OnNotifyTray(uI
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CRDPDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBK
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON
int cyIcon = GetSystemMetrics(SM_CYICON
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CRDPDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIc
}
// Send the packet to all connected clients
void CRDPDlg::SendToAllConnecte
{
std::vector<CString> vAliases;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
// Locate the server
if (!itAccept->m_bRDV && Packet.m_csServer == itAccept->m_csAlias)
{
// Locate the viewers of this server
std::set<CString>::iterato
for (;itView != itAccept->m_Viewers.end();
{
// Locate each viewers connection and send the update
CString csViewer = *itView;
for (std::set<sAccept>::iterat
{
if (itAccept2->m_bRDV && itAccept2->m_csAlias == csViewer)
{
// This really needs to be in its own thread...
CTCPSocket & Alias = *(itAccept2->m_pAccept);
Alias << Packet;
}
}
}
break;
}
}
}
// Set the state of the UI
void CRDPDlg::EnableControls(BO
{
// Update the controls
m_Start.EnableWindow(bOffl
m_Stop.EnableWindow(!bOffl
m_StaticPort.EnableWindow(
m_Port.EnableWindow(bOffli
m_ClientList.EnableWindow(
}
// Hide the window for close notifications
void CRDPDlg::OnClose()
{
ShowWindow(SW_HIDE);
}
// Show the window for the "restore" menu item
void CRDPDlg::OnRestore()
{
ShowWindow(SW_SHOW);
}
// Start the listener
void CRDPDlg::OnStart()
{
// Update the UI entries
UpdateData();
// Disable the UI
EnableControls(FALSE);
// Create a listener
m_Operator.Create(m_iPort)
m_Operator.SetParent(GetSa
// Hide the dialog
OnClose();
// Update the settings
CString csPort;
csPort.Format("%d",m_iPort
//UpdateSettings(CString("
UpdateSettings(CString("lo
}
// Stop the listener
void CRDPDlg::OnStop()
{
// Remove the inactive connection
std::set<sAccept>::iterato
for (itAccept = m_setAccept.begin();itAcce
{
// Get the connection
CTCPSocket * pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
// Shutdown and close the connection
Accept.ShutDown();
Accept.Close();
// Clean up the connection
delete pAccept;
}
// Remove all the closed connections
m_setAccept.clear();
m_ClientList.ResetContent(
// Shutdown and close the operator
m_Operator.ShutDown();
m_Operator.Close();
// Enable the controls
EnableControls();
}
#if defined(_DEBUG)
#else
#if !defined(X64)
#pragma comment(lib,"..\\pxp1.lib"
#else
#pragma comment(lib,"..\\pxp2.lib"
#endif
#endif
// Operator telling us that there is a pending connection to accept
LRESULT CRDPDlg::OnAcceptConn(WPAR
{
// Temporarily stop accepting connections until this one can be verified
m_Operator.AsyncSelect(FD_
// Accept connections again after 10 seconds
SetTimer(m_iTimerId + 1,10000,NULL);
// Allow a new connection
g_pAccept = new CTCPSocket;
CTCPSocket & Accept = *g_pAccept;
// Set the parent for receiving socket notifications
Accept.SetParent(GetSafeHw
// Accept the remote connection
m_Operator.Accept(Accept);
if (!Accept.InitTP())
return 0;
// Generate the session id
CPacket Packet(theApp.m_iSessionId
Accept << Packet;
// Set a timer to flush this connection after 5 seconds
SetTimer(m_iTimerId,5000,N
return 1;
}
// Connection telling us that there is pending data to receive
LRESULT CRDPDlg::OnReceiveData(WPA
{
// Get the connection
CTCPSocket * pAccept = (CTCPSocket *)wParam;
CTCPSocket & Accept = *pAccept;
// Receive a packet of data
CPacket Packet;
Accept >> Packet;
if (Packet.m_ucPacketType == 7 || Packet.m_ucPacketType == 5 || Packet.m_ucPacketType == 10)
{
// Send the packet to all connected clients
// SendToAllConnectedClients(
//m_packet = CreatePacket(...); // assign packet to member
CWinThread* pthread = AfxBeginThread(&ThreadFunc
}
else if (Packet.m_ucPacketType == 9)
{
// Get the record number
USHORT nbBytes = 0,nbRec = 0;
UINT uiLen = 0;
nbBytes = Packet.m_nbBytes;
BYTE * pBuffer = (BYTE *)Packet.m_pBuffer;
CMemFile PacketCache(pBuffer,nbByte
PacketCache.Read(&uiLen,si
PacketCache.Read(&nbRec,si
if (nbRec == 1) // RecNb 1 - Request Control - Send names of client and server, 4 parameters, UINT, BYTE[], UINT, BYTE[]
{
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csServer.
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csViewer.
csViewer.ReleaseBuffer();
}
// Locate the servers connection that the client wants to control
bool bFound = false;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
if (itAccept->m_csAlias.Compa
{
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
bFound = true;
break;
}
}
if (!bFound)
{
// Drop the client since the server they wanted dropped in the interim
Accept.ShutDown();
Accept.Close();
}
}
else if (nbRec == 2) // Response of request to be taken control of, the desktop dimensions
{
// Find viewer and forward the dimensions
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csServer.
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csViewer.
csViewer.ReleaseBuffer();
}
bool bFound = false;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
if (itAccept->m_bRDV && itAccept->m_csAlias.Compar
{
itAccept->m_Viewers.insert
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
else if (nbRec == 3) // RecNb 3 - Request screen updates - Pass client and server names - 4 parameters, UINT, BYTE[], UINT, BYTE[]
{
CString csServer,csViewer;
USHORT nLen;
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csServer.
csServer.ReleaseBuffer();
}
PacketCache.Read(&nLen,siz
if (nLen)
{
PacketCache.Read(csViewer.
csViewer.ReleaseBuffer();
}
bool bFound = false;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
if (itAccept->m_csAlias.Compa
{
itAccept->m_Viewers.insert
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
}
else if (Packet.m_ucPacketType == 6)
{
// Create the verification packet
//CPacket Packet2(m_csPassword);
// Test the submitted password hash
//if (Packet.m_csPasswordHash == Packet2.m_csPasswordHash)
//{
// Prevent the connection from being dropped
KillTimer(m_iTimerId);
// Store the connection
sAccept Client;
Client.m_pAccept = pAccept;
Client.m_csAlias = Packet.m_csAlias;
Client.m_bRDV = Packet.m_bRDV;
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
if (itAccept->m_bRDV)
{
// Don't allow duplicate connections to the same server
if (itAccept->m_csAlias == Packet.m_csAlias)
{
Accept.ShutDown();
Accept.Close();
return 0;
}
}
}
// Don't allow duplicate servers
bool bAccept = true;
for (std::set<sAccept>::iterat
{
if (!Client.m_bRDV && Client.m_csAlias == itAccept->m_csAlias)
{
Accept.ShutDown();
Accept.Close();
bAccept = false;
}
}
if (bAccept)
{
// The set in this client will be either viewer or server connections depending on type of connection, client amd server respectively)
Client.m_csIp = pAccept->GetIp();
m_setAccept.insert(Client)
// Track the connections
TrackConnections(Packet.m_
// Test for viewer or server
if (!Packet.m_bRDV)
{
// Add an entry to the servers list of connections
m_ClientList.AddString(Cli
UpdateData(FALSE);
}
else
{
// Send back the list of available connections
std::vector<CString> vAliases;
itAccept = m_setAccept.begin();
for (;itAccept != m_setAccept.end();++itAcce
{
if (!itAccept->m_bRDV)
vAliases.push_back(itAccep
}
CPacket Packet2(vAliases);
Accept << Packet2;
}
}
//}
//else
//{
// Accept.ShutDown();
// Accept.Close();
//}
}
else if (Packet.m_ucPacketType == 4)
{
// The packet contains the intended destination server
std::set<sAccept>::iterato
for (;itAccept != m_setAccept.end();++itAcce
{
if (!itAccept->m_bRDV && itAccept->m_csAlias == Packet.m_csAlias)
{
CTCPSocket & Alias = *(itAccept->m_pAccept);
Alias << Packet;
break;
}
}
}
return 1;
}
// Client connection has been closed
LRESULT CRDPDlg::OnCloseConn(WPARA
{
// Get the connection
CTCPSocket * pAccept = (CTCPSocket *)wParam;
// Clean up and remove the closed connection
sAccept Accept;
Accept.m_pAccept = pAccept;
std::set<sAccept>::iterato
if (itAccept != m_setAccept.end())
{
// Delete up the closed connection
BOOL bRDV = itAccept->m_bRDV;
CTCPSocket * pAccept = itAccept->m_pAccept;
CString csAlias = itAccept->m_csAlias;
std::set<CString> Viewers = itAccept->m_Viewers;
delete pAccept;
// Track the connection
TrackConnections(itAccept-
// Remove the closed connection
m_setAccept.erase(itAccept
// Remove the server entry from the list
if (!bRDV)
{
int iPos = m_ClientList.FindString(-1
if (iPos != -1)
{
m_ClientList.DeleteString(
UpdateData(FALSE);
}
// Drop all viewers connected to this dropped server
for (std::set<CString>::iterat
{
CString csView = *itView;
for (itAccept = m_setAccept.begin();itAcce
{
if (itAccept->m_csAlias == csView)
{
CTCPSocket * pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
Accept.ShutDown();
Accept.Close();
delete pAccept;
// Remove the closed connection
m_setAccept.erase(itAccept
break;
}
}
}
}
else
{
// Tell connected servers that the client closed
for (itAccept = m_setAccept.begin();itAcce
{
// Not a viewer
if (!itAccept->m_bRDV)
{
bool bErase;
do
{
// Look at the viewers of this server
bErase = false;
std::set<CString>::iterato
for (itAlias = itAccept->m_Viewers.begin(
{
// Are the servers the same
if (csAlias == *itAlias)
{
itAccept->m_Viewers.erase(
if (itAccept->m_Viewers.empty
{
pAccept = itAccept->m_pAccept;
CTCPSocket & Accept = *pAccept;
CPacket Packet2((USHORT)0,csAlias)
Accept << Packet2;
}
bErase = true;
break;
}
if (!bErase)
++itAlias;
}
} while (bErase);
}
}
}
}
// If the server pool was not accepting then they now can with an opened slot
SetTimer(m_iTimerId + 1,0,NULL);
return 1;
}
// Update the DIBs with the current screen content
void CRDPDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == m_iTimerId)
{
// Client failed to authenticate
g_pAccept->Close();
// Clean up the closed connection
delete g_pAccept;
g_pAccept = NULL;
// Accept connections again
SetTimer(m_iTimerId + 1,0,NULL);
// Timer nased command events don't persist
KillTimer(nIDEvent);
}
else if (nIDEvent == m_iTimerId + 1)
{
// Test for limiting connections to the server
if (m_setAccept.size() < MAXCONNECTIONS)
{
// Accept connections again
m_Operator.AsyncSelect(FD_
}
// Timer based command events don't persist
KillTimer(nIDEvent);
}
}
// Track the connections
void CRDPDlg::TrackConnections(
{
// Set the sub-key of the main key
CString csSection = _T("Connection");
// Create/Update/Remove the key
if (bRemove)
WritePrivateProfileStruct(
else
{
if (bRDV)
AfxGetApp()->WriteProfileS
else
AfxGetApp()->WriteProfileS
}
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks a lot ,But issue i am facing is server exe is getting hanged for 2-3 connections.What could be the reason ?exception i am getting in the below method of TCPSocket class
// Send the buffer of data
int CTCPSocket::Send(const void * lpBuf,int nBufLen,int nFlags)
{
// Test for a function pointer to the TransmitPackets function
if (!TransmitPackets)
return 0;
// Create the transmit buffer data structure
_TRANSMIT_PACKETS_ELEMENT TPE;
TPE.dwElFlags = TP_ELEMENT_MEMORY;
TPE.cLength = nBufLen;
TPE.pBuffer = (PVOID)lpBuf;
// Transmit the packet
BOOL bTransmit = TransmitPackets(m_hSocket, &TPE,1,nBu fLen,NULL, TF_USE_KER NEL_APC);
return nBufLen;
}
Many thanks
// Send the buffer of data
int CTCPSocket::Send(const void * lpBuf,int nBufLen,int nFlags)
{
// Test for a function pointer to the TransmitPackets function
if (!TransmitPackets)
return 0;
// Create the transmit buffer data structure
_TRANSMIT_PACKETS_ELEMENT TPE;
TPE.dwElFlags = TP_ELEMENT_MEMORY;
TPE.cLength = nBufLen;
TPE.pBuffer = (PVOID)lpBuf;
// Transmit the packet
BOOL bTransmit = TransmitPackets(m_hSocket,
return nBufLen;
}
Many thanks
ASKER
Hi Sara,
I need your help to put each connection into one ,one thread with the suggested modification i am getting issues,It would be great help for me if you provide the solution .I dont knw threads also suggest some good articles to understand.
Thanks
I need your help to put each connection into one ,one thread with the suggested modification i am getting issues,It would be great help for me if you provide the solution .I dont knw threads also suggest some good articles to understand.
Thanks
the problem with the mfc socket classes(for me) is that they were trying to do asynchronous socket handling without threads using timers handled in the main message queue. as you have seen mixing of the two concepts makes problems.
it seems not impossible to me to move some of the tasks you now do in the dialog to threads. if you want to proceed on this, I need your latest code and a detailed description of the issues so far.
it is also not impossible to add threading to the mfc classes but probably it would go beyond that what could be done here.
the simplest for me is to show you a solution using plain windows sockets instead of the mfc socket classes. as the mfc classes are in some aspects only wrappers of the plain socket api you will not have many problems to understand the solution. however, it would require to replace some of the parts that already worked, before you went to threading.
Sara
it seems not impossible to me to move some of the tasks you now do in the dialog to threads. if you want to proceed on this, I need your latest code and a detailed description of the issues so far.
it is also not impossible to add threading to the mfc classes but probably it would go beyond that what could be done here.
the simplest for me is to show you a solution using plain windows sockets instead of the mfc socket classes. as the mfc classes are in some aspects only wrappers of the plain socket api you will not have many problems to understand the solution. however, it would require to replace some of the parts that already worked, before you went to threading.
Sara
Open in new window
then instead of calling the function directly you would create a thread in a member function of your dialog like
Open in new window
the thread would run asynchronously to your main thread and would not block the dialog.
however, if the dialog was closed you need to stop the thread before the pthis pointer becomes invalid. you would do that by setting a stop flag member in your dialog and let the SendToAllConnectedClients functions check for that stop flag periodically (for example in the accept loop). because of that you should not use a blocking accept but use select with timeout before calling accept.
note, you can create further threads for each accepted client. the concept could be the same beside that the new threads should be created by the worker thread.
Sara