I am creating a (very basic) MJPG server to show webcam data on a browser. I have partly managed to do it so far.
Here is my code:
TcpServer::TcpServer(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
// whenever a user connects, it will emit signal
connect(server, SIGNAL(newConnection()),
this, SLOT(newConnection()));
if (!server->listen(QHostAddress::Any, 9999))
qDebug() << "Server could not start";
else
qDebug() << "Server started!";
}
...
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
"Cache-Control: no-cache\r\n" \
"Cache-Control: private\r\n" \
"Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n");
socket->write(ContentType);
std::vector<uchar> buff;
Mat img; //OpenCV Material
while (1) {
// Image to Byte Array via OPENCV Method
buff.clear();buff.empty();
vCapture->read(img); //Read from webcam
imencode(".jpg", img, buff, compression_params);
std::string content(buff.begin(), buff.end());
QByteArray CurrentImg(QByteArray::fromStdString(content));
QByteArray BoundaryString = ("--boundary\r\n" \
"Content-Type: image/jpeg\r\n" \
"Content-Length: ");
BoundaryString.append(QString::number(CurrentImg.length()));
BoundaryString.append("\r\n\r\n");
socket->write(BoundaryString);
socket->write(CurrentImg); // Write The Encoded Image
socket->flush();
}
}
The Problem -
When I run this program, the first image is shown. After that, the following error is continuously printed on the app -
QIODevice::write (QTcpSocket): device not open
It looked like the socket got closed, so I used re-initialized the socket, like this - socket = server->nextPendingConnection();, although the app threw an error with this code. Any help on how to fix this?
EDIT -
I tried the lambda method and it worked fine. However, I still face 2 problems -
The image size has to be excessively low (around 270x480 with lowest JPG quality)
(MORE IMPORTANT) I have to manually press the reload button on browser to reload the image, it doesn't automatically change from one image to the other.
It looked like the socket got closed
Rather than guessing, connect to the error signals of TCPServer and TCPSocket to know when errors occur, or when a client disconnects.
The problem you have is the while(1) loop. Qt is an event-driven framework, so having code in an infinite loop on the main thread is going to prevent events being delivered.
Instead of the infinite loop, connect to QTcpSocket::readyRead signal and handle the data when the connected slot is called.
Qt demonstrates this with the Fortune Server and Fortune Client example code.
If you're using C++ 11, you can use a connection to a lambda function to handle the readyRead, like this
void TcpServer::newConnection()
{
...
QTcpSocket *m_TcpHttpClient = server->nextPendingConnection();
connect(m_TcpHttpClient, &QTcpSocket::readyRead, [=](){
// handle received data here
});
}
Here Is the Code I used to open the MJPEG Streaming Server in my original Project. Perhaps It can help you finding out your Problem.
void MjpegStreamingEngine::StartServer(){
m_TcpHttpServer = new QTcpServer();
m_TcpHttpServer->connect(m_TcpHttpServer,
SIGNAL(newConnection()),
this,
SLOT(TcpHttpconnected()));
m_TcpHttpServer->listen(QHostAddress::Any,
8889);
}
void MjpegStreamingEngine::TcpHttpconnected(){
m_TcpHttpClient = m_TcpHttpServer->nextPendingConnection();
m_TcpHttpClient->connect(m_TcpHttpClient,
SIGNAL(readyRead()),
this,
SLOT(TcpHttpreadyRead()));
}
void MjpegStreamingEngine::TcpHttpreadyRead(){
m_TcpHttpClient->readAll(); // Discard "Get Request String"
QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
"Server: en.code-bude.net example server\r\n" \
"Cache-Control: no-cache\r\n" \
"Cache-Control: private\r\n" \
"Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n\r\n");
m_TcpHttpClient->write(ContentType);
while(1){
if (m_TcpHttpClient->isOpen())
{
// Send Image
QThread::msleep(10);
}else{
return;
}
}
m_TcpHttpClient->disconnect(); //Should never be Reached
}
Related
I created a TcpServer in order to receive data from a client. The client sends a lot of messages and I would like to read them. So far my TcpServer.cpp looks like this :
void TcpServer::serverStart()
{
server = new QTcpServer(this);
if (!server->listen(QHostAddress("192.168.x.x"), 48583))
{
qDebug() << "Not listening";
server->close();
delete server;
return;
}
else {
qDebug() << "Listening";
}
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
void TcpServer::newConnection()
{
socket = server->nextPendingConnection();
qDebug() << "Client connected";
connect(socket, SIGNAL(readyRead()), this, SLOT(getData()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
}
void TcpServer::getData()
{
QByteArray buffer;
while (socket->bytesAvailable())
{
buffer.append(socket->readAll());
}
qDebug() << buffer;
}
void TcpServer::serverStop()
{
server->close();
delete server;
}
I know my getData function needs a lot more in order to receive everything but I don't understand the steps needed to do that.If someone could give me some pointers I would be grateful !
TCP is a transport protocol which is stream oriented. Imagine it as being a continuous flow of data. There are no messages defined by TCP yet, because once again it is a continuous flow of data.
I'm taking from your comment that you are not using any application layer protocol. You need an application layer protocol, like e.g. http, which is then defining "messages" and giving you further instructions on how to read a complete message.
I am trying to receive packet from UDP-connected infrared camera. (FLIR Lepton 2.5) And I'm trying to run my codes on Windows 10, Qt Creator 4.5, Qt 5.9.
As you can see on the capture below, my UDP-connected infrared camera sends UDP packets.
And here's my code:
// myudp.cpp
#include "myudp.h"
#include <QNetworkProxy>
MyUDP::MyUDP(QObject *parent) :
QObject(parent)
{
// create a QUDP socket
socket = new QUdpSocket(this);
socket_cam = new QUdpSocket(this);
cam_address = QHostAddress("192.168.10.197");
cam_port = 32197;
connect(socket, SIGNAL(connected()), this, SLOT(readNwrite()));
socket->connectToHost(cam_address, cam_port, QIODevice::ReadWrite);
if (socket->waitForConnected(2000))
qDebug("Connected!");
else
qDebug("Cannot be connected..");
qDebug() << socket->state();
// seems to be connected...
}
void MyUDP::readNwrite()
{
QByteArray data;
qint64 resize_size = socket->pendingDatagramSize();
//socket have NO size(resize_size == -1, ERROR!)
if(resize_size != -1)
{
qDebug() << "data was resized properly; size: "<<resize_size;
data.resize(socket->pendingDatagramSize());
}
else
qDebug() << "data could not be resized(error)";
qint64 det_num;
det_num = socket->readDatagram(data.data(), 964, &cam_address, &cam_port);
qDebug() << "Can receive data(returns -1 if NO): "<<det_num;
//this returns nothing, too!
qDebug() << "Data is here: " << data.data();
}
Here's my code implementation result:
When signal is connected(), data could not be resized(error)
Can receive data(returns -1 if NO): -1
Data is here:
Connected!
QAbstractSocket::ConnectedState
When signal is readyRead(), Connected!
QAbstractSocket::ConnectedState data was resized properly; size: 0
Can receive data(returns -1 if NO): -1
Data is here:
readyRead signal sometimes seems NOT emitted as expected.
But it is obvious that any data is NOT being sent.
I've tried to find examples which are related to UDP network with a remote network. But there were few of them.
According to some threads, people recommended not to use connectToHost function on UDP network. However, I don't know how to make an approach to remote network without using connectToHost.
I want to know how I should correct my code to get packet from a remote network. Any advice will be very grateful for me because I'm a newbie to UDP network, and Qt.
I want to create a Server for MJPEGs and I found this tutorial: http://www.bogotobogo.com/Qt/Qt5_QTcpServer_Client_Server.php. But when I connect from a Web Browser like Chrome (Microsoft Telnet works fine), it shows connection reset.
After creating the server, I would like to show MJPEGs on my browser (like an IP Camera does) using this: How to Create a HTTP MJPEG Streaming Server With QTcp-Server Sockets?
Here's my Server.cpp (A little modified) -
#include "stdafx.h"
#include "TCPServer.h"
TcpServer::TcpServer(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(newConnection()));
if (!server->listen(QHostAddress::Any, 9999))
qDebug() << "Server could not start";
else
qDebug() << "Server started!";
}
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray header = "HTTP/1.1 200 OK\r\n";
socket->write(header);
QByteArray ContentType = "Content-Type: text/html\r\n";
socket->write(ContentType);
QByteArray Body = "Test";
socket->write(Body);
socket->flush();
socket->close();
}
After LOTS of trial and error (and luck), I found the solution. The code should end like this -
...
socket->flush();
socket->waitForBytesWritten(10000);
socket->close();
EDIT -
After trying again, I found out this - There should be an extra \r\n after the headers. So this will work -
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray header = "HTTP/1.1 200 OK\r\n";
socket->write(header);
QByteArray ContentType = "Content-Type: text/html\r\n\r\n\*Here is the edit*\";
socket->write(ContentType);
QByteArray Body = "Test";
socket->write(Body);
socket->flush();
socket->close();
}
I am designing and making a server that should be able to handle about 100+ hits per second. The information I am getting from the server is just the HTTP header. Based on the information from the header, it will query a database(different thread) for some information and send the final information back to the QTcpServer which create an output string, and send back a HTTP Response. I am having a big problem with this that I cannot debug. My code look similar to this:
TCPInterface::TCPInterface(QObject *parent): QTcpServer(parent)
{
//start listening for tcp traffic on port 80
listen(QHostAddress::Any, 80);
connect(this,SIGNAL(sendInfo(QTcpSocket*, QString *)), databaseThread, SLOT(recieveInfo(QTcpSocket*, QString*)));
connect(databaseThread, SIGNAL(sendToTCPSend(QTcpSocket *, QString *)), this, SLOT(TCPSend(QTcpSocket*, QString*)));
}
`
void TCPInterface::incomingConnection(int socket)
{
QTcpSocket *s = new QTcpSocket(this);
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
//connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
s->setSocketDescriptor(socket);
}
`
//void TCPInterface::discardClient()
//{
//QTcpSocket* socket = (QTcpSocket*)sender();
//socket->deleteLater();
//}
`
void TCPInterface::readClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
QString header;
while(socket->canReadLine())
{
header += socket->readLine();
}
emit sendInfo(socket, headerInfo);
}
`
void databaseThread::recieveInfo(QTcpSocket* socket, QString* headerInfo)
{
QString*outputInfo = getDatabaseInfo(headerInfo);
emit sendToTCPSend(socket, outputInfo);
}
`
void TCPInterface::TCPSend(QTcpSocket* socket, QString* outputInfo);
{
QString response = "HTTP/1.0 200 Ok\r\n";
response += "Content-Type: text/html; charset=\"utf-8\"\r\n";
response += "\r\n" + *outputInfo + "\n";
if(socket->isWritable() && socket->isOpen())
{
socket->write(response.toAscii());
}
//socket->disconnectFromHost();
socket->close();
delete headerInfo;
}
I having one main problem which I have an idea what it is, but cannot find a solution to fix it.
My problem is my memory is constantly increasing as I get more hits. I am sure the cause of this is my QTcpSockets are never being deleted, since I am just closing them. However when I don't use close, and use disconnectFromHost and disconnected/discardClient slot/signal my server will crash with heavy traffic(no message or anything so I am not sure of the exact reason of the crash). Has anyone run into this problem before? Any ideas at all.
I have the same problem!
close() suppose to be as de-constructor of the object. (QT manual QTcpSocket::~QTcpSocket())
There for I suggest to do an experiment: close a socket, and try to re open it. If it fails, it means the socket Object was destroyed, if not means the object should be deletelater()..
In my case the connection is closed by the client, and there for disconnect() SIGNAL is invoked and it trigger the correspond method to your discardClient() SLOT, where I deletelater() the socket.
When I stress test it, it usually crash when I bombard it with 600-800 connection simultaneously on an I5 laptop dual core. it crash every 5 times on average.
Other wise it doesn't.
Happy to discuss it further.
Gil
You should call deleteLater() on your client socket:
connect(socket, SIGNAL(disconnected()),
socket, SLOT(deleteLater()));
I am trying to implement a bidirectional client-server program, where clients and servers can pass serialized objects between one another. I am trying to do this using Qt (QTcpSocket and QTcpServer). I have implemented programs like this in java, but I can't figure out how to do it using Qt. I've checked out the fortune client and fortune server examples...but from what I can see, the client is simply signaling the server, and the server sends it some data. I need for the client and server to send objects back and forth. I am not looking for a complete solution, all I am looking for is some guidance in the right direction.
I wrote some code, which accepts a connection, but does not accept the data.
SERVER
this class is the server; it should be accepting a connection and outputting the size of the buffer which is being sent. However it is outputting 0
#include "comms.h"
Comms::Comms(QString hostIP, quint16 hostPort)
{
server = new QTcpServer(this);
hostAddress.setAddress(hostIP);
this->hostPort = hostPort;
}
void Comms::attemptConnection(){
connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
//socket = server->nextPendingConnection();
server->listen(hostAddress,hostPort);
//receivedData = socket->readAll();
}
void Comms::connectionAccepted(){
qDebug()<<"Connected";
socket = new QTcpSocket(server->nextPendingConnection());
char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
}
CLIENT
This class is the client. It should be sending the string 'hello'. It sends it successfully (to my knowledge)
#include "toplevelcomms.h"
#include "stdio.h"
TopLevelComms::TopLevelComms(QString hostIP, quint16 hostPort)
{
tcpSocket = new QTcpSocket();
hostAddress.setAddress(hostIP);
this->hostPort = hostPort;
}
void TopLevelComms::connect(){
tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
//tcpSocket->waitForConnected(1);
QString string = "Hello";
QByteArray array;
array.append(string);
qDebug()<<tcpSocket->write(array);
}
Please tell me what I'm doing wrong, or tell me the general logic of establishing what I want in Qt.
QTcpSocket is asynchronous by default, so when you call connectToHost and write in same context it won't be sent, as socket is not connected. You should change your "client" code:
void TopLevelComms::connect(){
tcpSocket->connectToHost(hostAddress,hostPort,QIODevice::ReadWrite);
if(tcpSocket->waitForConnected()) // putting 1 as parameter isn't reasonable, using default 3000ms value
{
QString string = "Hello";
QByteArray array;
array.append(string);
qDebug()<<tcpSocket->write(array);
}
else
{
qDebug() << "couldn't connect";
}
}
Note: you also didn't check if you're able to listen
void Comms::attemptConnection(){
connect(server, SIGNAL(newConnection()), this, SLOT(connectionAccepted()));
//socket = server->nextPendingConnection();
if(server->listen(hostAddress,hostPort))
{
qDebug() << "Server listening";
}
else
{
qDebug() << "Couldn't listen to port" << server->serverPort() << ":" << server->errorString();
}
//receivedData = socket->readAll();
}
And last thing. Note that QTcpServer::nextPendingConnection() return QTcpSocket, so instead of taking that new connection you create new QTcpSocket with nextPendingConnection as parent
void Comms::connectionAccepted(){
qDebug()<<"Connected";
// WRONG! it will use QTcpSocket::QTcpSocket(QObject * parent)
//socket = new QTcpSocket(server->nextPendingConnection());
// use simple asign
socket = server->nextPendingConnection();
// move reading to slot
connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
}
now we will move reading to separate slot
void Comms::readSocket()
{
// note that dynamic size array is incompatible with some compilers
// we will use Qt data structure for that
//char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
// note that QByteArray can be casted to char * and const char *
QByteArray data = socket->readAll();
}
I must admit, that it is a lot of errors as for such small code sample. You need to get some knowledge about TCP/IP connections. Those are streams and there is no warranty that whole data chunk will get to you at once
It looks like you have a timing issue. Since your client and server are different processes, there's no way you can guarantee that the entirety of TopLevelComms::connect() is being executed (along with the network data transfer) before your server's connectionAccepted() function tries to read from the socket.
I suspect that if you take advantage of QTcpSocket's waitForReadyRead() function, you should have better luck:
void Comms::connectionAccepted(){
qDebug()<<"Connected";
socket = new QTcpSocket(server->nextPendingConnection());
if( socket->waitForReadyRead() ) {
char* rec = new char[socket->readBufferSize()];
qDebug()<<socket->readBufferSize();
}
}