Итак, здесь у меня есть какое-то сложное клиент-серверное приложение, и некоторая его часть открывает порт для прослушивания и дальнейшего сетевого взаимодействия.QTcpServer
+ QTcpSocket
(подробности будут описаны ниже).
Этот «сетевой уровень» приложения работал нормально везде, кроме Windows 8.1 Pro.Только не спрашивайте, почему наш клиент решил, что его можно использовать в качестве сервера ... Случилось так, что неправильный перезапуск процесса, который открыл TCP-соединение на определенном порте для прослушивания, иногда приводит к тому, что этот порт не подходит длялюбые последующие попытки связать это.Это выглядит как какая-то магия, но поведение происходило следующим образом:
- Порт 8554 прослушивался ("соединение установлено") процессом
- Процесс аварийно завершается или каким-либо образом завершаетсяпри подключении клиентов через этот сокет
- Процесс перезапускается и снова пытается прослушивать порт.Сбой с «уже используется».
- Я останавливаю сервер и пытаюсь проверить порт через
netstat -an
.Это бесплатно. Я жду некоторое время и пытаюсь проверить порт через powershell, например:
$Listener = [System.Net.Sockets.TcpListener]8554
$Listener.Start()
Нет, это приводит к той же ошибке, что и "уже используется"Msgstr ""
- Я могу привязать сокет к другому порту, на них также работает фрагмент powershell.Но аварийный перезапуск нашего сервера также «ломает» их, сценарий тот же.
- Как только порт «сломан», перезапуск Windows - единственное лекарство от него.
- Привязка к »"любой адрес", т.е.
0.0.0.0:8554
ведет себя так, как описано.Привязка к точному IP-адресу, например 10.11.12.123:8554
, лучше, проверила его при попытке привязать FileZilla к «битому порту».
Теперь проблема кодирования.Предоставление точного IP-адреса для привязки выглядит плохой идеей, хотя бы в нашей архитектуре, поэтому я решил использовать SO_REUSEADDR в Windows.Но, похоже, мне нужно установить эту опцию перед вызовами bind / listen, что приводит к значительной настройке использования QTcpServer
.Имейте в виду, что приложение является кроссплатформенным (WinSock + sys / socket + some #defines, если нужно использовать что-то кроме методов Qt ...).Ааа, и вот моя любимая старая версия настройки QTcpServer
, посмотрите:
QTcpServer2.h
#pragma once
#include <QTcpServer>
#include <QMutex>
class QTcpServer2 : public QTcpServer
{
QMutex mConnectionMutex;
QList<qintptr> mSocketDescriptors;
private:
virtual void incomingConnection(qintptr socketDescriptor) override;
public:
bool TakeIncomingSocketDescription(qintptr& socketDescriptor);
public:
QTcpServer2();
};
QTcpServer2.cpp
#include <QMutexLocker>
#include "QTcpServer2.h"
void QTcpServer2::incomingConnection(qintptr socketDescriptor)
{
QMutexLocker lock(&mConnectionMutex);
mSocketDescriptors.append(socketDescriptor);
}
bool QTcpServer2::TakeIncomingSocketDescription(qintptr& socketDescriptor)
{
QMutexLocker lock(&mConnectionMutex);
if (mSocketDescriptors.empty()) {
return false;
}
socketDescriptor = mSocketDescriptors.takeFirst();
return true;
}
QTcpServer2::QTcpServer2()
{ }
Использование:
bool NetServer::DoInitConnection()
{
mNetServer = QSharedPointer<QTcpServer2>(new QTcpServer2);
if (!mNetServer->listen(QHostAddress::AnyIPv4, mPort)) {
Log.Fatal(QString("Listen port fail (port: %1)").arg(mPort), true);
return false;
}
return true;
}
Вздох ... Да, я знаю о механизме ожидающих соединений, об идее сигнального слота, но это вне кода на данный момент.TakeIncomingSocketDescription
используется для передачи дескриптора сокета куда-либо.И многопоточный доступ, да.Во всяком случае, этот кусок ... кода нуждается в рефакторинге, и мне очень нужен ваш совет: каков подходящий способ настройки привязки сокетов здесь? Предположим, что подклассы остаются, список дескрипторов тоже что-то не так-просто избавиться от.
Источники Qt показали мне следующее:
/*! \internal
*/
void QTcpServerPrivate::configureCreatedSocket()
{
#if defined(Q_OS_UNIX)
// Under Unix, we want to be able to bind to the port, even if a socket on
// the same address-port is in TIME_WAIT. Under Windows this is possible
// anyway -- furthermore, the meaning of reusable on Windows is different:
// it means that you can use the same address-port for multiple listening
// sockets.
// Don't abort though if we can't set that option. For example the socks
// engine doesn't support that option, but that shouldn't prevent us from
// trying to bind/listen.
socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
#endif
}
Это именно то, что я хочу, но нет доступа к этим внутренним компонентам из интерфейса классов Qt.QTcpServer
выполняет bind
вызов неявно, поэтому я не могу передать QAbstractSocket::ReuseAddressHint
туда.
Возможно, есть какое-то аккуратное решение, возможно, нет.Буду благодарен за любые идеи.