I'm trying to send some data from a c++ application to the apache server on the same machine.
I wrote some c++ code to send a POST request to the apache server.
The PHP script on the index.php page should collect the data and output them on the web page.
My C++ code:
void send_post()
{
portno = 80;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
cout << "ERROR opening socket" << endl;
}
else {cout << "Socket opened -> ";}
server = gethostbyname("localhost");
if (server == NULL)
{
cout << "ERROR, no such host" << endl;
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char*)&serv_addr.sin_addr.s_addr,server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
{
cout << "ERROR connecting" << endl;
}
else {cout << "Connected -> ";}
bzero(upcbuffer,512);
sprintf(upcbuffer, "%s", post_req.c_str());
z = write(sockfd,upcbuffer,strlen(upcbuffer));
if (z < 0)
{
cout << "ERROR writing to socket";
}
else {cout << "Wrote successfully to socket" << endl;}
close(sockfd);
cout << "Socket closed now" << endl;
}
With my post_req string:
post_req = "POST /index.php HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 8\r\n\r\ntesttest\r\n";
My PHP code on index.php
<?php
print_r($_POST);
?>
Sadly I can't see anything on the index page.
I'm sure that the POST request is well formatted.
Could you help me to fix the problem ?
Where do you expect to see something? To get the reply from the web server you should attempt to read from the socket instead of close it immediately after send/write.
I would also suggest you to get some more confidence with network programmin.
Here you get an evergreen read:
http://beej.us/guide/bgnet/output/html/multipage/index.html
Related
So I'm trying to upload a simple text file using Qt Network Mangager to a php script that I'm serving. But it's not working. I tried examples with QHttpMultiPart and with Setting raw data headers in request but none work.
Here is my Qt Code:
#include <QCoreApplication>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDebug>
#include <QEventLoop>
#include <QObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QFile>
#include <QHttpMultiPart>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString address = "http://localhost/api_test/";
//address = "https://dashboard.viewmind.ai/dashboard/api_test/welcome.php";
QUrl api_url = QUrl(address);
QVariantMap postDatamap;
postDatamap.insert("Name","Ariel Ñoño");
postDatamap.insert("Age",37);
QJsonDocument json = QJsonDocument::fromVariant(postDatamap);
qDebug() << "Sending the request";
QNetworkAccessManager *networkManager = new QNetworkAccessManager();
QNetworkRequest request(api_url);
QString bound = "<<<<<boundary>>>>>";
request.setRawHeader(QString("Content-Type").toUtf8(),QString("multipart/form-postData; boundary=" + bound).toUtf8());
//QByteArray postData;
QByteArray postData(QString("--" + bound + "\r\n").toUtf8());
postData.append("Content-Disposition: form-postData; name=\"action\"\r\n\r\n");
postData.append("welcome.php\r\n");
postData.append(QString("--" + bound + "\r\n").toUtf8());
postData.append("Content-Disposition: form-postData; name=\"uploaded\"; filename=\"");
postData.append("test.json");
postData.append("\"\r\n");
postData.append("Content-Type: text/xml\r\n\r\n"); //postData type
QFile file("test.json");
if (!file.open(QIODevice::ReadOnly)){
qDebug() << "QFile Error: File not found!";
delete networkManager;
return 0;
} else { qDebug() << "File found, proceed as planned"; }
postData.append(file.readAll());
postData.append("\r\n");
postData.append(QString("--" + bound + "\r\n").toUtf8());
request.setRawHeader(QString("Content-Length").toUtf8(), QString::number(postData.length()).toUtf8());
//request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json; charset=utf-8");
//request.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-postData; name=\"text\""));
//qDebug() << QString(json.toJson());
//QHttpMultiPart multipart(QHttpMultiPart::FormDataType);
//QHttpPart textPart;
// QFile file("test.json");
// if (!file.open(QIODevice::ReadOnly)){
// qDebug() << "Could not open file for reading";
// delete networkManager;
// return 0;
// }
//textPart.setBodyDevice(&file);
//multipart.append(textPart);
//file.setParent(&multipart);
//QNetworkReply *reply = networkManager->post(request,json.toJson());
//QNetworkReply *reply = networkManager->post(request,file.readAll());
//QNetworkReply *reply = networkManager->post(request,&multipart);
QNetworkReply *reply = networkManager->post(request,postData);
//file.setParent(reply);
//multipart.setParent(reply);
// Using the loop to wait for the reply to finish.
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
qDebug() << "Reply is finished";
//file.close();
if (reply->error() != QNetworkReply::NoError){
qDebug() << "The following error ocurred";
qDebug() << reply->errorString();
return 0;
}
QString postData_returned = QString::fromUtf8(reply->readAll());
qDebug() << "DATA RETURNED";
qDebug() << postData_returned;
return 0;
}
My php code looks like this
<?php
header("Access-Control-Allow-Origin: *"); // Anyone can access
header("Content-Type: application/json; charset=UTF-8"); // Will return json data.
error_log("FILES iS");
vardump_toerror($_FILES);
?>
It is my understanding that the $_FILES super global should get filled with the file information. Am I mistaken? But the print out shows it's empty.
I am not an expert in PHP but I find it unnecessary to use the content-type application/json since multipart (submit forms) is not part of that protocol. On the other hand I can't find a reference of the vardump_toerror function so I change with var_dump so my test php is:
<?php
var_dump($_FILES);
?>
In a previous question for PyQt5 I implemented a similar logic for django that also applies in this case so I will show a translation.
#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTextCodec>
QHttpMultiPart *buildMultpart(const QVariantMap & data, const QMap<QString, QString> filenames){
QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QVariantMap::const_iterator i_data = data.constBegin();
while (i_data != data.constEnd()) {
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(i_data.key()));
postpart.setBody(i_data.value().toByteArray());
multipart->append(postpart);
++i_data;
}
QMap<QString, QString>::const_iterator i_filenames = filenames.constBegin();
while (i_filenames != filenames.constEnd()) {
QFile *file = new QFile(i_filenames.value());
if(!file->open(QIODevice::ReadOnly)){
delete file;
continue;
}
QHttpPart postpart;
postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
QString("form-data; name=\"%1\"; filename=\"%2\"")
.arg(i_filenames.key(), file->fileName()));
postpart.setBodyDevice(file);
multipart->append(postpart);
file->setParent(multipart);
++i_filenames;
}
return multipart;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QUrl url("http://localhost:4000/upload.php");
QNetworkAccessManager manager;
QMap<QString, QString> filenames;
filenames["fileToUpload"] = "/path/of/data.txt";
QHttpMultiPart *multipart = buildMultpart({}, filenames);
QNetworkRequest request(url);
QNetworkReply *reply = manager.post(request, multipart);
multipart->setParent(reply);
QObject::connect(reply, &QNetworkReply::finished, QCoreApplication::quit);
a.exec();
if(reply->error() == QNetworkReply::NoError){
qDebug() << reply->readAll();
}
else{
qDebug() << reply->error() << reply->errorString();
}
delete reply;
return 0;
}
Output:
"array(1) {\n [\"fileToUpload\"]=>\n array(5) {\n [\"name\"]=>\n string(8) \"data.txt\"\n [\"type\"]=>\n string(0) \"\"\n [\"tmp_name\"]=>\n string(14) \"/tmp/phpVmOAhO\"\n [\"error\"]=>\n int(0)\n [\"size\"]=>\n int(6)\n }\n}\n"
I am having a really hard time reading character input that is sent through a socket connection to a Qt server application. The data is sent from PHP.
I understand the principles of reading streamdata because I already asked this on stack. I also got it working using a server and client written both in Qt.
The method I use is to append the bytesize of the data i want to send before the actual data. Then when the data comes in, I first read the length parth so that I know exactly how much bytes I have to read in order to have correctly formed data.
it looks like this:
send function:
void Client::sendNewMessage(){
qDebug() << "sendNewMessage()";
QString string(messageLineEdit->text());
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << quint16(0);
out << string;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
tcpSocket->write(block);
}
receive function:
QDataStream in(tcpServerConnection);
in.setVersion(QDataStream::Qt_4_0);
qDebug() << "bytes available = " << tcpServerConnection->bytesAvailable();
if (blockSize == 0) {
int size = (int) sizeof(quint16);
qDebug() << "size = " << size;
if (tcpServerConnection->bytesAvailable() < (int)sizeof(quint16)){
qDebug() << "less bytes than size...";
return;
}
qDebug() << "bytes available=" << tcpServerConnection->bytesAvailable();
in >> blockSize;
}
if (tcpServerConnection->bytesAvailable() < blockSize){
qDebug() << "less bytes available than blocksize, bytes="
<< tcpServerConnection->bytesAvailable();
return;
}
QString data;
in >> data;
qDebug() << "data = " << data;
Okay, this all works so I tried doing it with PHP but it failed
this is one of my attempts:
<?php
$addr = gethostbyname("127.0.0.1");
$client = stream_socket_client("tcp://$addr:*****", $errno, $errorMessage);
if ($client === false) {
throw new UnexpectedValueException("Failed to connect: $errorMessage");
}
$data = 'a';
$datatopost = serialize($data);
fwrite($client, strlen($data));
fwrite($client, base64_encode($data));
echo stream_get_contents($client);
fclose($client);
In Qt I have tried various combinations of quint8, 16, 32, 64, sizeof(char), sizeof(int).
in PHP I have tried serializing the data, encoding it, and also sending it without all that stuff. But i can not get it to work. I must be very close though because the data is actually sent as there are bytes available but I have no idea how to encode/decode correctly for it to work.
After asking various question concerning this topic I do feel that my understanding has gone up a lot but an important piece of information on how to actually do things is still missing for me.
So my question: What is going wrong here and what steps need to be taken to be able to read data from PHP to Qt/C++?
Details are highly apreciated as I really like to know how things work from the inside out.
side-note after sending data from the PHP script, the server sends data back aswel and that works. So the connection is made succesfuly
UPDATE
this is the working php script that actually also receives a reply back:
<?php
if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
perror("Could not create socket");
}
echo "Socket created n";
//Connect socket to remote server
if(!socket_connect($sock , '127.0.0.1' , *****))
{
perror("Could not connect");
}
echo "Connection established n";
$message = "aa";
//Send the message to the server
if( ! socket_send ( $sock , $message , strlen($message) , 0))
{
perror("Could not send data");
}
echo "Message send successfully n";
//Now receive reply from server
if(socket_recv ( $sock , $buf , 500 , MSG_WAITALL ) === FALSE)
{
perror("Could not receive data");
}
echo $buf;
///Function to print socket error message
function perror($msg)
{
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("$msg: [$errorcode] $errormsg n");
}
The script reply when executed from browser url:
Socket created nConnection established nMessage send successfully n hello
It's not that surprising the PHP code does not integrate. As mentioned you have to be aware that QDataStream implements a custom serialization. And as also mentioned you probably want to use (read|write)RawData, or (read|write)Bytes, if your reading something not previously serialized with QDataStream in general. However, the general idea of the way your trying to write string data from PHP should be compatible with the way Qt encodes strings (length then a series of characters. That is what the manual says anyway..). But there some issues.
QString is 2Byte Unicode.
PHP Strings are byte arrays of an arbitrary kind of ASCII compatible data - PHP String details.
There is a few things wrong with this bit:
fwrite($client, strlen($data));
fwrite($client, base64_encode($data));
strlen() returns the number of bytes in the underlying storage (which is the actual byte length for a ASCII string). base64_encode() changes the number of bytes in the string. And your assuming fwrite() is writing a four byte integer. Its type casting and writing a string.
We are still guessing at how
QString data;
in >> data;
really works.
General advice is, you've got to carefully define external binary APIs.
Do you need data serialization for this task at all? Your PHP client and Qt server are probably using different formats for it.
Try to send and receive raw data.
Here is a simple QTcpServer exmaple:
class DataReceiver : public QObject
{
Q_OBJECT
public:
explicit DataReceiver(QObject *parent = 0);
public slots:
void start(quint16 port = 9090);
private slots:
void newTcpConnection();
private:
QTcpServer server;
};
DataReceiver::DataReceiver(QObject *parent) :
QObject(parent)
{
connect(&server, SIGNAL(newConnection()), this, SLOT(newTcpConnection()));
}
void DataReceiver::start(quint16 port)
{
bool isOk = server.listen(QHostAddress::Any, port);
if (isOk && server.isListening())
{
qDebug() << "QTcpServer started on port" << port;
}
else
{
qCritical() << "Failed to start QTcpServer";
}
}
void DataReceiver::newTcpConnection()
{
qDebug() << "New incoming connection";
QTcpSocket *socket = server.nextPendingConnection();
QByteArray data;
while (true)
{
QByteArray tmp = socket->readAll();
data += tmp;
if (tmp.isEmpty() && !socket->waitForReadyRead())
{
break;
}
}
socket->deleteLater();
qDebug("Data received: %s (len = %d)", data.constData(), data.length());
}
Launching server:
#include <QCoreApplication>
#include "data_receiver.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
DataReceiver d;
d.start();
return a.exec();
}
You can use a PHP client to send data to it:
<?php
$addr = gethostbyname("127.0.0.1");
$port = 9090;
$data = 'hello from php';
$client = stream_socket_client("tcp://$addr:$port", $errno, $errorMessage);
if ($client === false) {
throw new UnexpectedValueException("Failed to connect: $errorMessage");
}
fwrite($client, $data);
fclose($client);
Or you can use the nc utility:
echo -n "hello from nc" | nc 127.0.0.1 9090
Here is server output for both cases:
QTcpServer started on port 9090
New incoming connection
Data received: hello from php (len = 14)
New incoming connection
Data received: hello from nc (len = 13)
What will be the C++ equivalemt command for below mentioned php command:
$command = shell_exec("sqlldr {$connect_string} control={$ctl_file_name} log={$log_file_name}");
So based on your comments a solution that would work would be to use popen(3):
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
// Set file names based on your input etc... just using dummies below
std::string
ctrlFileName = "file1",
logFileName = "file2",
cmd = "sqlldr usr/pwd#LT45 control=" + ctrlFileName + " log=" + logFileName ;
std::cout << "Executing Command: " << cmd << std::endl ;
FILE* pipe = popen(cmd.c_str(), "r");
if (pipe == NULL)
{
return -1;
}
char buffer[128];
std::string result = "";
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
{
result += buffer;
}
}
std::cout << "Results: " << std::endl << result << std::endl ;
pclose(pipe);
}
Try forkpty, you get a file descriptor which you can use to read from the other pseudoterminal.
I'm having some problems with sockets in c++(server) and php(client). Below is the code. The problem is that whenever the client gets disconnected (because of failure or whatever) the pClient->send() function should return -1 or 0. However, it doesn't. Therefore I cannot break the loop, and the program fails on this function. What am I doing wrong? I hope you can help me!
int sentSucces=0;
char szData[MAX_PACKET];
memset(szData, 0, MAX_PACKET);
CPassiveSocket socket;
CActiveSocket *pClient = NULL;
sendInterval = 10;
socket.Initialize();
socket.Listen((const uint8 *) "127.0.0.1", 9876);
socket.SetNonblocking(); //if no connection in queue, don't wait (accept())
while (1) {
//check queue for connections.
if ((pClient = socket.Accept()) != NULL) {
pClient->SetNonblocking();
cout << "++ connected\n";
while (1) {
// Receive request from the client.
cout << "- receiving....\n";
if (pClient->Receive(MAX_PACKET) > 0) {
cout << "-- received\n";
strncpy(szData, (const char *) pClient->GetData(), pClient->GetBytesReceived());
cout << "-- +: " << szData << "\n";
//clear memory of szData
memset(szData, 0, MAX_PACKET);
} else {
cout << "-- NOT received\n";
}
string bericht = "hallo";
cout << "sending....\n";
sentSucces = pClient->Send((const uint8 *) bericht.c_str(), strlen(bericht.c_str()));
cout << "++ send:" << sentSucces << "\n";
if (sentSucces < 1) break;
sleep(4);
}
cout << "++ disconnecting...\n";
pClient->Close();
cout << "++ disconnected\n";
} else {
// cout << "++ no connections\n";
}
usleep(10000);
}
Your code waits forever in Receive. If you don't want to do that, don't. The reason the Send function won't return an error is because you never get to it, waiting forever in Receive.
Whatever protocol you are implementing, does its specification say this end of the connection should wait forever try to receive data? If so, it's broken. If not, then your code is broken. If you don't have a protocol specification, you need to write one. Otherwise, all your code will only work together by luck.
The TCP protocol does guarantee that a side that sends will eventually detect a disconnection. But it makes no such guaranteed for a side that waits forever to receive data. If the other side never sends anything at all and goes away, you'll still be waiting forever. When you design a protocol that works over TCP, you must take this into account in the protocol design. This should have been resolved before even a single line of code was written.
i'm using popenRWE from http://www.jukie.net/bart/blog/popenRWE and making the script below
int pipes[3];
int pid;
const char *const args[] = {
"php ",
NULL
};
pid = popenRWE(pipes, args[0], args);
char *cmd = "<?php echo 'hello world';?> ";
cout << "write: " << write(pipes[0], cmd, strlen(cmd)) << endl;
cout << "err: " << errno << endl;
char res[100];
cout << "read: " << read(pipes[1], res, 100) << endl;
cout << "result: " << res << endl;
when i use cat command, it works, the input is the ouput (that's what cat doing), but using php the read is empty. i have confirmed that php is installed and on my path by running
echo "<?php echo 'hello world';?>" | php
directly on the console, and got the output. Can someone please advise or help on this code? Thanks in advance.
There are three problems with your code:
There is no executable named "php ". There is just "php" (notice that there is no space). The reason why this does not work is beceause popenRWE uses execvp which does not start a shell to execute the command but it expects the filename of the binary you want to executed (it searches for it in $PATH though).
You should close the stdin-filehandle after you've written your data, otherwise you might have to wait indefinitely for the output to be written.
Also you should wait for the php-process to finish using waitpid because otherwise you might "lose" some of the output.
To wrap it up:
int pipes[3];
int pid;
const char *const args[] = {
"php",
NULL
};
pid = popenRWE(pipes, args[0], args);
char *cmd = "<?php echo 'hello world', \"\\n\";?> ";
cout << "write: " << write(pipes[0], cmd, strlen(cmd)) << endl;
cout << "err: " << errno << endl;
close(pipes[0]);
// TODO: proper error handling
int status;
waitpid(pid, &status, 0);
char res[100];
int bytesRead = read(pipes[1], res, (sizeof(res)/sizeof(char))-1);
// zero terminate the string
res[bytesRead >= 0 ? bytesRead : 0] = '\0';
cout << "read: " << bytesRead << endl;
cout << "result: " << res << endl;