text: /* +------------------------------------------------------------------------+
     |                     Mobile Robot Programming Toolkit (MRPT)            |
     |                          https://www.mrpt.org/                         |
     |                                                                        |
     | Copyright (c) 2005-2022, Individual contributors, see AUTHORS file     |
     | See: https://www.mrpt.org/Authors - All rights reserved.               |
     | Released under BSD License. See: https://www.mrpt.org/License          |
     +------------------------------------------------------------------------+ */
  
  #include "comms-precomp.h"	// Precompiled headers
  //
  #include <mrpt/comms/CClientTCPSocket.h>
  #include <mrpt/comms/CServerTCPSocket.h>
  #include <mrpt/comms/net_utils.h>
  #include <mrpt/core/exceptions.h>
  #include <mrpt/system/os.h>
  
  #include <cstdio>  // stderr
  
  using namespace mrpt::comms;
  
  #if defined(MRPT_OS_LINUX) || defined(__APPLE__)
  #define INVALID_SOCKET (-1)
  
  #include <arpa/inet.h>
  #include <fcntl.h>
  #include <netdb.h>
  #include <netinet/in.h>
  #include <sys/ioctl.h>
  #include <sys/socket.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #include <cerrno>
  #endif
  
  #ifdef _WIN32
  #include <winsock.h>
  using socklen_t = int;
  #endif
  
  std::string CServerTCPSocket::getLastErrorStr()
  {
  	return mrpt::comms::net::getLastSocketErrorStr();
  }
  
  void CServerTCPSocket::setupSocket(
  	unsigned short listenPort, const std::string& IPaddress,
  	int maxConnectionsWaiting)
  {
  	MRPT_START
  
  	// Create the socket:
  	// ----------------------------
  	m_serverSock = socket(AF_INET, SOCK_STREAM, 0);
  	if (INVALID_SOCKET == m_serverSock) THROW_EXCEPTION(getLastErrorStr());
  
  	// Bind it:
  	// ----------------------------
  	sockaddr_in desiredIP;
  
  	desiredIP.sin_family = AF_INET;
  	desiredIP.sin_addr.s_addr = inet_addr(IPaddress.c_str());
  	desiredIP.sin_port = htons((unsigned short)listenPort);
  
  	if (INVALID_SOCKET ==
  		::bind(m_serverSock, (struct sockaddr*)(&desiredIP), sizeof(desiredIP)))
  		THROW_EXCEPTION(getLastErrorStr());
  
  	// Put in listen mode:
  	// ----------------------------
  	if (INVALID_SOCKET == listen(m_serverSock, maxConnectionsWaiting))
  		THROW_EXCEPTION(getLastErrorStr());
  
  	MRPT_LOG_DEBUG(format(
  		"[CServerTCPSocket] Listening at %s:%i\n", IPaddress.c_str(),
  		listenPort));
  
  	MRPT_END
  }
  
  /*---------------------------------------------------------------
  					isListening
   ---------------------------------------------------------------*/
  bool CServerTCPSocket::isListening() { return INVALID_SOCKET != m_serverSock; }
  /*---------------------------------------------------------------
  					accept
   ---------------------------------------------------------------*/
  std::unique_ptr<CClientTCPSocket> CServerTCPSocket::accept(int timeout_ms)
  {
  	MRPT_START
  
  	if (m_serverSock == INVALID_SOCKET) return nullptr;
  
  	struct timeval timeoutSelect;
  	struct timeval* ptrTimeout;
  	fd_set sockArr;
  
  	// Init fd_set structure & add our socket to it:
  	FD_ZERO(&sockArr);
  	FD_SET(m_serverSock, &sockArr);
  
  	// The timeout:
  	if (timeout_ms < 0) { ptrTimeout = nullptr; }
  	else
  	{
  		timeoutSelect.tv_sec = timeout_ms / 1000;
  		timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
  		ptrTimeout = &timeoutSelect;
  	}
  
  	// Wait for READ flag (meaning incoming connections):
  	MRPT_LOG_DEBUG("[CServerTCPSocket::accept] Waiting incoming connections");
  
  	int selRet = ::select(
  		m_serverSock + 1,  // __nfds
  		&sockArr,  // Wait for read
  		nullptr,  // Wait for write
  		nullptr,  // Wait for except.
  		ptrTimeout);  // Timeout
  
  	if (selRet == INVALID_SOCKET)
  	{
  		fprintf(stderr, "%s\n", getLastErrorStr().c_str());
  		return nullptr;
  	}
  
  	if (selRet == 0)
  	{
  		MRPT_LOG_WARN(
  			"[CServerTCPSocket::accept] Timeout waiting incoming "
  			"connections\n");
  
  		// Timeout:
  		return nullptr;
  	}
  	else
  	{
  		MRPT_LOG_DEBUG(
  			"[CServerTCPSocket::accept] Incoming connection accepted\n");
  
  		// We have a new connection:
  
  		sockaddr_in otherPart;
  		socklen_t otherPartSize = sizeof(otherPart);
  
  		int aceptdSock = ::accept(
  			m_serverSock, (struct sockaddr*)&otherPart, &otherPartSize);
  
  		if (aceptdSock == INVALID_SOCKET)
  		{
  			MRPT_LOG_ERROR_FMT("%s\n", getLastErrorStr().c_str());
  			return std::unique_ptr<CClientTCPSocket>();
  		}
  
  		auto ret = std::make_unique<CClientTCPSocket>();
  
  		ret->m_hSock = aceptdSock;
  #if defined(MRPT_OS_LINUX)
  		ret->internal_attach_epoll_to_hsock();
  #endif
  
  		ret->m_remotePartIP = std::string(inet_ntoa(otherPart.sin_addr));
  		ret->m_remotePartPort = ntohs(otherPart.sin_port);
  
  		MRPT_LOG_DEBUG_FMT(
  			"[CServerTCPSocket::accept] Connection accepted from %s:%u\n",
  			ret->m_remotePartIP.c_str(), ret->m_remotePartPort);
  
  		return ret;
  	}
  
  	MRPT_END
  }
  