1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2002 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
41 #include <boost/bind.hpp>
43 #include "OSGConfig.h"
44 #include "OSGThread.h"
45 #include "OSGThreadManager.h"
46 #include "OSGClusterServer.h"
47 #include "OSGPointConnection.h"
48 #include "OSGConnectionFactory.h"
49 #include "OSGDgramSocket.h"
50 #include "OSGClusterWindow.h"
51 #include "OSGBinaryMessage.h"
52 #include "OSGClusterNetwork.h"
53 #include "OSGRemoteAspect.h"
54 #include "OSGTypeFactory.h"
56 #include "OSGImageComposer.h"
59 //#include "OSGMultiDisplayWindow.h"
61 #include "OSGOSGWriter.h"
65 /*! \class OSG::ClusterServer
66 \brief Cluster rendering server
68 A ClusterServer is responsible for syncronizing all client changes.
69 Each cluster renderer can offer it's service by a symbolic name.
70 So it is possible to have a server called "left" or "right".
71 The server uses a local Qt or GLUT window for rendering.
74 GLUTWindowPtr window=GLUTWindow::create();
75 server = new ClusterServer(window,"server1","Multicast");
76 // wait for clients to connect
84 std::vector
<InitFuncF
> *ClusterServer::osgInitFunctions
= NULL
;
85 std::vector
<ExitFuncF
> *ClusterServer::osgExitFunctions
= NULL
;
87 void ClusterServer::addInitFunction(InitFuncF initFunc
)
89 if(osgInitFunctions
== NULL
)
91 osgInitFunctions
= new std::vector
<InitFuncF
>(0);
94 osgInitFunctions
->push_back(initFunc
);
97 void ClusterServer::addExitFunction(ExitFuncF exitFunc
)
99 if(osgExitFunctions
== NULL
)
101 osgExitFunctions
= new std::vector
<ExitFuncF
>(0);
104 osgExitFunctions
->push_back(exitFunc
);
107 bool ClusterServer::init(Int32
,
110 bool returnValue
= true;
112 if(GlobalSystemState
!= Running
)
114 FFATAL(("ClusterServer::init: System not initialized; calls is "
120 if(osgInitFunctions
!= NULL
)
122 for(UInt32 i
= 0; i
< osgInitFunctions
->size(); i
++)
124 returnValue
&= (*osgInitFunctions
)[i
]();
126 if(returnValue
== false)
130 osgInitFunctions
->clear();
136 bool ClusterServer::exit(void)
138 bool returnValue
= true;
140 if(GlobalSystemState
!= Running
)
145 if(osgExitFunctions
!= NULL
)
147 for(PtrDiffT i
= osgExitFunctions
->size() - 1; i
>= 0; i
--)
149 returnValue
&= (*osgExitFunctions
)[i
]();
151 if(returnValue
== false)
156 delete osgExitFunctions
;
161 /*-------------------------------------------------------------------------*/
166 * \param window rendering window. e.g. a Qt or GLUT window
167 * \param serviceName wait for connections that request this name
168 * \param connectionType network type. e.g. "Multicast"
169 * \param address address to wait for connections
170 * \param servicePort port to wait for connections
171 * \param serviceGroup service group
174 ClusterServer::ClusterServer( Window
*window
,
175 const std::string
&serviceName
,
176 const std::string
&connectionType
,
177 const std::string
&address
,
179 const std::string
&serviceGroup
):
182 _requestAddress(address
),
186 _serviceName(serviceName
),
187 _connectionType(connectionType
),
188 _servicePort(servicePort
),
189 _serviceGroup(serviceGroup
),
195 // default is hostname
196 if(_serviceName
.empty())
198 osgGetHostname(localhost
,255);
199 _serviceName
= localhost
;
201 // if service contains ":" than treat as address
202 if(_requestAddress
.empty())
204 if(strstr(_serviceName
.c_str(),":"))
205 _requestAddress
= _serviceName
;
209 /*-------------------------------------------------------------------------*/
212 /*! Destructor. Disconnect from all connected rendering servers
215 ClusterServer::~ClusterServer(void)
230 /*-------------------------------------------------------------------------*/
236 * Start cluster server and wait for a client to connect. This method
237 * will return after a client connection or an error situation.
240 void ClusterServer::start(void)
242 OSG::FieldContainerType
*fct
;
251 _aspect
= new RemoteAspect();
253 // register interrest for all changed cluster windows
255 i
< OSG::TypeFactory::the()->getNumTypes();
258 fct
= OSG::FieldContainerFactory::the()->findType(i
);
260 if(fct
&& fct
->isDerivedFrom(ClusterWindow::getClassType()))
262 _aspect
->registerChanged(
264 boost::bind(&ClusterServer::windowChanged
, this, _1
, _2
));
268 // accept incomming connections
271 UInt8 forceNetworkOrder
;
272 #if BYTE_ORDER == LITTLE_ENDIAN
273 UInt8 littleEndian
= true;
275 UInt8 littleEndian
= false;
281 // determine network order
282 _connection
->putValue(littleEndian
);
283 _connection
->flush();
284 _connection
->selectChannel();
285 _connection
->getValue(forceNetworkOrder
);
286 _connection
->setNetworkOrder((forceNetworkOrder
!= 0));
294 /*! Stop cluster server, remove current remote aspect and all its
298 void ClusterServer::stop()
300 // get aspect ownership
301 if(_clusterWindow
!= NULL
)
303 _aspect
= _clusterWindow
->getNetwork()->getAspect();
305 _clusterWindow
->getNetwork()->setAspect(NULL
);
307 // That's the app one we will never receive as without the
308 // app window it can not be send.
309 _clusterWindow
->subReferenceUnresolved();
310 _clusterWindow
= NULL
;
314 _window
->resolveLinks();
316 // destroy connection
335 #ifdef OSG_OLD_RENDER_ACTION
337 /*! sync with client and render scenegraph
340 void ClusterServer::render(DrawActionBase
*action
)
349 /*! sync with client and render scenegraph
352 void ClusterServer::render(RenderActionBase
*action
)
360 /*! Synchronize all field containers with the client and call
361 * <code>serverInit</code>, <code>serverRender</code> and
362 * <code>serverSwap</code> for the cluster window.
363 * The cluster server uses the first synced ClusterWindow that
364 * contains the name of this server. <code>serverInit</code> is
365 * called after the first ClusterWindow sync.
367 * todo: Sync RenderAciton contents
370 void ClusterServer::doSync(bool applyToChangelist
)
372 // do we have a cluster window?
373 if(_clusterWindow
== NULL
)
378 _aspect
->receiveSync(*_connection
,applyToChangelist
);
380 while(_clusterWindow
== NULL
);
384 (_clusterWindow
->getServers(_serverId
) != _serviceName
) &&
385 (_serverId
< _clusterWindow
->getMFServers()->size());
388 // server connected and cluster window found
389 SINFO
<< "Start server " << _serviceName
390 << " with id " << _serverId
393 // now the window is responsible for connection and aspect
395 _clusterWindow
->getNetwork()->setMainConnection(_connection
);
396 _clusterWindow
->getNetwork()->setAspect (_aspect
);
401 _clusterWindow
->setDrawerId(_serverId
);
402 _clusterWindow
->serverInit(_window
,_serverId
);
405 RemoteAspect
*aspect
= _clusterWindow
->getNetwork()->getAspect();
406 Connection
*connection
=
407 _clusterWindow
->getNetwork()->getMainConnection();
409 // sync with render clinet
410 aspect
->receiveSync(*connection
, applyToChangelist
);
412 // sync with render client
413 if(_clusterWindow
->getInterleave())
415 // if the reminder of the division of interleave and
416 // framecount is equal to the servers id, the right
417 // sync point for the current render frame is reached
418 while( ( _clusterWindow
->getFrameCount() %
419 _clusterWindow
->getInterleave() ) !=
420 (_serverId
%_clusterWindow
->getInterleave()) )
422 aspect
->receiveSync(*connection
, applyToChangelist
);
426 if(applyToChangelist
)
432 commitChangesAndClear();
436 /*! render server window
439 void ClusterServer::doRender(RenderActionBase
*action
)
442 OSG::IndentFileOutStream
outFileStream("/tmp/cluster.osg");
446 //std::cerr << "STARTING PRINTOUT:" << std::endl;
448 OSG::OSGWriter
writer(outFileStream
, 4);
450 writer
.write(_clusterWindow
);
452 outFileStream
.close();
456 _clusterWindow
->serverRender(_window
, _serverId
, action
);
459 /*! swap server window
462 void ClusterServer::doSwap(void)
464 _clusterWindow
->serverSwap(_window
, _serverId
);
467 /*! return the cluster window received from the client
470 Window
*ClusterServer::getClusterWindow(void)
472 return _clusterWindow
;
475 /*! return the window used for rendering
478 Window
*ClusterServer::getServerWindow(void)
483 /*! clusterWindow changed callback. This is a callback functor.
484 It is called for each change of a ClusterWindow.
487 bool ClusterServer::windowChanged(FieldContainer
* const fcp
,
490 if(_clusterWindow
!= NULL
)
493 ClusterWindow
*window
= dynamic_cast<ClusterWindow
*>(fcp
);
495 if(window
->getMFServers()->size())
497 MFString::const_iterator sIt
=
498 window
->getMFServers()->find(_serviceName
);
500 if(sIt
== window
->getMFServers()->end())
502 SWARNING
<< "wrong window" << std::endl
;
506 _clusterWindow
= window
;
510 fprintf(stderr
, "%p %" PRISize
" %td\n",
511 static_cast<void *>(&(*_window
)),
512 window
->getMFServers()->size(),
513 sIt
- window
->getMFServers()->begin());
515 const MFUInt32
&vIds
= *(window
->getMFServerIds());
517 if(window
->getMFServers()->size() <= vIds
.size())
519 _window
->setDrawerId(vIds
[sIt
-
520 window
->getMFServers()->begin()]);
524 _window
->setDrawerId(sIt
- window
->getMFServers()->begin());
533 /*! Wait for incomming clients. A client can send a request for a
534 * special connection type or it can try to connect it it knows
535 * the servers address.
538 void ClusterServer::acceptClient(void)
541 DgramSocket serviceSock
;
544 std::string connectionType
;
546 bool connected
=false;
550 SINFO
<< "Waiting for request of "
556 if(!_requestAddress
.empty())
560 _connection
= ConnectionFactory::the()->createPoint(
566 _connection
->setInterface(_interface
);
570 // bind to requested address
571 _boundAddress
= _connection
->bind(_requestAddress
);
576 SINFO
<< "Unable to bind, use name as symbolic "
584 serviceSock
.setReusePort(true);
586 // join to multicast group
587 if(!_serviceGroup
.empty())
589 SocketAddress groupAddress
=
590 SocketAddress(_serviceGroup
.c_str(),
593 if(groupAddress
.isMulticast())
595 SINFO
<< "wait for request on multicast:"
596 << _serviceGroup
<< std::endl
;
598 serviceSock
.bind(SocketAddress(SocketAddress::ANY
,
600 serviceSock
.join(SocketAddress(groupAddress
));
604 SINFO
<< "wait for request by broadcast:"
605 << _serviceGroup
<< std::endl
;
606 serviceSock
.bind(SocketAddress(groupAddress
));
611 SINFO
<< "wait for request by broadcast" << std::endl
;
612 serviceSock
.bind(SocketAddress(SocketAddress::ANY
,
621 readable
= serviceSock
.waitReadable(.01);
627 serviceSock
.recvFrom(msg
,addr
);
629 service
= msg
.getString();
630 connectionType
= msg
.getString();
632 SINFO
<< "Request for "
637 if(service
== _serviceName
)
639 // remove old connection if typename missmaches
641 _connection
->getType()->getName() != connectionType
)
647 // try to create connection
652 ConnectionFactory::the()->createPoint(
658 _connection
->setInterface(_interface
);
660 _boundAddress
= _connection
->bind(
667 SINFO
<< "Unknown connection type '"
668 << connectionType
<< "'" << std::endl
;
675 msg
.putString(_serviceName
);
676 msg
.putString(_boundAddress
);
677 serviceSock
.sendTo(msg
, addr
);
680 << connectionType
<< ":"
688 catch(SocketConnReset
&e
)
690 // ignore if there is a connection. This can happen, if
691 // a client has send a request. The server has send an
692 // answer meanwile the client has send a second request
693 // the client gets the answer to the first request and
694 // the server tries to send a second answer. The second
695 // answer can not be delivered because the client has
696 // closed its service port. This is a win-socket problem.
698 SWARNING
<< e
.what() << std::endl
;
700 // if there is no connection, then its a real problem
704 catch(OSG_STDEXCEPTION_NAMESPACE::exception
&e
)
706 SWARNING
<< e
.what() << std::endl
;
711 if(bound
&& _connection
&& _connection
->acceptGroup(0.2) >= 0)
714 SINFO
<< "Connection accepted "
720 catch(OSG_STDEXCEPTION_NAMESPACE::exception
&e
)
722 SWARNING
<< e
.what() << std::endl
;
729 catch(OSG_STDEXCEPTION_NAMESPACE::exception
&)