Мне потребовалось время, чтобы рассмотреть все возможные решения, хотя их было совсем немного.
Руководства сказали мне, что флаг SO_LINGER часто является результатом плохой архитектуры (в большинстве случаев клиент должен закрывать соединение, а не сервер), после нескольких экспериментов он получил этот флаг В моем случае это было бесполезно.
Привязка к точному IP приводит к нескольким сложностям, скорее костыль, чем решение ИМХО.
Итак, после незначительной адаптации класс прослушивания сокетов стал примерно таким:
QTcpServer2.h
#pragma once
#include
#include
class QTcpServer2 : public QTcpServer
{
QMutex mConnectionMutex;
QList mSocketDescriptors;
const int mPort;
bool mConnectionSuccessful;
private:
/*override */virtual void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
public:
bool TakeIncomingSocketDescription(qintptr& socketDescriptor);
bool isConnectionSuccessfull() const
{ return mConnectionSuccessful; }
public:
QTcpServer2(int port);
};
QTcpServer.cpp
#include "QTcpServer2.h"
#include
#ifdef Q_OS_WIN
#include
#pragma comment(lib, "ws2_32.lib")
#endif
#ifdef Q_OS_LINUX
#include
#include
#include
#endif
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(int port)
: QTcpServer()
, mPort(port)
, mConnectionSuccessful(false)
{
// open server and listen on given port
int sockfd = 0;
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
qDedug() << "QTcpServer2: socket couldn't be opened successfully!";
return; //RET
}
#ifdef Q_OS_WIN
// Not required in Linux, won't make any good
int flag = 1;
if(::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&flag), sizeof(int)) < 0)
{
qDedug() << "QTcpServer2: Can't set SO_REUSEADDR";
return; //RET
}
#endif
//set Address,IFace, Port...
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(static_cast(mPort));
if (::bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(sockaddr_in)) < 0)
{
qDedug() << "QTcpServer2: can't bind socket" ;
return; //RET
}
if(::listen(sockfd, SOMAXCONN) < 0)
{
qDedug() << "QTcpServer2: can't listen on port";
return; //RET
}
//forward our descriptor with SO_REUSEPORT to QTcpServer member
setSocketDescriptor(sockfd);
mConnectionSuccessful = true;
qDedug() << "QTcpServer2: socket success =)";
}
Поскольку «магическая проблема с сокетами» появилась только в Windows 8.1, #define
делает SO_REUSEADDR используемым только в Windows. В Linux Qt уже устанавливает этот флаг самостоятельно (как показано в вопросе), поэтому этот небольшой патч только улучшил поведение Windows, чтобы соответствовать желаемому, предоставленному в Linux без проблем.
Надеюсь, ничего подобного никогда не понадобится на других платформах.
private bool ViewExists(string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}
Для тех, кто ищет метод расширения копирования / вставки:
public static class ControllerExtensions
{
public static bool ViewExists(this Controller controller, string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(controller.ControllerContext, name, null);
return (result.View != null);
}
}
В ядре asp.net 2.x ViewEngines
свойство больше не существует так, мы должны использовать ICompositeViewEngine
сервис. Это вариант принятого ответа с помощью внедрения зависимости:
public class DemoController : Controller
{
private readonly IViewEngine _viewEngine;
public DemoController(ICompositeViewEngine viewEngine)
{
_viewEngine = viewEngine;
}
private bool ViewExists(string name)
{
ViewEngineResult viewEngineResult = _viewEngine.FindView(ControllerContext, name, true);
return viewEngineResult?.View != null;
}
public ActionResult Index() ...
}
Для любопытного: основной интерфейс IViewEngine
не регистрируется как услуга, таким образом, мы должны ввести ICompositeViewEngine
вместо этого. FindView()
метод однако предоставлен IViewEngine
, таким образом, членская переменная может использовать основной интерфейс.
Если вы хотите повторно использовать это в нескольких действиях контроллера, основываясь на решении, данном Дэйвом, вы можете определить результат пользовательского представления следующим образом:
public class CustomViewResult : ViewResult
{
protected override ViewEngineResult FindView(ControllerContext context)
{
string name = SomeMethodToGetViewName();
ViewEngineResult result = ViewEngines.Engines.FindView(context, name, null);
if (result.View != null)
{
return result;
}
return base.FindView(context);
}
...
}
Затем в вашем действии просто верните экземпляр вашего настраиваемого представления:
public ActionResult Index()
{
return new CustomViewResult();
}
А как насчет того, чтобы попробовать что-то вроде следующего, предполагая, что вы используете только один движок представления:
bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;