#include #include #include #include #include #include #include "server/network/Socket.h" Socket::Socket() : socketId(-1) { // domain - AF_INET - IPv4 Internet protocols // type - SOCK_STREAM - two-way, connection-based byte streams // protocol - 0 - use standard protocol for the given socket type socketId = socket(AF_INET, SOCK_STREAM, 0); if(socketId == -1) { std::cout << "cannot create socket: " << strerror(errno) << "\n"; } } Socket::~Socket() { if(socketId == -1) { return; } if(close(socketId) == -1) { std::cout << "cannot close socket: " << strerror(errno) << "\n"; } } bool Socket::hasError() const { return socketId == -1; } bool Socket::setNonLinger() { // prevents clients from blocking the port if the server exits // this is useful if server and client run on the same system struct linger sl; sl.l_onoff = 1; // nonzero to linger on close sl.l_linger = 0; // time to linger // sockfd - listenerSocket - modified socket // level - SOL_SOCKET - manipulate options at the sockets API level // optname - SO_LINGER - identifier of the option if(setsockopt(socketId, SOL_SOCKET, SO_LINGER, &sl, sizeof (struct linger)) == -1) { std::cout << "cannot set non lingering: " << strerror(errno) << "\n"; return true; } return false; } bool Socket::listenOnPort(u16 port, uint queueLength) const { // specify binding data struct sockaddr_in connectSocketData; // clear padding memset(&connectSocketData, 0, sizeof (struct sockaddr_in)); // IPv4 Internet protocols connectSocketData.sin_family = AF_INET; // port in network byte order connectSocketData.sin_port = htons(port); // address in network byte order, accept any incoming messages connectSocketData.sin_addr.s_addr = htons(INADDR_ANY); // bind the socket if(bind(socketId, (struct sockaddr*) &connectSocketData, sizeof (struct sockaddr_in)) == -1) { std::cout << "cannot bind socket: " << strerror(errno) << "\n"; return true; } // mark the socket as handler for connection requests // backlog - queueLength - max queue length of pending connections if(listen(socketId, queueLength) == -1) { std::cout << "cannot start listening on socket: " << strerror(errno) << "\n"; return true; } return false; } WaitResult Socket::waitForConnection(uint timeoutMillis) const { // wait until a connection arrives with timeout, this prevents being stuck in accept struct pollfd fds; fds.fd = socketId; // file descriptor for polling fds.events = POLLIN; // wait until data is ready to read fds.revents = 0; // return events - none // nfds_t - 1 - amount of passed in structs // timeout - timeoutMillis - milliseconds to wait until an event occurs int result = poll(&fds, 1, timeoutMillis); if(result > 0) { return WaitResult::SUCCESS; } else if(result == 0) { return WaitResult::TIMEOUT; } std::cout << "poll error: " << strerror(errno) << "\n"; return WaitResult::ERROR; } int Socket::acceptConnection() const { struct sockaddr_in clientSocketData; // accepts an incoming client connection and stores the data in the given struct socklen_t addrlen = sizeof (struct sockaddr_in); int clientSocket = accept(socketId, (struct sockaddr*) &clientSocketData, &addrlen); if(clientSocket >= 0) { return clientSocket; } std::cout << "accept error: " << strerror(errno) << "\n"; return -1; }