I'm creating a multi client server (IRC) in C++ using Qt lib. I'd like to know whether it's a good approach to the server architecture.
I'd like to avoid creating thread per connection, so I thought that I could have all client sockets in some kind of container and perform actions (like processing incoming packet) using ThreadPool.
My only concern is whether having sockets connected to the SLOT guarantees parallel client handling.
Code :
CServer::CServer(QObject *parent) : QTcpServer(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
if(!server->listen(QHostAddress::Any, 6667))
qDebug() << "Oh noes";
}
void CServer::newConnection(){
add server->nextPendingConnection() to the container
}
Related
I have a need for a very simple server program which executes one of five different activities, based on client connections.
Since this is for a demo, I don't need any complex network handling, my intention was just to open up five server sockets (say 10001 thru 10005 inclusive) and simply await incoming connections.
Upon the server receiving an incoming connection on (for example) the first socket 10001, it would immediately accept and close the connection, then execute the first action. Ditto for the other sockets. That way, I could demo the actions from another window simply by executing:
telnet 127.0.0.1 10001
Normally, I would use select() with a very short timeout value (i.e., not too onerous on the event processing thread) to await and detect which port was being connected to but, since this is a Qt application, I'm not sure that will work so well with the Qt event model.
What would be the best way of doing this with Qt (5.5, if it matters)? Is the use of a small-timeout select() actually going to work or do I need to go heavyweight with five separate QTcpServer objects, each with their own infrastructure (callbacks and such)?
If I properly understand, you want handle all requests in one place. In Qt you can use signal/slot for it. Connect signals from all QTcpServer objects to one slot, something like:
// Method to fully start a server on specific port.
QTcpServer *MyClass::StartServer(int port) {
QTcpServer *server = new QTcpServer();
server->listen(QHostAddress::Any, port);
connect(server, SIGNAL(newConnection()), this, SLOT(HandleConn()));
return server;
}
// Method to start all servers, serverArr is an array of QTcpServer
// items (in the object, obviously).
void MyClass::StartAllServers() {
for (size_t i = 0; i < sizeof(serverArr) / sizeof(*serverArr); i++)
serverArr[i] = StartServer(10000 + i);
}
// Callback to handle connections.
void MyClass::HandleConn() {
// This will call doAction with parameter based on which QTcpServer
// has caused the callback.
QTcpServer *source = static_cast<QTcpServer*>(sender());
for (size_t i = 0; i < sizeof(serverArr) / sizeof(*serverArr); i++)
if (source == serverArr[i])
doAction(i);
// Action done, so just accept and close connection.
QTcpSocket *socket = source->nextPendingConnection();
socket->close();
}
I have two programs doing IPC using QLocalSocket & QLocalServer, I have managed to implement the connection and send the data successfully.
However, for the QLocalServer side, how can I know a connection is ended by client (like the client program quit.)?
So that I could release the resource of corresponding socket on server side?
You can connect the void QLocalSocket::disconnected() signal from QLocalHost to a slot in your class:
connect(mySocket, &QLocalSocket::disconnected, this, &MyClass::socketDisconnected, Qt::QueuedConnection);
Or you can connect to the stateChange (for more detailed version), something like:
// Connect like this
connect(mySocket, &QLocalSocket::stateChanged, this, &MyClass::socketNewState, Qt::QueuedConnection);
// Implement a slot that handles the various states...
MyClass::socketNewState(QLocalSocket::LocalSocketState socketState)
{
qDebug() << "New state is " << socketState << endl;
}
I have compiled Qt's Trip Planner example that uses QTcpSocket and QTcpServer to create a client and server.
The problem is, the server's incomingConnection() slot is not being called. Even though the client connects to the server successfully. Therefore, the socket's readyRead() signal is never emitted and the client gets no data back.
tripserver.cpp
TripServer::TripServer(QObject *parent)
: QTcpServer(parent)
{
}
void TripServer::incomingConnection(int socketId)
{
qDebug() << "Incoming Connection";
ClientSocket *socket = new ClientSocket(this);
socket->setSocketDescriptor(socketId);
}
If I add a newConnection() slot, it gets called. So what is going on?
Found my answer.
The parameter list has changed since Qt 4.8.1
http://qt-project.org/doc/qt-5.0/qtnetwork/qtcpserver.html#incomingConnection
void TripServer::incomingConnection(qintptr socketId){}
REQUIREMENT
A Client Server Application
Communication will be done by thrift
Server will be running in background or invoked through terminal with no GUI
Client will be Qt based
Current Scenario and problem
Currently, the server uses TNonBlockingServer with certain number of threads(using threadmanager)
Clients connect to the server and does the job.
But, there is a certain requirement where if my server is not running and client tries to connect to it then a message box should be displayed in client's screen.
Currently program just gives a segmentation fault, so i tried using try catch, which didn't work. Upon searching i noticed that Qt doesn't support Exceptions.
Upon some more searching i came across This, but this seems to be using QT(and I still don't know if my problem can be resolved by this)
Server Code :
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<workerHandlerHandler> handler(new workerHandlerHandler());
shared_ptr<TProcessor> processor(new workerHandlerProcessor(handler));
shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(15);
shared_ptr<PosixThreadFactory> threadFactory = shared_ptr<PosixThreadFactory>(new PosixThreadFactory());
threadManager->threadFactory(threadFactory);
threadManager->start();
TNonblockingServer server(processor, protocolFactory, port,threadManager);
server.serve();
Client connects using
boost::shared_ptr<TSocket> socket(new TSocket(serverip.toUtf8().data(), 59999));
boost::shared_ptr<TTransport> transport(new TFramedTransport(socket));
boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
workerHandlerClient client(protocol);
transport->open();
int pingValue = client.ping();
transport->close();
Why not use something like:
QTcpSocket *socket = new QTcpSocket();
socket->connectToHost(serverip, serverport);
if(!socket->waitForConnected(time-in-msecs))
QMessageBox::critical(some-ui-window-as-parent, "Error Title", "Error connecting Text");
If the client is Qt based, you can use QTcpSocket to connect the server :
QTcpSocket clientSocket;
By connecting the error signal of the client socket to a slot, you can display appropriate message box when an error occurs. If the server is not running and the client tries to connect, the slot is called and a message box containing the error is shown :
connect( &clientSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(tcpError(QAbstractSocket::SocketError)) );
clientSocket.connectToHost(ipAddress, portNo );
The slot is as :
void MyClass::tcpError(QAbstractSocket::SocketError error)
{
QMessageBox::warning( this, tr("Error"),tr("TCP error: %1").arg( clientSocket.errorString() ) );
clientSocket.disconnectFromHost();
}
I don't know what you really mean.Qt just support try and catch.
Now, I'm developing a program which is similar with your program.
I also use No-QT server and QT client, but in my code I use the struct like below:
try
{
}
catch(TException e)
{
QMessageBox::information(this, "Warn", "Thrift server has shut down");
}
And it works well
I've modified the threaded fortune-server from Qt examples.
The client connects to the server and then sends a header to authenticate.
tcpSocket = new QTcpSocket();
tcpSocket->connectToHost(addr, port);
QByteArray block = "someheader";
int x = tcpSocket->write(block);
qDebug() << x;
The client seems OK here and qDebug prints the actual size of block.
On the server side I've predefined incomingConnection and I start thread to each new connection.
void Server::incomingConnection(int socketDescriptor) {
const QString &str = vec[qrand() % vec.size()];
SpellThread *thread = new SpellThread(socketDescriptor, str);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
qDebug() << " -- incoming connection";
thread->start();
}
I'm connecting sock to check is there something to read. (sock here is QTcpServer*)
void SpellThread::run() {
qDebug() << " -- in spellthread";
connect(sock, SIGNAL(readyRead()), this, SLOT(checkBytes()));
//....
qDebug() << " -- end spellthread";
}
The first problem is that when I'm sending data from the client, readyRead is not fired. (I've added debug message in checkBytes)
Messages are:
-- incoming connection
-- in spellthread
-- end spellthread
Although the client prints the actual size of header length.
The second problem is that checkBytes currently is very bad-designed. First it checks is header OK and sets a flag, then it gets the size of message and sets another flag and finally it gets the real message. This is very clumsy. I first tried to escape signals and instead use sock->waitForReadyRead(). However it always returns false. (From the docs: "Reimplement this function to provide a blocking API for a custom device. The default implementation does nothing, and returns false.").
So how to really make a client/server application in Qt with multiple clients and multiple reads/writes? I really want suggestions to improve design of my application and to solve my current two problems.
You can't use slots or socket signals with a thread without calling QThread::exec() to start an event loop within that thread/the run function.
Since your checkBytes slot belongs to QThread, it wouldn't be executed by the thread (there is a detailed article about QThreads here)
The closest example that seems to already do what you want is the Network Chat (particularly the two classes Server and Connection).
----------Edit
If you need to use threads (without any slot), the QTcpSocket object must belongs to the same thread as the one where you call waitForReadyRead. For example, with:
SpellThread::SpellThread(int socketDescriptor, const QString & str) {
tcpSocket = new QTcpSocket(); // There should be no parent to be able
// to move it to the thread
tcpSocket->moveToThread(this);
...
Or by creating the QTcpSocket object inside the run function so that it automatically belongs to that thread (it was briefly explained in the fortune example).
If you allocate the QTcpSocket dynamically, and because it won't have a parent, you should also delete it manually.