2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "network-performance-server.h"
22 #include <boost/thread.hpp>
25 #include <base/performance-logging/performance-marker.h>
36 namespace // un-named namespace
38 const unsigned int SERVER_PORT = 3031;
39 const unsigned int MAXIMUM_PORTS_TO_TRY = 10; ///< if port in use, try up to SERVER_PORT + 10
40 const unsigned int CONNECTION_BACKLOG = 2; ///< maximum length of the queue of pending connections.
41 const unsigned int SOCKET_READ_BUFFER_SIZE = 4096;
42 typedef Vector< NetworkPerformanceClient*> ClientList;
45 NetworkPerformanceServer::NetworkPerformanceServer( AdaptorInternalServices& adaptorServices,
46 const EnvironmentOptions& logOptions )
47 : mTriggerEventFactory( adaptorServices.GetTriggerEventFactoryInterface() ),
48 mSocketFactory( adaptorServices.GetSocketFactoryInterface() ),
49 mLogOptions( logOptions ),
50 mServerThread( NULL ),
51 mListeningSocket( NULL ),
54 mLogFunctionInstalled( false )
58 NetworkPerformanceServer::~NetworkPerformanceServer()
62 if( mLogFunctionInstalled )
64 mLogOptions.UnInstallLogFunction();
68 void NetworkPerformanceServer::Start()
70 // start a thread to listen for incoming connection requests
73 if( mListeningSocket )
75 mSocketFactory.DestroySocket( mListeningSocket );
77 mListeningSocket = mSocketFactory.NewSocket( SocketInterface::TCP);
78 mListeningSocket->ReuseAddress( true );
81 unsigned int basePort = 0;
83 // try a small range of ports, so if multiple Dali apps are running you can select
84 // which one to connect to
85 while( !bound && ( basePort < MAXIMUM_PORTS_TO_TRY ))
87 bound = mListeningSocket->Bind( SERVER_PORT + basePort );
95 DALI_LOG_ERROR("Failed to bind to a port \n");
99 mListeningSocket->Listen( CONNECTION_BACKLOG );
101 // start a thread which will block waiting for new connections
102 mServerThread = new boost::thread(boost::bind(&NetworkPerformanceServer::ConnectionListener, this));
104 Dali::Integration::Log::LogMessage(Integration::Log::DebugInfo, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
108 void NetworkPerformanceServer::Stop()
114 // close the server thread to prevent any new connections
115 mListeningSocket->ExitSelect();
117 // wait for the thread to exit.
118 mServerThread->join();
121 mListeningSocket->CloseSocket();
123 delete mServerThread;
124 mSocketFactory.DestroySocket( mListeningSocket );
126 mServerThread = NULL;
127 mListeningSocket = NULL;
129 // this will tell all client threads to quit
132 // wait for all threads to exit and the client count to hit zero
134 boost::mutex::scoped_lock lock(mClientListMutex);
136 while (mClientCount != 0)
138 mClientCountUpdated.wait(lock);
143 bool NetworkPerformanceServer::IsRunning() const
152 void NetworkPerformanceServer::ClientThread( NetworkPerformanceClient* client )
156 SocketInterface& socket( client->GetSocket() );
160 SocketInterface::SelectReturn ret = socket.Select();
162 if( ret == SocketInterface::DATA_AVAILABLE )
165 char buffer[ SOCKET_READ_BUFFER_SIZE ];
166 unsigned int bytesRead;
168 bool ok = socket.Read( buffer, sizeof( buffer ) , bytesRead);
169 if( ok && ( bytesRead > 0) )
171 client->ProcessCommand( buffer, bytesRead );
173 else // if bytesRead == 0, then client closed connection, if ok == false then an error
175 DeleteClient( client );
179 else // ret == QUIT or ERROR
181 DeleteClient( client);
187 void NetworkPerformanceServer::ConnectionListener()
189 // install Dali logging function for this thread
190 if( !mLogFunctionInstalled )
192 mLogOptions.InstallLogFunction();
193 mLogFunctionInstalled = true;
198 // this will block, waiting for a client to connect
199 // or for mListeningSocket->ExitSelect() to be called
201 SocketInterface::SelectReturn ret = mListeningSocket->Select();
203 if( ret == SocketInterface::DATA_AVAILABLE )
205 SocketInterface* clientSocket = mListeningSocket->Accept();
207 // new connection made, spawn a thread to handle it
208 NetworkPerformanceClient* client = AddClient( clientSocket );
209 new boost::thread(boost::bind(&NetworkPerformanceServer::ClientThread, this, client));
211 else // ret == SocketInterface::QUIT or SocketInterface::ERROR
218 NetworkPerformanceClient* NetworkPerformanceServer::AddClient( SocketInterface* clientSocket )
220 // This function is only called from the listening thread
221 NetworkPerformanceClient* client= new NetworkPerformanceClient(clientSocket,
223 mTriggerEventFactory,
227 // protect the mClients list which can be accessed from multiple threads.
228 boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
230 mClients.PushBack( client );
235 void NetworkPerformanceServer::DeleteClient( NetworkPerformanceClient* client )
237 // protect the mClients list while modifying
238 boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
240 // remove from the list, and delete it
241 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
243 if( (*iter) == client )
245 mClients.Erase( iter );
248 // if there server is shutting down, it waits for client count to hit zero
251 // lets the server know the client count has been modified
252 mClientCountUpdated.notify_one();
258 void NetworkPerformanceServer::SendData( const char* const data, unsigned int bufferSizeInBytes,unsigned int clientId )
265 // prevent clients been added / deleted while transmiting data
266 boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
268 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
270 NetworkPerformanceClient* client = (*iter);
271 if( client->GetId() == clientId )
273 client->WriteSocket(data ,bufferSizeInBytes);
279 void NetworkPerformanceServer::TransmitMarker( const PerformanceMarker& marker, const char* const description )
285 // prevent clients been added / deleted while transmiting data
286 boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
288 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
290 NetworkPerformanceClient* client = (*iter);
291 client->TransmitMarker( marker, description );
296 void NetworkPerformanceServer::StopClients()
298 // prevent clients been added / deleted while stopping all clients
299 boost::mutex::scoped_lock sharedDatalock( mClientListMutex );
301 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
303 NetworkPerformanceClient* client = (*iter);
304 // stop the client from waiting for new commands, and exit from it's thread
305 client->ExitSelect();
309 } // namespace Internal
311 } // namespace Adaptor