209 lines
5.7 KiB
C++
209 lines
5.7 KiB
C++
|
#include "server.h"
|
||
|
#include <mitsuba/core/sstream.h>
|
||
|
#include <mitsuba/core/sched_remote.h>
|
||
|
|
||
|
ServerThread::ServerThread(Logger *logger, int listenPort, const std::string &nodeName)
|
||
|
: Thread("serv"), m_listenPort(listenPort), m_nodeName(nodeName) {
|
||
|
setLogger(logger);
|
||
|
}
|
||
|
|
||
|
ServerThread::~ServerThread() {
|
||
|
}
|
||
|
|
||
|
void ServerThread::shutdown() {
|
||
|
m_active = false;
|
||
|
join();
|
||
|
}
|
||
|
|
||
|
void ServerThread::run() {
|
||
|
SLog(EInfo, "Mitsuba version " MTS_VERSION ", Copyright (c) " MTS_YEAR " Wenzel Jakob");
|
||
|
/* Allocate a socket of the proper type (IPv4/IPv6) */
|
||
|
ref<Scheduler> scheduler = Scheduler::getInstance();
|
||
|
struct addrinfo hints, *servinfo, *p = NULL;
|
||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||
|
hints.ai_family = AF_UNSPEC;
|
||
|
hints.ai_flags = AI_PASSIVE;
|
||
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
char portName[8];
|
||
|
int rv, one = 1;
|
||
|
m_socket = INVALID_SOCKET;
|
||
|
m_active = true;
|
||
|
|
||
|
snprintf(portName, sizeof(portName), "%i", m_listenPort);
|
||
|
if ((rv = getaddrinfo(m_nodeName.c_str(), portName, &hints, &servinfo)) != 0)
|
||
|
SLog(EError, "Error in getaddrinfo(%s:%i): %s", m_nodeName.c_str(), m_listenPort, gai_strerror(rv));
|
||
|
|
||
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||
|
/* Allocate a socket */
|
||
|
m_socket = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||
|
if (m_socket == -1)
|
||
|
SocketStream::handleError("socket");
|
||
|
|
||
|
/* Avoid "bind: socket already in use" */
|
||
|
if (setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0)
|
||
|
SocketStream::handleError("setsockopt");
|
||
|
|
||
|
/* Bind the socket to the port number */
|
||
|
if (bind(m_socket, p->ai_addr, (socklen_t) p->ai_addrlen) == -1) {
|
||
|
SocketStream::handleError(formatString("bind(%s:%i)", m_nodeName.c_str(), m_listenPort), EError);
|
||
|
#if defined(WIN32)
|
||
|
closesocket(m_socket);
|
||
|
#else
|
||
|
close(m_socket);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (p == NULL)
|
||
|
SLog(EError, "Failed to bind to port %i!", m_listenPort);
|
||
|
|
||
|
freeaddrinfo(servinfo);
|
||
|
|
||
|
if (listen(m_socket, CONN_BACKLOG) == -1)
|
||
|
SocketStream::handleError("bind");
|
||
|
|
||
|
SLog(EInfo, "%s: Listening on port %i.. Close the window to stop.",
|
||
|
m_nodeName.c_str(), m_listenPort);
|
||
|
|
||
|
fd_set fds;
|
||
|
struct timeval tv;
|
||
|
memset(&tv, 0, sizeof(struct timeval));
|
||
|
tv.tv_usec = 500 * 1000;
|
||
|
|
||
|
int connectionIndex = 0;
|
||
|
/* Wait for connections */
|
||
|
while (m_active) {
|
||
|
FD_ZERO(&fds);
|
||
|
FD_SET(m_socket, &fds);
|
||
|
int ret = select(m_socket+1, &fds, NULL, NULL, &tv);
|
||
|
if (ret <= 0 || !FD_ISSET(m_socket, &fds))
|
||
|
continue;
|
||
|
|
||
|
socklen_t addrlen = sizeof(sockaddr_storage);
|
||
|
struct sockaddr_storage sockaddr;
|
||
|
memset(&sockaddr, 0, addrlen);
|
||
|
|
||
|
SOCKET newSocket = accept(m_socket, (struct sockaddr *) &sockaddr, &addrlen);
|
||
|
|
||
|
if (newSocket == INVALID_SOCKET) {
|
||
|
#if defined(WIN32)
|
||
|
if(!m_active)
|
||
|
break;
|
||
|
#else
|
||
|
if (errno == EINTR)
|
||
|
continue;
|
||
|
#endif
|
||
|
SocketStream::handleError("accept", EWarn);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ref<StreamBackend> backend = new StreamBackend(formatString("con%i", connectionIndex++),
|
||
|
scheduler, m_nodeName, new SocketStream(newSocket), true);
|
||
|
backend->start();
|
||
|
}
|
||
|
|
||
|
#if defined(WIN32)
|
||
|
closesocket(m_socket);
|
||
|
#else
|
||
|
close(m_socket);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
ServerWidget::ServerWidget(QWidget *parent,
|
||
|
const QString &nodeName, int listenPort)
|
||
|
: QMainWindow(parent) {
|
||
|
m_contents = new QTextEdit(this);
|
||
|
QFont font("Monospace");
|
||
|
font.setStyleHint(QFont::TypeWriter);
|
||
|
m_contents->setFont(font);
|
||
|
m_contents->setReadOnly(true);
|
||
|
QPalette palette;
|
||
|
palette.setColor(QPalette::Base, Qt::black);
|
||
|
m_contents->setPalette(palette);
|
||
|
setPalette(palette);
|
||
|
QToolBar *toolBar = new QToolBar(this);
|
||
|
toolBar->setMovable(false);
|
||
|
toolBar->setAllowedAreas(Qt::TopToolBarArea);
|
||
|
toolBar->setIconSize(QSize(32, 32));
|
||
|
toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||
|
toolBar->setFloatable(false);
|
||
|
|
||
|
QAction *actionClear = new QAction(this);
|
||
|
QIcon clearIcon;
|
||
|
clearIcon.addFile(QString::fromUtf8(":/resources/clear.png"), QSize(), QIcon::Normal, QIcon::Off);
|
||
|
actionClear->setIcon(clearIcon);
|
||
|
actionClear->setToolTip(tr("Clear"));
|
||
|
actionClear->setText(tr("Clear"));
|
||
|
connect(actionClear, SIGNAL(triggered()), m_contents, SLOT(clear()));
|
||
|
toolBar->addAction(actionClear);
|
||
|
|
||
|
#if defined(__OSX__)
|
||
|
toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
||
|
#endif
|
||
|
|
||
|
addToolBar(Qt::TopToolBarArea, toolBar);
|
||
|
setCentralWidget(m_contents);
|
||
|
setUnifiedTitleAndToolBarOnMac(true);
|
||
|
setMenuBar(NULL);
|
||
|
setWindowTitle(tr("Server Log"));
|
||
|
resize(QSize(1000, 500));
|
||
|
|
||
|
QConsoleAppender *consoleAppender = new QConsoleAppender();
|
||
|
|
||
|
m_logger = new Logger(Thread::getThread()->getLogger()->getLogLevel());
|
||
|
m_logger->addAppender(consoleAppender);
|
||
|
m_logger->setFormatter(new DefaultFormatter());
|
||
|
connect(consoleAppender, SIGNAL(textMessage(ELogLevel, const QString &)),
|
||
|
this, SLOT(onTextMessage(ELogLevel, const QString &)), Qt::QueuedConnection);
|
||
|
|
||
|
m_thread = new ServerThread(m_logger, listenPort, nodeName.toStdString());
|
||
|
m_thread->start();
|
||
|
}
|
||
|
|
||
|
ServerWidget::~ServerWidget() {
|
||
|
}
|
||
|
|
||
|
void ServerWidget::onTextMessage(ELogLevel level, const QString &message) {
|
||
|
QColor color;
|
||
|
switch (level) {
|
||
|
case ETrace:
|
||
|
case EDebug:
|
||
|
case EInfo:
|
||
|
color = Qt::gray;
|
||
|
break;
|
||
|
case EError:
|
||
|
color = Qt::red;
|
||
|
break;
|
||
|
case EWarn:
|
||
|
default:
|
||
|
color = QColor(255,180,180);
|
||
|
break;
|
||
|
}
|
||
|
m_contents->setTextColor(color);
|
||
|
m_contents->append(message);
|
||
|
QTextCursor c = m_contents->textCursor();
|
||
|
c.movePosition(QTextCursor::End);
|
||
|
m_contents->setTextCursor(c);
|
||
|
m_contents->ensureCursorVisible();
|
||
|
}
|
||
|
|
||
|
void ServerWidget::show() {
|
||
|
if (isVisible()) {
|
||
|
raise();
|
||
|
} else {
|
||
|
QDesktopWidget *desktop = QApplication::desktop();
|
||
|
move((desktop->width() - width())/2,
|
||
|
(desktop->height() - height())/2);
|
||
|
QMainWindow::show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ServerWidget::closeEvent(QCloseEvent *event) {
|
||
|
QMainWindow::closeEvent(event);
|
||
|
m_thread->shutdown();
|
||
|
event->accept();
|
||
|
emit closed();
|
||
|
}
|
||
|
|