/*************************************************************************** socket.cpp - description ------------------- begin : Sun Mar 10 2002 copyright : (C) 2002 by Vladimir Shutoff email : vovan@shutoff.ru ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include "socket.h" #include "misc.h" #include "log.h" namespace SIM { using namespace std; #ifndef INADDR_NONE #define INADDR_NONE 0xFFFFFFFF #endif const unsigned RECONNECT_TIME = 5; const unsigned RECONNECT_IFINACTIVE = 60; const unsigned LOGIN_TIMEOUT = 120; struct SocketFactoryPrivate { bool m_bActive; QValueList errSockets; QValueList errSocketsCopy; QValueList removedSockets; QValueList removedServerSockets; SocketFactoryPrivate() : m_bActive(true) {} }; void Socket::error(const QString &err_text, unsigned code) { if (notify) notify->error_state(err_text, code); } ServerSocket::ServerSocket() { notify = NULL; } ClientSocket::ClientSocket(ClientSocketNotify *notify, Socket *sock) { m_notify = notify; bRawMode = false; bClosed = false; m_sock = sock; if (m_sock == NULL) m_sock = getSocketFactory()->createSocket(); m_sock->setNotify(this); } ClientSocket::~ClientSocket() { getSocketFactory()->erase(this); delete m_sock; } void ClientSocket::close() { m_sock->close(); bClosed = true; } const QString &ClientSocket::errorString() const { return errString; } void ClientSocket::connect(const QString &host, unsigned short port, TCPClient *client) { if (client){ EventSocketConnect e(this, client, host, port); e.process(); } m_sock->connect(host, port); } void ClientSocket::write() { if (writeBuffer().size() == 0) return; m_sock->write(writeBuffer().data(), writeBuffer().size()); writeBuffer().init(0); } bool ClientSocket::created() { return (m_sock != NULL); } void ClientSocket::resolve_ready(unsigned long ip) { m_notify->resolve_ready(ip); } void ClientSocket::connect_ready() { m_notify->connect_ready(); bClosed = false; } void ClientSocket::setRaw(bool mode) { bRawMode = mode; read_ready(); } void ClientSocket::read_ready() { if (bRawMode){ for (;;){ char b[2048]; int readn = m_sock->read(b, sizeof(b)); if (readn < 0){ error_state(I18N_NOOP("Read socket error")); return; } if (readn == 0) break; unsigned pos = readBuffer().writePos(); readBuffer().setWritePos(readBuffer().writePos() + readn); memcpy(readBuffer().data(pos), b, readn); } if (m_notify) m_notify->packet_ready(); return; } for (;;){ if (bClosed || errString.length()) break; int readn = m_sock->read(readBuffer().data(readBuffer().writePos()), readBuffer().size() - readBuffer().writePos()); if (readn < 0){ error_state(I18N_NOOP("Read socket error")); return; } if (readn == 0) break; readBuffer().setWritePos(readBuffer().writePos() + readn); if (readBuffer().writePos() < readBuffer().size()) break; if (m_notify) m_notify->packet_ready(); } } void ClientSocket::write_ready() { if (m_notify) m_notify->write_ready(); } unsigned long ClientSocket::localHost() { return m_sock->localHost(); } void ClientSocket::pause(unsigned n) { m_sock->pause(n); } void ClientSocket::setSocket(Socket *s, bool bClearError) { if (m_sock){ if (m_sock->getNotify() == this) m_sock->setNotify(NULL); if (bClearError){ getSocketFactory()->erase(this); } } m_sock = s; if (s) s->setNotify(this); } void ClientSocket::error_state(const QString &err, unsigned code) { // -> false -> already there if(!getSocketFactory()->add(this)) return; errString = err; errCode = code; QTimer::singleShot(0, getSocketFactory(), SLOT(idle())); } SocketFactory::SocketFactory(QObject *parent) : QObject(parent) { d = new SocketFactoryPrivate; } SocketFactory::~SocketFactory() { idle(); delete d; } bool SocketFactory::isActive() const { return d->m_bActive; } void SocketFactory::setActive(bool isActive) { if (isActive == d->m_bActive) return; d->m_bActive = isActive; EventSocketActive(d->m_bActive).process(); } void SocketFactory::remove(Socket *s) { s->setNotify(NULL); s->close(); if(d->removedSockets.contains(s)) return; d->removedSockets.push_back(s); QTimer::singleShot(0, this, SLOT(idle())); } void SocketFactory::remove(ServerSocket *s) { s->setNotify(NULL); s->close(); if(d->removedServerSockets.contains(s)) return; d->removedServerSockets.push_back(s); QTimer::singleShot(0, this, SLOT(idle())); } bool SocketFactory::add(ClientSocket *s) { if(!d->errSockets.contains(s)) { d->errSockets += s; return true; } return false; } bool SocketFactory::erase(ClientSocket *s) { QValueList::iterator it = d->errSocketsCopy.find(s); if(it != d->errSocketsCopy.end()) *it = NULL; return(d->errSockets.remove(s) > 0); } void SocketFactory::idle() { d->errSocketsCopy = d->errSockets; // important! error_state() modifes d->errSockets d->errSockets.clear(); QValueList::iterator it = d->errSocketsCopy.begin(); for ( ; it != d->errSocketsCopy.end(); ++it){ ClientSocket *s = *it; // can be removed in SocketFactory::erase(); if(!s) continue; ClientSocketNotify *n = s->m_notify; if (n){ QString errString = s->errorString(); s->errString = QString::null; if (n->error_state(errString, s->errCode)) delete n; } } QValueList::iterator its = d->removedSockets.begin(); for ( ; its != d->removedSockets.end(); ++its) delete *its; d->removedSockets.clear(); QValueList::iterator itss = d->removedServerSockets.begin(); for ( ; itss != d->removedServerSockets.end(); ++itss) delete *itss; d->removedServerSockets.clear(); } TCPClient::TCPClient(Protocol *protocol, Buffer *cfg, unsigned priority) : Client(protocol, cfg), EventReceiver(priority) { m_clientSocket = NULL; m_ip = 0; m_timer = new QTimer(this); m_loginTimer = new QTimer(this); m_reconnect = RECONNECT_TIME; m_bWaitReconnect = false; connect(m_timer, SIGNAL(timeout()), this, SLOT(reconnect())); connect(m_loginTimer, SIGNAL(timeout()), this, SLOT(loginTimeout())); } bool TCPClient::processEvent(Event *e) { if (e->type() == eEventSocketActive){ EventSocketActive *s = static_cast(e); if (m_bWaitReconnect && s->active()) reconnect(); } return false; } void TCPClient::resolve_ready(unsigned long ip) { m_ip = ip; } bool TCPClient::error_state(const QString &err, unsigned code) { log(L_DEBUG, "Socket error %s (%u)", err.local8Bit().data(), code); m_loginTimer->stop(); if (m_reconnect == NO_RECONNECT){ m_timer->stop(); setStatus(STATUS_OFFLINE, getCommonStatus()); setState(Error, err, code); return false; } if (!m_timer->isActive()){ unsigned reconnectTime = m_reconnect; if (!getSocketFactory()->isActive()){ if (reconnectTime < RECONNECT_IFINACTIVE) reconnectTime = RECONNECT_IFINACTIVE; } setClientStatus(STATUS_OFFLINE); setState((m_reconnect == NO_RECONNECT) ? Error : Connecting, err, code); m_bWaitReconnect = true; log(L_DEBUG, "Wait reconnect %u sec", reconnectTime); m_timer->start(reconnectTime * 1000); } else { /* slot reconnect() neeeds this flag to be true to make actual reconnect, but it was somehow false. serzh. */ m_bWaitReconnect = true; } return false; } void TCPClient::reconnect() { m_timer->stop(); if (m_bWaitReconnect) setClientStatus(getManualStatus()); } void TCPClient::setStatus(unsigned status, bool bCommon) { setClientStatus(status); Client::setStatus(status, bCommon); } void TCPClient::connect_ready() { m_timer->stop(); m_bWaitReconnect = false; m_loginTimer->stop(); m_loginTimer->start(LOGIN_TIMEOUT * 1000, true); } void TCPClient::loginTimeout() { m_loginTimer->stop(); if ((m_state != Connected) && socket()) socket()->error_state(I18N_NOOP("Login timeout")); } Socket *TCPClient::createSocket() { return NULL; } void TCPClient::socketConnect() { if (socket()) socket()->close(); if (socket() == NULL) m_clientSocket = createClientSocket(); log(L_DEBUG, "Start connect %s:%u", static_cast(getServer().local8Bit()), getPort()); socket()->connect(getServer(), getPort(), this); } ClientSocket *TCPClient::createClientSocket() { return new ClientSocket(this, createSocket()); } void TCPClient::setClientStatus(unsigned status) { if (status != STATUS_OFFLINE){ if (getState() == Connected){ setStatus(status); return; } m_logonStatus = status; if ((getState() != Connecting) || m_bWaitReconnect){ setState(Connecting, NULL); m_reconnect = RECONNECT_TIME; m_bWaitReconnect = false; setState(Connecting); socketConnect(); } return; } m_bWaitReconnect = false; m_timer->stop(); m_loginTimer->stop(); if (socket()) setStatus(STATUS_OFFLINE); m_status = STATUS_OFFLINE; setState(Offline); disconnected(); if (socket()){ socket()->close(); delete socket(); m_clientSocket = NULL; } } ServerSocketNotify::ServerSocketNotify() { m_listener = NULL; } ServerSocketNotify::~ServerSocketNotify() { if (m_listener) getSocketFactory()->remove(m_listener); } void ServerSocketNotify::setListener(ServerSocket *listener) { if (m_listener) getSocketFactory()->remove(m_listener); m_listener = listener; if (m_listener) m_listener->setNotify(this); } void ServerSocketNotify::bind(unsigned short minPort, unsigned short maxPort, TCPClient *client) { if (m_listener) getSocketFactory()->remove(m_listener); m_listener = getSocketFactory()->createServerSocket(); m_listener->setNotify(this); m_listener->bind(minPort, maxPort, client); } #ifndef WIN32 void ServerSocketNotify::bind(const char *path) { if (m_listener) getSocketFactory()->remove(m_listener); m_listener = getSocketFactory()->createServerSocket(); m_listener->setNotify(this); m_listener->bind(path); } #endif } #ifndef NO_MOC_INCLUDES #include "socket.moc" #endif