I have been using a QTcpServer subclass as a http server, but now I need to reuse the server port.
I have tried to set ShareAddress | ReuseAddressHint
in a QTcpSocket, it seems worked, because the same port can be bound twice. But I did not find a way to get a QTcpSocket object from an existing QTcpServer object.
I also used the socketDescriptor()
to get the native socket, because I want to use the linux C way to setsockopt
, but I don't know how to use linux C code with Qt code together to set socket options.(I followed the Qt style until now.)
I am on ubuntu and Qt5.4. And I am stuck... Any help would be appreciated. Thanks in advance.
Because SO_REUSEPORT needs to be set before bind/listen is called you need to create descriptor first, set all needed flags, bind, listen and forward your descriptor to QTcpServer for future usage with it.
Here is an example which will listen on port 9999 on any interface
mytcpserver.h:
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
public slots:
void newConnection();
private:
QTcpServer *server;
};
#endif // MYTCPSERVER_H
mytcpserver.cpp:
#include "mytcpserver.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
MyTcpServer::MyTcpServer(QObject *parent):QObject(parent)
{
this->server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &MyTcpServer::newConnection);
// open server and listen on given port
int sockfd = 0;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
qDebug() << "ERROR: IoT Omega Daemon can't open socket";
exit(EXIT_FAILURE);
}
int flag = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
exit(EXIT_FAILURE);
}
//set Address,IFace, Port...
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(9999);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(sockaddr_in)) < 0)
{
qDebug() << "ERROR: can't bind socket";
exit(EXIT_FAILURE);
}
if(listen(sockfd,SOMAXCONN) < 0)
{
qDebug() << "ERROR: can't listen on port";
exit(EXIT_FAILURE);
}
//forward our descriptor with SO_REUSEPORT to QTcpServer member
server->setSocketDescriptor(sockfd);
}
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
socket->close();
}
Maybe you would like to optimize your socket usage even more? For example setting SO_LINGER timeout of zero to avoid large numbers of connections sitting in the TIME_WAIT state? Maybe you don't ned waiting for ACKs(TCP_NODELAY)?
Then your newConnection function can look like this:
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
int flag = 1;
struct linger l = {1,0};
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
}
if(setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: can't fork set TCP_NODELAY";
}
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_LINGER, (const char *)&l,sizeof(l)) < 0)
{
qDebug() << "ERROR: Can't set SO_LINGER";
}
socket->close();
}
This will just do the job and free any used socket resource so they can be reused just right after that. Usefull for heavyload microservers, etc..