I am trying to connect to a website through a proxy. I implemented a QTcpServer and a QTcpSocket.
The server passes the connection to the socket.
It works well, but for some sites, expecially for those which have dynamically created javascript, the socket stucks at some point and nothing is shown in the navigator.
Attach the code, hope clear.
#include "webproxy.h"
#include <QtNetwork>
#include <QMessageBox>
#include <QtGui>
#include <QHash>
WebProxy::WebProxy(QObject *parent,int port): QObject(parent)
{
qDebug()<<" Listen...";
authMethod = "";
QTcpServer *proxyServer = new QTcpServer(this);
if (!proxyServer->listen(QHostAddress::Any, port)) {
emit error(1);
return;
}
connect(proxyServer, SIGNAL(newConnection()), this, SLOT(manageQuery()));
qDebug() << "Proxy server running at port" << proxyServer->serverPort();
void WebProxy::manageQuery() {
QTcpServer *proxyServer = qobject_cast<QTcpServer*>(sender());
QTcpSocket *socket = proxyServer->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), this, SLOT(processQuery()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
qDebug()<<"New connection started..."<<socket->peerAddress();
}
QUrl WebProxy::getUrl(QList<QByteArray > &entries)
{
QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);
qDebug()<<method;
qDebug()<<address;
qDebug()<<version;
QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {
qWarning() << "Invalid URL:" << url;
return QString();
}
return url;
}
void WebProxy::processQuery() {
int wSize = 0;
QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = socket->readAll();
qDebug()<<"Request "<<requestData;
int pos = requestData.indexOf("\r\n");
QByteArray requestLine = requestData.left(pos);
requestData.remove(0, pos + 2);
QList<QByteArray> entries = requestLine.split(' ');
QByteArray method = entries.value(0);
QByteArray address = entries.value(1);
QByteArray version = entries.value(2);
QByteArray auth;
QByteArray authMethod;
QUrl url = QUrl::fromEncoded(address);
if (!url.isValid()) {
qWarning() << "Invalid URL:" << url;
socket->disconnectFromHost();
return;
}
QString host = url.host();
int port = (url.port() <= 0) ? 80 : url.port();
QByteArray req = url.encodedPath();
if (url.hasQuery())
req.append('?').append(url.encodedQuery());
requestLine = method + " " + req + " " + version + "\r\n";
if (!authMethod.isEmpty())
{
requestLine.append(requestLine);
requestLine.append(authMethod);
requestLine.append("\r\n");
}
QString key = host + ':' + QString::number(port);
QTcpSocket *proxySocket = socket->findChild<QTcpSocket*>(key);
if (proxySocket) {
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
wSize = proxySocket->write(requestData);
} else {
proxySocket = new QTcpSocket(socket);
proxySocket->setObjectName(key);
proxySocket->setProperty("url", url);
proxySocket->setProperty("requestData", requestData);
connect(proxySocket, SIGNAL(connected()), this, SLOT(sendRequest()));
connect(proxySocket, SIGNAL(readyRead()), this, SLOT(transferData()));
connect(proxySocket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
connect(proxySocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(closeConnection()));
proxySocket->connectToHost(host, port);
}
}
void WebProxy::sendRequest() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray requestData = proxySocket->property("requestData").toByteArray();
int wSize = 0;
wSize = proxySocket->write(requestData);
}
void WebProxy::transferData() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
QByteArray data = proxySocket->readAll();
qDebug()<<"READ TRANSFER SIZE..."<<data.size();
QString host = proxySocket->peerAddress().toString();
QByteArray filtered(data);
QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
int wSize = 0;
if (!data.trimmed().isEmpty())
{
wSize = socket->write(filtered);
if (wSize==-1)
qDebug()<<"WP error";
else
qDebug()<<"TRANSFER WRITE SIZE = "<<wSize<<" READ SIZE"<<filtered.size();
}
}
void WebProxy::closeConnection() {
QTcpSocket *proxySocket = qobject_cast<QTcpSocket*>(sender());
if (proxySocket) {
QTcpSocket *socket = qobject_cast<QTcpSocket*>(proxySocket->parent());
if (socket)
socket->disconnectFromHost();
if (proxySocket->error() != QTcpSocket::RemoteHostClosedError)
qWarning() << "Error for:" << proxySocket->property("url").toUrl()
<< proxySocket->errorString();
proxySocket->deleteLater();;
}
}
You may want to use QTcpServer in a multi-threaded way.
Subclass QTcpServer, overload QTcpServer::incomingConnection(int), create your QThread derived handler (described next) and start it with QThread::start
Subclass QThread, make the constructor accept an int (the socket descriptor), overload QThread::run(). In the run function, create QTcpSocket, call QAbstractSocket::setSocketDescriptor to initialise the socket, connect up the socket slots and call QThread::exec() to start the thread event loop.
Make sure you create the socket in the run of QThread, not the constructor so the socket is associated with that thread.
For more details, look at the Threaded Fortune Server Example
Related
I am writing a WebSocket client application, based on the example, where the client application needs to pass the WebSocket Protocol while establishing connection with the server. Since QtWebSockets does not support the Websocket protocols, I am writing a C++ wrapper to use libWebSockets library and emit
connected, disconnected, textReceived kind of signals similar to QWebSocket.
My client application is able to connect to the server and is receiving the text message from the server, and I have copied minimum example code below, here I am facing an issue that when I emit a signal from the callback function the signal is not actually published and the slot I have connected to this signal never executed. I have verified that the object address passed in the session data is correct (copied the Logs of my program). What am I doing wrong here.
WebSocket.h
class WebSocket : public QObject
{
Q_OBJECT
public:
WebSocket(QObject *parent = Q_NULLPTR);
~WebSocket();
signals:
void connected();
void disconnected();
void textMessageReceived(const QString &message);
private:
static int callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
struct lws_context *context;
struct lws *client_wsi;
static const struct lws_protocols protocols[];
};
WebSocket.cpp
const struct lws_protocols WebSocket::protocols[] = {
{
"dumb_protocol",
callback_dumb_increment,
sizeof(per_session_data),
0,
},
{ NULL, NULL, 0, 0 }
};
WebSocket::WebSocket(QObject *parent) : QObject(parent)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
int n = 0;
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
qDebug() << "[parent] address: " << this;
info.user = this;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
context = lws_create_context(&info);
if (!context) {
qDebug() << "lws init failed";
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 7070;
i.address = "localhost";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.pwsi = &client_wsi;
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi)
n = lws_service(context, 0);
}
int WebSocket::callback_dumb_increment( struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len )
{
/* This will be same for every connected peer */
void *userdata = lws_context_user(lws_get_context(wsi));
qDebug() << "userData address: " << userdata;
QString message = "";
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
if (in)
qDebug() << "CLIENT_CONNECTION_ERROR: " << (char *)in;
else
qDebug() << "CLIENT_CONNECTION_ERROR: (null)";
wsi = NULL;
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
qDebug() << __func__ << " established";
emit static_cast<WebSocket*>(userdata)->connected();
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
message = QString::fromUtf8((const char *)in);
qDebug() << "RX: " << message;
emit static_cast<WebSocket*>(userdata)->textMessageReceived(message);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
wsi = NULL;
emit static_cast<WebSocket*>(userdata)->disconnected();
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
SocketClient.h
class SocketClient : public QObject
{
Q_OBJECT
public:
explicit SocketClient(QObject *parent = Q_NULLPTR);
~SocketClient();
public slots:
void onConnected();
void onTextMessageReceived(QString message);
private:
std::shared_ptr<WebSocket> webSock = nullptr;
};
SocketClient.cpp
SocketClient::SocketClient(QObject *parent) :QObject(parent)
{
webSock = std::make_shared<WebSocket>(this);
connect(webSock .get(), &WebSocket::connected, this, &SocketClient::onConnected);
connect(webSock .get(), &WebSocket::textMessageReceived,
this, &SocketClient::onTextMessageReceived);
}
Logs:
[parent] address: WebSocket(0x1b2cae32140)
userData address: 0x1b2cae32140
WebSocket::callback_dumb_increment established
userData address: 0x1b2cae32140
RX: "Hello World"
I am using QTCPSockets to talk to a program I've written in Qt for Raspberry Pi. The same software runs on my Mac (or Windows, whatever). The Pi is running a QTCPServer.
I send JSON data to it and most of the time this goes ok.
But sometimes, the Pi is not responding, the data does not seem to arrive. But then, when I send some more data, that data is not being handled, but the previous Json message is! And this stays like this. All the messages are now off by 1. Sending a new messages, triggers the previous one.
It feels a bit connected to this bugreport: https://bugreports.qt.io/browse/QTBUG-58262
But I'm not sure if it is the same.
I've tried waitForBytesWritten and flush and it seemed to work at first, but later I saw the issue again.
I expect that the TCP buffer on the Pi is not being flushed, but I do now know how to make sure that all data is handled right away.
As asked, here is some sourcecode:
This is the client software:
Client::Client() : tcpSocket(new QTcpSocket(this)), in(tcpSocket)
{
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readData);
connect(tcpSocket, &QTcpSocket::connected, this, &Client::connected);
connect(tcpSocket, &QTcpSocket::stateChanged, this, &Client::onConnectionStateChanged);
void (QAbstractSocket:: *sig)(QAbstractSocket::SocketError) = &QAbstractSocket::error;
connect(tcpSocket, sig, this, &Client::error);
}
void Client::connectTo(QString ip, int port) {
this->ip = ip;
this->port = port;
tcpSocket->connectToHost(ip, port);
}
void Client::reconnect() {
connectTo(ip, port);
}
void Client::disconnect()
{
tcpSocket->disconnectFromHost();
}
void Client::connected()
{
qDebug() << TAG << "connected!";
}
void Client::error(QAbstractSocket::SocketError error)
{
qDebug() << TAG << error;
}
void Client::sendData(const QString& data)
{
bool connected = (tcpSocket->state() == QTcpSocket::ConnectedState);
if (!connected) {
qDebug() << TAG << "NOT CONNECTED!";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
tcpSocket->write(block);
tcpSocket->flush();
}
void Client::sendData(const QByteArray& data) {
bool connected = (tcpSocket->state() == QTcpSocket::ConnectedState);
if (!connected) {
qDebug() << TAG << " is NOT connected!";
return;
}
tcpSocket->write(data);
tcpSocket->flush();
}
void Client::readData()
{
in.startTransaction();
QString data;
in >> data;
if (!in.commitTransaction())
{
return;
}
emit dataReceived(data);
}
void Client::onConnectionStateChanged(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
connectionState = "Not connected";
break;
case QAbstractSocket::ConnectingState:
connectionState = "connecting";
break;
case QAbstractSocket::ConnectedState:
connectionState = "connected";
break;
default:
connectionState = QString::number(state);
}
qDebug() << TAG << " connecting state: " << state;
emit connectionStateChanged(connectionState);
if (state == QAbstractSocket::UnconnectedState) {
QTimer::singleShot(1000, this, &Client::reconnect);
}
}
and here the server part:
Server::Server()
{
tcpServer = new QTcpServer(this);
connect(tcpServer, &QTcpServer::newConnection, this, &Server::handleConnection);
tcpServer->listen(QHostAddress::Any, 59723);
QString ipAddress;
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// if we did not find one, use IPv4 localhost
if (ipAddress.isEmpty())
ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
qDebug() << TAG << "ip " << ipAddress << " serverport: " << tcpServer->serverPort();
}
void Server::clientDisconnected()
{
QTcpSocket *client = qobject_cast<QTcpSocket *>(QObject::sender());
int idx = clients.indexOf(client);
if (idx != -1) {
clients.removeAt(idx);
}
qDebug() << TAG << "client disconnected: " << client;
client->deleteLater();
}
void Server::handleConnection()
{
qDebug() << TAG << "incoming!";
QTcpSocket* clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, &QAbstractSocket::disconnected, this, &Server::clientDisconnected);
connect(clientConnection, &QIODevice::readyRead, this, &Server::readData);
clients.append(clientConnection);
broadcastUpdate(Assets().toJson());
}
void Server::readData()
{
QTcpSocket *client = qobject_cast<QTcpSocket *>(QObject::sender());
QDataStream in(client);
in.startTransaction();
QString data;
in >> data;
if (!in.commitTransaction())
{
return;
}
...
// here I do something with the data. I removed that code as it is
// not necessary for this issue
...
broadcastUpdate(data);
}
void Server::broadcastUpdate(const QString& data)
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
foreach(QTcpSocket* client, clients) {
bool connected = (client->state() == QTcpSocket::ConnectedState);
if (!connected) {
qDebug() << TAG << client << " is NOT connected!";
continue;
}
client->write(block);
}
}
I think the problem is with your void Client::readData(): you have to write it in such a way that you read all available data from the socket inside it (usually it's written with while (socket->bytesAvailable() > 0) { ... } loop).
It is because of the way the readyRead() signal is emitted: the remote peer may send any non-zero number of packets to you, and your socket will emit any non-zero number of readyRead() signals. In your case, it seems that the server sends two messages but they only cause one readyRead() signal to be emitted on the client.
I have used QTcpSocket and QTcpServer class of qt to establish two way communication. I am able to send data from client to server. But am not getting the response back from server i.e my client.cpp never fires readyRead() signal. I have checked using Wireshark that my data from the server is available in specifed port.
I am posting my client.cpp code( Please help) :
Client::Client(QObject* parent): QObject(parent)
{
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()),
this, SLOT(startTransfer()));
connect(socket, SIGNAL(readyRead()),this, SLOT(startRead()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(socketError(QAbstractSocket::SocketError)) );
}
Client::~Client()
{
socket->close();
}
void Client::start(QString address, quint16 port)
{
addr.setAddress(address);
socket->connectToHost(addr,port,QTcpSocket::ReadWrite);
}
void Client::startTransfer()
{
printf("Connection established.\n");
char buffer[1024];
forever
{
printf(">> ");
gets(buffer);
int len = strlen(buffer);
buffer[len] = '\n';
buffer[len+1] = '\0';
socket->write(buffer);
socket->flush();
}
}
void Client::startRead()
{
cout<<"inside startRead()<<endl";
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
if(strcmp(ba.constData(), "!exit\n") == 0)
{
socket->disconnectFromHost();
break;
}
printf(">> %s", ba.constData());
}
}
void Client::socketError(QAbstractSocket::SocketError )
{
qDebug()<<"error" ;
}
Looks like you have forever loop here. This means that your Qt main eventloop never gets the control back after you call startTransfer(). How do you suppose the Qt should run the startRead() code if you block your execution thread with infinite loop?
For Amartel adding the server code:
Server::Server(QObject* parent): QObject(parent)
{
// cout << "Before connect" << endl;
connect(&server, SIGNAL(newConnection()),
this, SLOT(acceptConnection()));
cout << "Listening.." << endl;
server.listen(QHostAddress::Any, 9999);
// cout << "Server started.." << endl;
}
Server::~Server()
{
server.close();
}
void Server::acceptConnection()
{
// cout << "In acceptConnection" << endl;
client = server.nextPendingConnection();
connect(client, SIGNAL(readyRead()),
this, SLOT(startRead()));
}
void Server::startRead()
{
while(client->canReadLine())
{
QByteArray ba = client->readLine();
if(strcmp(ba.constData(), "!exit\n") == 0)
{
client->disconnectFromHost();
break;
}
printf(">> %s", ba.constData());
int result = 0;
bool ack = true;
result = client->write("I Reached");
cout<<result<<endl;
if(result <= 0)
qDebug("Ack NOT sent to client!!!");
else
qDebug("Ack sent to client.");
// client->write("I Reached");
client->flush();
}
}
How can I write user data to QTcpSocket and read it in
QTcpSocket * sock = tcpServer->nextPendingConnection();
?
Once you get your socket, you can just hook it up via signals/slots to get the data. You will need to save the socket via an instance variable.
Header:
QTcpSocket* sock;
Implementation File:
YourClass::someMethod {
this->sock = tcpServer->nextPendingConnection();
connect(this->sock, SIGNAL(readyRead()), this, SLOT(startRead()));
}
void YourClass::startRead {
char buffer[1024] = {0};
this->sock->read(buffer, client->bytesAvailable());
cout >> buffer >> endl;
this->sock->close();
}
I'm trying to make simple Udp communication between two application.
Client sends request to server to start some processing calculations etc. Server response is result of this computations. Server should report about its state.
My code:
onServerRecv method:
void MainWindow::onServerRecv()
{
while(m_udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(m_udpSocket->pendingDatagramSize());
m_udpSocket->readDatagram(datagram.data(),datagram.size(),&m_sender,
&m_senderPort);
QDateTime dt = QDateTime::currentDateTime();
QString sDtString = dt.toString("yyyy-MM-dd HH:mm:ss");
QString sLog = sDtString + ":" + QString::fromUtf8(datagram) + "\n";
m_textEdit->append(sLog);
//process datagram
instructionCall(datagram,m_sender,m_senderPort);
}
}
instructionCall:
void MainWindow::instructionCall(QByteArray instruction, QHostAddress addr,
qint16 senderPort)
{
if(instruction == "start")
{
QByteArray started = "started";
m_udpSocket->writeDatagram(started,m_sender,m_senderPort);
m_status = "bussy";
connect(&m_timer1,SIGNAL(timeout()),this,SLOT(sendStatus()));
m_timer1.start(500);
for(int i = 0; i != std::numeric_limits<int>::max(); i++)
{
}
m_status = "finished data ready";
disconnect(&m_timer1,SIGNAL(timeout()),this,SLOT(sendStatus()));
QByteArray finished = "finished";
m_udpSocket->writeDatagram(finished,m_sender,m_senderPort);
}
}
sendStatus:
void MainWindow::sendStatus()
{
m_udpSocket->writeDatagram(m_status,m_sender,m_senderPort);
}
I hope that it's clear what I want to do. Any suggestions about solutions? Can it be done without threads?