how to set SO_REUSEADDR on the socket used by QTcpServer?

爱⌒轻易说出口 提交于 2020-01-03 05:16:19

问题


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.


回答1:


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..



来源:https://stackoverflow.com/questions/47268023/how-to-set-so-reuseaddr-on-the-socket-used-by-qtcpserver

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!