如何在 QTcpServer 使用的套接字上设置 SO_REUSEADDR?
how to set SO_REUSEADDR on the socket used by QTcpServer?
我一直在使用QTcpServer子类作为http服务器,但现在我需要重用服务器端口。
我试过在QTcpSocket中设置ShareAddress | ReuseAddressHint
,似乎可行,因为同一个端口可以绑定两次。但是我没有找到从现有 QTcpServer 对象获取 QTcpSocket 对象的方法。
我也用了socketDescriptor()
获取native socket,因为我想用linux C的方式去setsockopt
,但是不知道怎么用linux C代码和Qt代码一起设置套接字选项。(我一直沿用Qt风格直到现在。)
我在 ubuntu 和 Qt5.4。我被卡住了...
任何帮助,将不胜感激。提前致谢。
因为 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 以便将来使用它。
这是一个示例,它将在任何接口上侦听端口 9999
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();
}
也许您想进一步优化套接字的使用?例如,将 SO_LINGER 超时设置为零以避免大量连接处于 TIME_WAIT 状态?也许您不需要等待 ACK(TCP_NODELAY)?
那么您的 newConnection 函数可以如下所示:
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();
}
这将完成工作并释放所有已使用的套接字资源,以便之后可以重新使用它们。对重负载微服务器等很有用。
我一直在使用QTcpServer子类作为http服务器,但现在我需要重用服务器端口。
我试过在QTcpSocket中设置ShareAddress | ReuseAddressHint
,似乎可行,因为同一个端口可以绑定两次。但是我没有找到从现有 QTcpServer 对象获取 QTcpSocket 对象的方法。
我也用了socketDescriptor()
获取native socket,因为我想用linux C的方式去setsockopt
,但是不知道怎么用linux C代码和Qt代码一起设置套接字选项。(我一直沿用Qt风格直到现在。)
我在 ubuntu 和 Qt5.4。我被卡住了... 任何帮助,将不胜感激。提前致谢。
因为 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 以便将来使用它。
这是一个示例,它将在任何接口上侦听端口 9999
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();
}
也许您想进一步优化套接字的使用?例如,将 SO_LINGER 超时设置为零以避免大量连接处于 TIME_WAIT 状态?也许您不需要等待 ACK(TCP_NODELAY)?
那么您的 newConnection 函数可以如下所示:
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();
}
这将完成工作并释放所有已使用的套接字资源,以便之后可以重新使用它们。对重负载微服务器等很有用。