I'm looking for an easy way to establish an ssh connection in Qt similar to how I can operate in Java.
For instance, to log in to an ssh connection via java, I can do:
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
......
SshClient ssh = new SshClient();
try {
// Timeout of ten seconds
ssh.setSocketTimeout(10*1000);
ssh.connect(ip, port, new IgnoreHostKeyVerification());
PasswordAuthenticationClient auth = new PasswordAuthenticationClient();
auth.setUsername(username);
auth.setPassword(password);
if (ssh.authenticate(auth) != AuthenticationProtocolState.COMPLETE) {
errorLabel.setForeground(Color.RED);
errorLabel.setText("Username or password is incorrect");
}
else
successful = true;
}
catch (Exception e) {
errorLabel.setForeground(Color.RED);
errorLabel.setText("Cannot log into website");
e.printStackTrace();
}
The solutions I've seen for Qt are:
Roll my own using libssh
Roll my own using libssh2
Use QProcess to call an outside SSH process to establish a connection
Buy the library here: http://netsieben.com/products/ssh/
Since the Java version is free, I'm a bit loathe to spend money on this, but if I have to, I have to. I'd rather not roll my own, because I can only see that being a cause of mistakes (unless those libraries are easy to use?).
Any thing I haven't found?
EDIT: I have now spent days wrestling with both libssh and libqxt. Both are utter failures on windows, from what I can determine. Libqxt won't compile on windows with ssh, even after following the steps described here and here. Libssh's C++ wrapper class includes everything in the header, which is causing linking failures if not carefully shepherded. Once those linker issues are solved, Libssh's compiled library crashes CDB, so debugging with libssh becomes impossible, regardless of whether or not the c++ wrapper is used. So now I'm back to libssh2 or paying for it via some dubious program that has apparently not been updated in four years. Cryptlib, maybe? It doesn't help that most of the qt forum help seems to be 'spawn another process', which therefore requires my users to have ssh installed, which is definitely not par for the course on Windows.
Here is an asynchronous ssh & scp "socket" I wrote for Qt that is cross platform. This requires libSSH and is not perfect (hides rsa key passing stuff, delivers command responses in single shot instead of via readyRead signals) , but it will most likely work for your needs. In order to get this working for windows this file must be the FIRST file included in your project (so that qt doesn't try and import windows.h before this can import the proper windows socket code).
Header:
#ifndef QSSHSOCKET_H
#define QSSHSOCKET_H
#include <libssh/libssh.h>
#include <QByteArray>
#include <QThread>
#include <QVector>
#include <QFile>
#include <sys/stat.h>
#include <sys/types.h>
#include <QDebug>
class QSshSocket: public QThread
{
Q_OBJECT
public:
enum SshError
{
/*! \brief There was trouble creating a socket. This was most likely due to the lack of an internet connection.*/
SocketError,
/*! \brief The ssh session could not be created due to inability to find the remote host.*/
SessionCreationError,
/*! \brief An ssh channel could not be created for the previous operation.*/
ChannelCreationError,
/*! \brief An scp channel could not be created for the previous file transfer operation.*/
ScpChannelCreationError,
/*! \brief There was an error requesting a pull file transfer.*/
ScpPullRequestError,
/*! \brief There was an error requesting a push file transfer.*/
ScpPushRequestError,
/*! \brief The destination file for the previous transfer does not exist.*/
ScpFileNotCreatedError,
/*! \brief There was an error reading a remote file. This could possibly be due to user permissions.*/
ScpReadError,
/*! \brief There was an error writing to a remote file. This could possibly be due to user permissions.*/
ScpWriteError,
/*! \brief The credentials of a user on the remote host could not be authenticated.*/
PasswordAuthenticationFailedError
};
/*!
\param position The center position of the box.
\param size The size of the box.
\brief The constructor.
*/
explicit QSshSocket(QObject * parent = 0);
/*!
\brief The deconstructor.
*/
~QSshSocket();
/*!
\param host The hostname to establish an ssh connection with.
\param port The port to establish an ssh connection over.
\brief This function connects this socket to the specified host over the specified port. On success, the signal connected is emitted while error is emmited on failure.
*/
void connectToHost(QString host, int port =22);
/*!
\brief This function disconnects the socket from the current host (if there is one. On success, the signal disconnected is emitted while error is emmited on failure.
*/
void disconnectFromHost();
/*!
\param command The command to be executed.
\brief This function executes a remote command on the connected host. If not connected to a remote host, the command is not executed.
On success, the signal commandExecuted is emitted while error is emmited on failure.
*/
void executeCommand(QString command);
/*!
\brief Returns the hostname of the remote machine this socket is connected to. If not connected to a remote host, this returns "".
*/
QString host();
/*!
\brief Returns whether or not a user has been logged in at remote host.
*/
bool isLoggedIn();
/*!
\brief Returns whether or not this socket is currently connected to a remote host.
*/
bool isConnected();
/*!
\param user The username to login with.
\param password The password of the account for the specified username.
\brief This function to login to the currently connected host given credentials.
On success, the signal authenticated is emitted while error is emmited on failure.
*/
void login(QString user, QString password);
/*!
\brief Returns the port of the current connection. If not connected to a remote host, this returns -1.
*/
int port();
/*!
\param localPath A path to a file stored on the local machine.
\param password A path to a file stored on the remote machine.
\brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
On success, the signal pullSuccessful is emitted while error is emmited on failure.
If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
*/
void pullFile(QString localPath, QString remotePath);
/*!
\param localPath A path to a file stored on the local machine.
\param password A path to a file stored on the remote machine.
\brief This function attempts to pull a remote file from the connected host to a local file. The local file does not need to be created beforehand.
On success, the signal pushSuccessful is emitted while error is emmited on failure.
If not connected to a remote host, or if the transfer was unsuccessful, the signal error is emitted.
*/
void pushFile(QString localPath, QString remotePath);
/*!
\param path A relative or absolute path to a directory on the remote host.
\brief This function attempts to set the working directory of the connection to path and emits workingDirectorySet upon completion.
If workingDirectorySet indicates no change in the working directory, the path could not be found.
If not connected to a remote host the signal error will be emitted.
*/
void setWorkingDirectory(QString path);
/*!
\brief Returns the username of the current authenticated user on the remote host. If not connected to a remote host, or if a user has not been authenticated this returns "".
*/
QString user();
signals:
/*!
\brief This signal is emitted when remote host has been connected to."
*/
void connected();
/*!
\brief This signal is emitted when this class has been properly disconnected from a remote host.
*/
void disconnected();
/*!
\param error The type of error that occured.
\brief This signal is emitted when an error occurs.
*/
void error(QSshSocket::SshError error);
/*!
\param command The command that was executed on the remote host.
\param response The response to the command that was executed.
\brief This signal is emitted when a response from the remote host is received regarding a command.
*/
void commandExecuted(QString command,QString response);
/*!
\brief This signal is emitted when a user has been loggen in to the remote host."
*/
void loginSuccessful();
/*!
\param localFile The path to a local file that the remote file was pulled to.
\param remoteFile The path to a file pulled from the remote host.
\brief This signal is emitted when a remote file is successfully transfered to a local file.
*/
void pullSuccessful(QString localFile, QString remoteFile);
/*!
\param localFile The path to a local file pushed to the remote host.
\param remoteFile The path to a remote file that the local file was pushed to.
\brief This signal is emitted when a local file is successfully transfered to a remote file.
*/
void pushSuccessful(QString localFile, QString remoteFile);
/*!
\param cwd The current working directory of the session on the remote host.
\brief This signal is emitted when a current working directory is set.
*/
void workingDirectorySet(QString cwd);
private slots:
void run();
private:
enum SSHOperationType
{
Command,
WorkingDirectoryTest,
Pull,
Push
};
struct SSHOperation
{
SSHOperationType type;
QString adminCommand,command, localPath, remotePath;
bool executed;
};
int m_port;
bool m_loggedIn ;
QThread * m_thread;
QString m_workingDirectory,m_nextWorkingDir,m_user, m_host,m_password;
SSHOperation m_currentOperation;
ssh_session m_session;
bool m_connected,m_run;
};
#endif // QSSHSOCKET_H
Source:
#include "qsshsocket.h"
#include <QFileInfo>
// if compiling in windows, add needed flags.
#ifdef _WIN32
# include <io.h>
typedef int mode_t;
/// #Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any
/// of User, Group, or Other will set Read for User and setting Write
/// will set Write for User. Otherwise, Read and Write for Group and
/// Other are ignored.
///
/// #Note For the POSIX modes that do not have a Windows equivalent, the modes
/// defined here use the POSIX values left shifted 16 bits.
static const mode_t S_ISUID = 0x08000000; ///< does nothing
static const mode_t S_ISGID = 0x04000000; ///< does nothing
static const mode_t S_ISVTX = 0x02000000; ///< does nothing
static const mode_t S_IRUSR = mode_t(_S_IREAD); ///< read by user
static const mode_t S_IWUSR = mode_t(_S_IWRITE); ///< write by user
static const mode_t S_IXUSR = 0x00400000; ///< does nothing
# ifndef STRICT_UGO_PERMISSIONS
static const mode_t S_IRGRP = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWGRP = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = mode_t(_S_IREAD); ///< read by *USER*
static const mode_t S_IWOTH = mode_t(_S_IWRITE); ///< write by *USER*
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# else
static const mode_t S_IRGRP = 0x00200000; ///< does nothing
static const mode_t S_IWGRP = 0x00100000; ///< does nothing
static const mode_t S_IXGRP = 0x00080000; ///< does nothing
static const mode_t S_IROTH = 0x00040000; ///< does nothing
static const mode_t S_IWOTH = 0x00020000; ///< does nothing
static const mode_t S_IXOTH = 0x00010000; ///< does nothing
# endif
static const mode_t MS_MODE_MASK = 0x0000ffff; ///< low word
#endif
QSshSocket::QSshSocket(QObject * parent )
:QThread(parent)
{
m_host = "";
m_user = "";
m_password = "";
m_port = -1;
m_loggedIn = false;
m_session = NULL;
m_workingDirectory = ".";
qRegisterMetaType<QSshSocket::SshError>("QSshSocket::SshError");
m_currentOperation.executed = true;
m_run = true;
start();
}
QSshSocket::~QSshSocket()
{
m_run = false;
this->wait();
}
void QSshSocket::run()
{
while(m_run)
{
if (m_session == NULL)
{
if (m_host != "")
{
m_session = ssh_new();
//set logging to verbose so all errors can be debugged if crash happens
int verbosity = SSH_LOG_PROTOCOL;
// set the pertinant ssh session options
ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.toAscii().data());
ssh_options_set(m_session, SSH_OPTIONS_USER, m_user.toAscii().data());
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);
// try to connect given host, user, port
int connectionResponse = ssh_connect(m_session);
// if connection is Successful keep track of connection info.
if (connectionResponse == SSH_OK)
connected();
else
error(SessionCreationError);
}
}
// if we have a vaild ssh connection, authenticate connection with credentials
else if (!m_loggedIn)
{
// check to see if a username and a password have been given
if (m_user != "" && m_password !="")
{
// try authenticating current user at remote host
int worked = ssh_userauth_password(m_session, m_user.toAscii().data(), m_password.toAscii().data());
// if successful, store user password.
if (worked == SSH_OK)
{
loginSuccessful();
m_loggedIn = true;
}
else
{
m_user = "";
m_password = "";
error(PasswordAuthenticationFailedError);
}
}
}
// if all ssh setup has been completed, check to see if we have any commands to execute
else if (!m_currentOperation.executed)
{
if (m_currentOperation.type == Command || m_currentOperation.type == WorkingDirectoryTest)
{
// attempt to open ssh shell channel
ssh_channel channel = ssh_channel_new(m_session);
// if attempt fails,return
if (ssh_channel_open_session(channel) != SSH_OK)
{
error(ChannelCreationError);
}
int requestResponse = SSH_AGAIN;
// attempt to execute shell command
while (requestResponse == SSH_AGAIN)
requestResponse = ssh_channel_request_exec(channel, m_currentOperation.adminCommand.toAscii().data());
// if attempt not executed, close connection then return
if (requestResponse != SSH_OK)
{
error(ChannelCreationError);
}
QByteArray buffer;
buffer.resize(1000);
// read in command result
int totalBytes = 0, newBytes = 0;
do
{
newBytes = ssh_channel_read(channel, &(buffer.data()[totalBytes]), buffer.size() - totalBytes, 0);
if (newBytes > 0)
totalBytes += newBytes;
}while (newBytes > 0);
// close channel
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
QString response = QString(buffer).mid(0,totalBytes);
response.replace("\n","");
if (m_currentOperation.type == WorkingDirectoryTest)
{
if (response == "exists")
m_workingDirectory = m_nextWorkingDir;
m_nextWorkingDir = ".";
workingDirectorySet(m_workingDirectory);
}
else
commandExecuted( m_currentOperation.command, response) ;
}
// if all ssh setup has been completed, check to see if we have any file transfers to execute
else if (m_currentOperation.type == Pull)
{
ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_READ, m_currentOperation.remotePath.toAscii().data());
if (scpSession == NULL)
error(ScpChannelCreationError);
// attempt to initialize new scp session.
int scpInitialized = ssh_scp_init(scpSession);
if(scpInitialized != SSH_OK)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpChannelCreationError);
}
// attempt to authorize new scp pull
if (ssh_scp_pull_request(scpSession) != SSH_SCP_REQUEST_NEWFILE)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpPullRequestError);
}
// accept authorization
ssh_scp_accept_request(scpSession);
// get remote file size
int size = ssh_scp_request_get_size(scpSession);
// resize buffer, read remote file into buffer
QByteArray buffer;
buffer.resize(size);
// if an error happens while reading, close the scp session and return
if (ssh_scp_read(scpSession, buffer.data() , size) == SSH_ERROR)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpReadError);
}
// loop until eof flag
if (ssh_scp_pull_request(scpSession) != SSH_SCP_REQUEST_EOF)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpReadError);
}
//close scp session
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
// open up local file and write contents of buffer to it.
QFile file(m_currentOperation.localPath);
file.open(QIODevice::WriteOnly);
file.write(buffer);
file.close();
pullSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);
}
else if (m_currentOperation.type == Push)
{
// attempt to create new scp from ssh session.
ssh_scp scpSession = ssh_scp_new(m_session,SSH_SCP_WRITE, m_currentOperation.remotePath.toAscii().data());
// if creation failed, return
if (scpSession == NULL)
error(SocketError);
// attempt to initialize new scp session.
int scpInitialized = ssh_scp_init(scpSession);
// if failed, close scp session and return.
if(scpInitialized != SSH_OK)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpChannelCreationError);
}
// open the local file and check to make sure it exists
// if not, close scp session and return.
QFile file(m_currentOperation.localPath);
if (!file.exists())
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpFileNotCreatedError);
}
// if the file does exist, read all contents as bytes
file.open(QIODevice::ReadOnly);
QByteArray buffer =file.readAll();
file.close();
// attempt to authorize pushing bytes over scp socket
// if this fails, close scp session and return.
if (ssh_scp_push_file(scpSession, m_currentOperation.remotePath.toAscii().data(), buffer.size(), S_IRUSR | S_IWUSR) != SSH_OK)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpPushRequestError);
}
// once authorized to push bytes over scp socket, start writing
// if an error is returned, close scp session and return.
if ( ssh_scp_write(scpSession,buffer.data(), buffer.size()) != SSH_OK)
{
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
error(ScpWriteError);
}
// close scp session and return.
ssh_scp_close(scpSession);
ssh_scp_free(scpSession);
pushSuccessful(m_currentOperation.localPath,m_currentOperation.remotePath);
}
m_currentOperation.executed = true;
}
else
{
msleep(100);
}
}
}
void QSshSocket::disconnectFromHost()
{
m_host = "";
m_user = "";
m_password = "";
m_port = -1;
m_loggedIn = false;
if (m_session != NULL)
{
ssh_disconnect(m_session);
ssh_free(m_session);
}
m_session = NULL;
}
void QSshSocket::connectToHost(QString host, int port)
{
m_host = host;
m_port = port;
}
void QSshSocket::login(QString user, QString password)
{
m_user = user;
m_password = password;
}
void QSshSocket::executeCommand(QString command)
{
m_currentOperation.type = Command;
if (m_workingDirectory != ".")
m_currentOperation.adminCommand = "cd " + m_workingDirectory + "; " + command;
else
m_currentOperation.adminCommand = command ;
m_currentOperation.command =command;
m_currentOperation.executed = false;
}
void QSshSocket::pullFile(QString localPath, QString remotePath)
{
m_currentOperation.localPath = localPath;
if (QFileInfo(remotePath).isAbsolute())
m_currentOperation.remotePath = remotePath;
else
m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
m_currentOperation.type = Pull;
m_currentOperation.executed = false;
}
void QSshSocket::pushFile(QString localPath, QString remotePath)
{
m_currentOperation.localPath = localPath;
if (QFileInfo(remotePath).isAbsolute())
m_currentOperation.remotePath = remotePath;
else
m_currentOperation.remotePath = m_workingDirectory + "/" + remotePath;
m_currentOperation.type = Push;
m_currentOperation.executed = false;
}
void QSshSocket::setWorkingDirectory(QString path)
{
m_nextWorkingDir = path;
m_currentOperation.type = WorkingDirectoryTest;
m_currentOperation.adminCommand = "[ -d " + m_nextWorkingDir +" ] && echo 'exists'";
m_currentOperation.executed = false;
}
bool QSshSocket::isConnected()
{
return m_session != NULL;
}
bool QSshSocket::isLoggedIn()
{
return m_loggedIn;
}
QString QSshSocket::user(){return m_user;}
QString QSshSocket::host(){return m_host;}
int QSshSocket::port(){return m_port;}
I searched for some sort of solution to this for a couple days and then forgot about the problem. Then today I stumbled across this little gem in the Qt-Creator source Utils::ssh, includes support for SFTP, plain-old SSH, and all sorts of goodies.
Disentangling stuff from Qt-Creator can be a pain, but having gone through this process it amounts to grabbing Botan (one of the other libs in QT-Creator) + Utils
See LibQxt. Look for QxtSsh classes. hg repo using libssh2 internally.
I'm using libssh2 http://www.libssh2.org/ with qt. I'm using it in the easiest way - native sockets. I had no time to convert it to use qt sockets. However it works great.
The project description is:
http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html
and the source code is in:
https://bitbucket.org/nchokoev/qtsshremote
Related
I'm moving my application from Qt 4.7 to Qt 6.3. In Qt 4.7 all works fine. In Qt 6.3 I have some issues when tcp server closes connection, I establish again connection, and I try to write data.
This is the function I use to write to socket:
void NetworkFacility::Write(QTcpSocket& oTCPSocket, const QVariant& oV)
{
//Controls Socket is valid
if (oTCPSocket.state() == QAbstractSocket::ConnectedState)
{
QByteArray block; //ByteArray to serialiaze object
MyPacket oPacket; //Packet to send
//Set Size of QVariant object
oPacket.setPacketSize(getQVariantSize(oV));
//Set QVariant
oPacket.setTransport(oV);
//Create stream to write on ByteArray
QDataStream out(&block, QIODevice::WriteOnly);
//Sets version
out.setVersion(QDataStream::Qt_4_7);
//Serializes
out << oPacket;
//TX to socket
oTCPSocket.write(block);
}
}
I manage disconnection this way:
void MyClient::remoteNodeDisconnected()
{
m_pTCPSocket->flush();
m_pTCPSocket->close();
}
void MyClient::ManagesTCPError(QAbstractSocket::SocketError socketError)
{
//Clears last packets
m_pTCPSocket->flush();
}
This is connection portion of code after disconnection:
m_pTCPSocket->connectToHost(m_sIpAddress, m_iIpPort);
//Waits connection
if (m_pTCPSocket->waitForConnected(MSEC_WAIT_FOR_CONNECTION))
{
//Print connected and exit from while loop
break;
}
Finally this is the way in which I manage the remote server connecte:
void MyClient::remoteNodeConnected()
{
//Improve Network latency on this connection
m_pTCPSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
}
The issue is that on the first connection all works fine. If the server disconnects (i.e. I umplugg the server cable in my LAN or I shutdown and restarts the server application) and then connects again the call to:
oTCPSocket.write(block);
in Networkfacility::Write method generates a crash.
Why the write method generates a crash after reconnection?
I fixed the issue by removing completely th oTCPSocket instance.
void MyClient::remoteNodeDisconnected()
{
m_pTCPSocket->flush();
m_pTCPSocket->close();
m_pTCPSocket.deleteLater();
m_pTCPSocket = 0;
}
I'm trying to enable disk cache for my app for a better user experience. I used this Documentation as the guideline for this. I initially thought that I configured it properly since I didn't get any errors but then saw my disk cache folder is empty. Then I have searched everywhere and tried a lot of things but couldn't fix this issue. The following are the relevant code snippets,
CWMWebPageCheck.h
class CWMWebPageCheck : public QObject
{
Q_OBJECT
public:
/// \brief Constructor for a CWMWebPageCheck object
///
/// \param oUrl
CWMWebPageCheck(QUrl oUrl);
virtual ~CWMWebPageCheck();
void initialize();
void initiateChecksumCalculation();
void resetHash();
...
protected:
///
/// \brief Reads the hash out of database.
///
void hashRead();
///
/// \brief Writes the hash into database.
///
void hashWrite();
bool m_bInitialized; ///< state if the CWMWebPageCheck has been initialized
QUrl m_oUrl; ///< the url which has to be scanned
QStringList m_oRequests; ///< list of requests
QNetworkAccessManager *m_oNetworkManager; ///< networkmanager to start network requests
//added by Isuru for implementing the diskcache
QNetworkDiskCache *m_oDiskCache;
CHashReplyManager m_oReplyManager; ///< reply manager which handles the network replies
QByteArray m_oHash; ///< the current hash
void initiateDataTransfer();
};
#endif // CWMWEBPAGECHECK_
CWMWebPageCheck.cpp
...
CWMWebPageCheck::CWMWebPageCheck(QUrl oUrl)
{
m_bInitialized = false;
m_oUrl = oUrl;
qDebug() << "Starting WebPageCheck with url: " << oUrl;
m_oRequests << "scripts/scripts_WM.js";
m_oRequests << "screens_wm/logbook_wm.html";
m_oNetworkManager = new QNetworkAccessManager(this);
m_oDiskCache = new QNetworkDiskCache(this);
//m_oDiskCache->setMaximumCacheSize(1000000); //set the cache to 10megs
//QString location = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); //QStandardPath is not included in the Qt4.8
m_oDiskCache->setCacheDirectory(QCoreApplication::applicationDirPath() + "/Disk_cache");
//m_oDiskCache->setCacheDirectory(location);
m_oNetworkManager->setCache(m_oDiskCache);
//m_oDiskCache->maximumCacheSize(); //Returns the current maximum size in bytes for the disk cache
connect(m_oNetworkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onReplyFinished(QNetworkReply*)));
connect(&m_oReplyManager, SIGNAL(sigCalculationError(Te_SealRequest)), this, SLOT(onSigCalculationError(Te_SealRequest)));
connect(&m_oReplyManager, SIGNAL(sigCalculationFinished(Te_SealRequest,QByteArray)), this, SLOT(onSigCalculationFinished(Te_SealRequest,QByteArray)));
m_oReplyManager.setCount(m_oRequests.length());
}
//Now comes the part where I think the problem comes from
void CWMWebPageCheck::initiateDataTransfer()
{
qDebug("*******inside initiateDataTransfer()");
for (QString oPath: m_oRequests)
{
QString sUrl = m_oUrl.toString();
// do a normal request (preferred from network, as this is the default)
// QNetworkRequest request(QUrl(QString("%1/%2").arg(sUrl).arg(oPath)));
// m_oNetworkManager->get(request);
// do a request preferred from cache
QNetworkRequest request2(QUrl(QString("%1/%2").arg(sUrl).arg(oPath)));
request2.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
m_oNetworkManager->get(request2);
/* QString sUrl = m_oUrl.toString();
// m_oNetworkManager->get(QNetworkRequest(QString("%1/%2").arg(sUrl).arg(oPath)).setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache));
//#################################################################################################
/* QNetworkRequest request(QUrl(QString("%1/%2").arg(sUrl).arg(oPath)));
*
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
m_oNetworkManager->get(request);*/
/*QNetworkRequest request = QNetworkRequest();
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
request.setUrl(QUrl(QString("%1/%2").arg(sUrl).arg(oPath)));
m_oNetworkManager->get(request);*/
// qDebug("***********====== GOT REQUET=======***************");
// do a request preferred from cache
/* QNetworkRequest request2(QUrl(QString("%1/%2").arg(sUrl).arg(oPath)));
request2.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
m_oNetworkManager->get(request2);
qDebug("***********====== GOT REQUET=======***************");*/
//#################################################################################################
}
What am I doing wrong here? Thanks in advance!
My gui have a two button's, one to start a server & other to stop a server.
Brief :---
Once server is started, on every new client request i will create a new thread & this will handle communication with the client.
Detail :--
When start button is pressed, I am creating an object 'tcpserverobjectWrapper' which creates an another object 'tcpserverobject' this object creats an Qtcpserver.
Now this Qtcpserver is listing for new connection. And when a new connection comes i create an 'TcpSocketThreadWrapperObject' object which
creates a thread & this thread handles communication with client . Also 'tcpserverobject' keeps the list of new client request objects created
'QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;' .
I am able to connect to server from telnet clients & it creates new thread for each client & works fine.
When stop button pressed i am able to stop server & client threads.
But i have two problems here :---
1> Everytime client send some data to server. I get this kind of QsocketNotifier. What is this ?
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
2> If i press stop button on GUI i am able to stop the threads succesfully.
But how to stop the threads & delete the objects created for every client when client send 'STOP command' to server or closes the connection with server ?
I will also have to delete the following objects created on each client request ?
client request --> TcpSocketThreadWrapperObject -- creates --> TcpSocketThreadObject -- creates --> TcpSocketThreadObject
Can someone suggest how to solve above two problems ? Reply on this will be appreciated.
Here is the code :---
================= start & stop buttons handler =====
void MainWindow::on_actionStop_triggered()
{
if(b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted = false;
delete p_tcpserverobjectWrapper;
/* ------------------ Tcp server object ------------------------*/
}
}
void MainWindow::on_actionStart_triggered()
{
if(!b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted =true;
p_tcpserverobjectWrapper = new tcpserverobjectWrapper(this,modelCANalyzer);
qDebug() << " \n start ";
/* ------------------ Tcp server object ------------------------*/
}
}
======== tcpserverobjectWrapper class ===============
// Main server object wrapper
class tcpserverobjectWrapper : public QObject
{
Q_OBJECT
public:
explicit tcpserverobjectWrapper(QMainWindow *ptrWidget, QStandardItemModel *modelCANalyzer, QObject *parent=0);
~tcpserverobjectWrapper();
//Device thread object
tcpserverobject *m_tcpserverobject;
};
tcpserverobjectWrapper::tcpserverobjectWrapper(QMainWindow *ptrWidget , QStandardItemModel *modelCANalyzer,QObject *parent) :
QObject(parent)
{
m_tcpserverobject = new tcpserverobject ;
//save model
m_tcpserverobject->modeltable = modelCANalyzer;
m_tcpserverobject->ptrmainwindow = ptrWidget;
qDebug() << "\n tcp server thread started";
}
tcpserverobjectWrapper::~tcpserverobjectWrapper()
{
qDebug() << " \n called delete later on tcpserverobjectWrapper .. !!";
m_tcpserverobject->deleteLater(); // ---------------------> change it to - delete m_tcpserverobject
qDebug() << " \n tcp server object successfully quited .. !! ";
}
========== tcpserverobject object ==================
class tcpserverobject : public QObject
{
Q_OBJECT
public:
explicit tcpserverobject(QObject *parent = 0);
~tcpserverobject();
/*!
Pointer to QStandardItemModel to be used inside - canTableView
*/
QStandardItemModel *modeltable;
//mainwindow pointer
QMainWindow *ptrmainwindow;
// Create list of new -- socket thread wrapper objects
QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;
private:
QTcpServer *tcpServer;
signals:
public slots:
void on_newConnection();
};
tcpserverobject::tcpserverobject(QObject *parent) :
QObject(parent), tcpServer(0)
{
tcpServer = new QTcpServer;
// Connect slot of the server
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
//lisen on socket
if (!tcpServer->listen(QHostAddress::LocalHost, SERVER_PORT )) {
qDebug() << "\n returning from server listning error .. !!! ";
return;
}
qDebug() << "\n server listning";
}
tcpserverobject::~tcpserverobject()
{
// to do
while (!TcpSocketThreadWrapperObjectList.isEmpty())
delete TcpSocketThreadWrapperObjectList.takeFirst();
}
void tcpserverobject::on_newConnection()
{
QByteArray block;
block.append(" \n Hello from server .. !!!") ;
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
// Create new thread for this .. client request ..!!
qDebug() << "\n New connection request ..!!!";
qDebug() << "\n New client from:" << clientConnection->peerAddress().toString();
clientConnection->write(block);
clientConnection->flush();
// create new tcp object
TcpSocketThreadWrapperObject* TcpSocketThreadWrapperObjectPtr = new TcpSocketThreadWrapperObject(clientConnection);
// Append object to the list
TcpSocketThreadWrapperObjectList.append(TcpSocketThreadWrapperObjectPtr);
}
============ TcpSocketThreadWrapperObject ==============
// Main device thread object
class TcpSocketThreadWrapperObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent = 0);
~TcpSocketThreadWrapperObject();
/*!
pointer for write thread
*/
QThread m_TcpSocketRWThread;
/// pointer to the socketthread object
class TcpSocketThreadObject *m_pTcpSocketThreadObject;
signals:
public slots:
};
// constructor for the deviceThreadObject
TcpSocketThreadWrapperObject::TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent) :
QObject(parent)
{
m_pTcpSocketThreadObject = new TcpSocketThreadObject(m_pTcpSocket);
//set flag for event loop -- make while(1)
m_pTcpSocketThreadObject->m_bQuit = false;
// connect the signal & slot
connect(&m_TcpSocketRWThread,SIGNAL(started()),m_pTcpSocketThreadObject,SLOT(dowork_socket()));
// Move thread to object
m_pTcpSocketThreadObject->moveToThread(&m_TcpSocketRWThread);
//Start the thread
m_TcpSocketRWThread.start();
}
TcpSocketThreadWrapperObject::~TcpSocketThreadWrapperObject()
{
//set flag for event loop -- make while(0)
m_pTcpSocketThreadObject->m_bQuit = false;
// Wait for the thread to terminate
m_TcpSocketRWThread.quit();
m_TcpSocketRWThread.wait();
// Delete the object
m_pTcpSocketThreadObject->deleteLater();
qDebug() << "\n deleted - TcpSocketThreadWrapperObject";
}
======== TcpSocketThreadObject object ========
class TcpSocketThreadObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent = 0);
~TcpSocketThreadObject();
/*!
Pointer to TCP socket -- created by the server
*/
QTcpSocket *m_pclientConnectionSocket;
/*!
Termination control main thread
*/
volatile bool m_bQuit;
signals:
public slots:
void dowork_socket();
};
// constructor for the deviceThreadObject
TcpSocketThreadObject::TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent) :
QObject(parent)
{
m_pclientConnectionSocket = m_pTcpSocketTemp;
// todo
}
TcpSocketThreadObject::~TcpSocketThreadObject()
{
// todo
}
void TcpSocketThreadObject::dowork_socket()
{
QByteArray block;
block.append(" \n hi again .. !!!") ;
// Write to socket
m_pclientConnectionSocket->write(block);
m_pclientConnectionSocket->flush();
// Close socket
m_pclientConnectionSocket->disconnectFromHost();
qDebug() << "\n entring loop of socket thread ..!!!";
while(!m_bQuit)
{
// while loop --> send/rx command from client
}
}
======= output of one client connected to server =====
New connection request ..!!!
New client from: "127.0.0.1"
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
entring loop of socket thread ..!!!
I think the simplest solution to all of this is to stop using QThread.
QTcpSocket and QTcpServer are both asynchronous, so when you receive a connection, you only need to create a class to wrap the QTcpSocket and this class handles the reading of the data, having connected the QTcpSocket signals to the class's slots.
If you're getting a lot of connections, then you'll be creating lots of threads. More threads than processor cores is just a waste of time.
If each connection is requesting a lot of work to be done by the server, you can then create separate worker objects and move those to a different thread, but overall, I recommend that you do not use separate threads for QTcpServer and QTcpSockets.
I want to communicate with a POP3 server with ssl and port 995 with my client app
the certificate of server is self-signed and while running the app the error that received is:
The certificate is self-signed, and untrusted
A part of code is:
socket = new QSslSocket(this);
QFile certfile("D:\\hani\\cert\\localhost.localdomain.pem");
Q_ASSERT(certfile.open(QIODevice::ReadOnly));
QList<QSslCertificate> certList;
QSslCertificate cert(&certfile,QSsl::Pem);
certList.append(cert);
socket->addCaCertificate(cert);
socket->setCaCertificates(certList);
QList<QSslCertificate> serverCert = socket->caCertificates();
What can I do?
DO NOT, let me repeat, DO NOT call ignoreSslErrors(). It completely defeats the purpose of SSL/TLS. There are very special cases where it can be called safely, but this (self-signed certificate) is not a special case.
The following minimal code, ready to run, shows how to securely accept a server self-signed certificate. Do not shortcut it.
The driver:
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
QTextStream log(stdout);
DummyClient dummy(log);
QObject::connect(&dummy, SIGNAL(done()), &app, SLOT(quit()));
return app.exec();
}
The DummyClient class:
/*
* Show how to safely authenticate a TLS server which uses a self-signed certificate.
* Warning: No error handling to keep the code short.
*/
class DummyClient : public QObject {
Q_OBJECT
public:
DummyClient(QTextStream& log)
: _log(log),
_sock(new QSslSocket(this)) {
connect(_sock, SIGNAL(encrypted()), this, SLOT(onEncrypted()));
connect(_sock, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(onSslErrors(QList<QSslError>)));
connect(_sock, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(onErrors(QAbstractSocket::SocketError)));
// Trust store: which CAs or self-signed certs we are going to trust.
//
// We use setCaCertificates() instead than QSslSocket::addCaCertificates()
// because we don't want to trust the ~200 default CAs.
QList<QSslCertificate> trustedCas = QSslCertificate::fromPath("server-cert.pem");
if (trustedCas.empty()) {
qFatal("Error: no trusted Cas");
}
_sock->setCaCertificates(trustedCas);
bool mutualAuth = false;
if (mutualAuth) {
// Our identity
_sock->setPrivateKey("client-key.pem");
_sock->setLocalCertificate("client-cert.pem");
}
_log << "Connecting" << endl;
// Note: serverName must match the cert CN or alternative name.
Qstring serverName = "myserver.example.org";
_sock->connectToHostEncrypted(serverName, 995);
}
signals:
void done();
private slots:
void onEncrypted() {
_log << "onEncrypted" << endl;
/* Everything is good. Start communicating. */
emit done();
}
void onSslErrors(QList<QSslError> errors) {
QSslError first = errors.takeFirst();
_log << "onSslErrors: " << first.errorString() << endl;
/* Something went wrong in the TLS handshake. Inform the user and quit! */
emit done();
}
void onErrors(QAbstractSocket::SocketError) {
_log << "onErrors: " << _sock->errorString() << endl;
emit done();
}
private:
QTextStream& _log;
QSslSocket* _sock;
};
http://qt-project.org/doc/qt-5.1/qtnetwork/qsslsocket.html
Look at the description of QSslSocket::sslErrors:
If you want to continue connecting despite the errors that have occurred,
you must call QSslSocket::ignoreSslErrors() from inside a slot connected
to this signal.
DISCLAIMER: This is very ill-advised, as it leaves the server "wide open" to man-in-the-middle attacks
and first of all sorry for my english
I have developed driver for fiscal printer using Qt. Driver specifics is communication between PC and device via RS232. Driver stored in shared object (Qt plugin) and server performs its loading by QPluginLoader.
So, about my problem... When i use one device and one driver instance all works fine, but when i attach many devices (3 for example), only last loaded device work. I have done many code checks, read many dumps of logs data, there is no error with devices and ports naming, but if i adress first device with command - 2 left devices receive the same command (according to log entries) and only last loaded device performs commands executing.
For example: from my server i send PrintReceipt command to device 1, at log file of device 1 i see entry : PrintReceipt, at log file of device 2 i see entry : PrintReceipt, : PrintReceipt, and at log file of device 3 i see 3 same entries. So, as i see my problem - QPluginLoader creates one instance for driver for first loaded device, then, when i try to load driver to second device - QPluginLoader creates new instance and replace driver for first device by recently created and so on for every device. So, at least, i have only one instance of driver (plugin) for many devices and my app logic crashes.
My question is: How to create multiple instances of the same plugin in Qt using QPluginLoader? My driver interface and loading code listed below.
class IPrinterDriver : public QObject
{
public:
// Printer driver initialization
virtual bool Init(QextSerialPort* port, const QString& deviceID) = 0;
// Driver identify name
virtual QString GetName() = 0;
// Gets current device state
virtual DeviceStates GetState() = 0;
// Gets device info and state test
virtual QString DeviceInfo() = 0;
// Set print mode to specified
virtual bool SetPrintMode(PrintModes mode) = 0;
// Get current print mode
virtual PrintModes PrintMode() const = 0;
// Sets device password from configuration
virtual void SetDevicePasswords(int operatorPassword, int adminPassword) = 0;
// Sets non-fiscal permissoin to device
virtual void SetNonFiscalModePermission(bool allowed) = 0;
// Gets device operator password
virtual int GetOperatorPassword() const = 0;
// Gets device administrator password
virtual int GetAdministratorPassword() const = 0;
// Gets non-fiscal mode permission
virtual bool GetNonFiscalModePermission() const = 0;
// Payment transaction
virtual bool PaymentTransaction(PaymentItem& item) = 0;
// Query device for X-Report
virtual bool GetXReport() = 0;
// Encashment transaction (Z-Report)
virtual bool Encash(bool fromBuffer) = 0;
// Print transaction
virtual bool Print(QString& text) = 0;
// Performs fiscal sale at device and returns receipt data
virtual FiscalReceiptData FPSSale(int requestID, int amount) = 0;
// Gets last fiscal receipt data
virtual FiscalReceiptData GetLastReceiptData() = 0;
// Gets serial port assigned to device
virtual QextSerialPort* GetDevicePort() = 0;
signals:
// Emits when device logging needed
virtual void DeviceLoggingNeeded(LogEntry entry, const QString& deviceID) = 0;
};
Q_DECLARE_INTERFACE(IPrinterDriver, "InfSys.Devices.IPrinterDriver/1.0")
And driver loading method:
// Performs loading specified driver for device at specified port
IPrinterDriver* PrintSystem::LoadDriver(PortConfiguration portConfig, const QString& driverName, const QString& adminPassword, const QString& operPassword, const QString& deviceID)
{
IPrinterDriver* result = NULL;
// Prepare plugin loader
QDir driversDir(_driversPath);
QStringList filesMask;
filesMask << tr("libdriver.%1.*").arg(driverName);
driversDir.setNameFilters(filesMask);
QStringList driversFiles = driversDir.entryList(QDir::Files);
// Load plugin with specified driver
foreach(QString driverFile, driversFiles)
{
// Load current driver;
QString driverFileName = driversDir.absoluteFilePath(driverFile);
QPluginLoader driversLoader(driverFileName);
// Try to init driver
QObject *driverObject = driversLoader.instance();
if (driverObject)
{
result = qobject_cast<IPrinterDriver *>(driverObject);
if (result && (result->GetName() == driverName))
{
QextSerialPort* devicePort = ConfigureSerialPort(portConfig);
if (devicePort == NULL)
{
driversLoader.unload();
return NULL;
}
// Init device
result->SetDevicePasswords(operPassword.toInt(), adminPassword.toInt());
result->SetNonFiscalModePermission(false);
result->SetPrintMode(Fiscal);
connect(result, SIGNAL(DeviceLoggingNeeded(LogEntry,QString)), App->LoggingModule, SLOT(OnDeviceLoggingNeeded(LogEntry,QString)), Qt::QueuedConnection);
bool initResult = result->Init(devicePort, deviceID);
if (!initResult)
{
driversLoader.unload();
return NULL;
}
}
else
driversLoader.unload();
}
}
return result;
}
I've been looking at this problem for a while now on my own project. I believe that there isn't any way to do this directly - QPluginLoader is working as designed.
The most straightforward way around the issue that I've come up with so far is to make the primary plugin interface function a factory for the objects you actually want.
In the example below, the thing I actually WANT is multiple IPlaybackDataSource objects. So I create a factory interface that the plugin implements. The plugin then returns as many objects of the type I desire as I want.
IPlaybackDataSource.h:
#include <QSharedPointer>
class IPlaybackDataSource {
public:
virtual bool open()=0;
};
// This is the interface that the plugin will implement
class IPlaybackDSFactory {
public:
virtual QSharedPointer<IPlaybackDataSource> newDataSource()=0;
};
Q_DECLARE_INTERFACE(IPlaybackDSFactory,
"com.moberg.DeviceSimulators.IPlaybackDSFactory/1.0")
TranFile.h:
#include <QtGui>
#include "TranFile_global.h"
#include "IPlaybackDataSource.h"
class TRANFILESHARED_EXPORT TranFile : public QObject, public IPlaybackDSFactory
{
Q_OBJECT
Q_INTERFACES(IPlaybackDSFactory)
public:
TranFile();
~TranFile();
virtual QSharedPointer<IPlaybackDataSource> newDataSource();
};
TranFile.cpp:
#include "TranFile.h"
Q_EXPORT_PLUGIN2(IPlaybackDSFactory, TranFile );
#include <QtCore>
#include <QDebug>
TranFile::TranFile(): QObject(), IPlaybackDSFactory() {}
TranFile::~TranFile() {}
QSharedPointer<IPlaybackDataSource> TranFile::newDataSource() {
return QSharedPointer<IPlaybackDataSource>();
}