Blob Blame Raw

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cerrno>

#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>


#include "socket.h"



static void addressToInaddr(const Address &address, struct sockaddr_in &addr) {
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = address.ipUInt;
	addr.sin_port = htons(address.port);
}

static bool configureTcpSocket(int sockId) {
	int tcp_nodelay = 1;
	return 0 == setsockopt(sockId, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay, sizeof(int))
	    && 0 == fcntl(sockId, F_SETFL, fcntl(sockId, F_GETFL, 0) | O_NONBLOCK);
}



Poll::Poll() { }
Poll::~Poll() { }

bool Poll::wait(Time duration) {
	const size_t count = list.size();
	const size_t size = sizeof(struct pollfd) * count;
	if (data.size() < size)
		data.resize(size);
	
	int mills = (int)((duration + rand()%1000)/1000);
	if (duration > 10 && mills == 0) mills = 1;
	int res;

	if (size) {
		memset(data.data(), 0, size);
		struct pollfd *pfdFirst = (struct pollfd *)data.data();
		struct pollfd *pfd = pfdFirst;
		for(List::const_iterator i = list.begin(); i != list.end(); ++i, ++pfd) {
			pfd->fd = i->sockId;
			pfd->events = POLLRDHUP;
			if (i->wantRead) pfd->events |= POLLIN;
			if (i->wantWrite) pfd->events |= POLLOUT;
		}
		
		res = poll(pfdFirst, count, mills);
		
		if (res > 0) {
			struct pollfd *pfd = pfdFirst;
			for(List::iterator i = list.begin(); i != list.end(); ++i, ++pfd) {
				unsigned int e = pfd->revents;
				i->canRead  = (bool)(e & POLLIN);
				i->canWrite = (bool)(e & POLLOUT);
				i->closed   = (bool)(e & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL));
			}
		}
	} else {
		res = poll(NULL, 0, mills);
	}
	
	return res >= 0;
}



int Socket::tcpConnect(const Address &address) {
	int sockId = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sockId < 0) return -1;
	struct sockaddr_in addr;
	addressToInaddr(address, addr);
	if ( 0 != ::connect(sockId, (struct sockaddr*)&addr, sizeof(addr))
	  || !configureTcpSocket(sockId) )
		{ ::close(sockId); return -1; }
	return sockId;
}


int Socket::tcpListen(const Address &address) {
	int sockId = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sockId < 0) return -1;
	struct sockaddr_in addr;
	addressToInaddr(address, addr);
	if ( 0 != ::bind(sockId, (struct sockaddr*)&addr, sizeof(addr))
	  || 0 != ::listen(sockId, 16)
	  || !configureTcpSocket(sockId) )
		{ ::close(sockId); return -1; }
	return sockId;
}


int Socket::tcpAccept(int sockId, Address &address) {
	address = Address();
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	socklen_t len = sizeof(addr);
	int newSockId = ::accept(sockId, (struct sockaddr*)&addr, &len);
	if (newSockId < 0) return -1;
	if (!configureTcpSocket(newSockId))
		{ ::close(newSockId); return -1; }
	address.ipUInt = addr.sin_addr.s_addr;
	address.port = ntohs(addr.sin_port);
	return newSockId;
}


int Socket::tcpSendAsync(int sockId, const void *data, int size) {
	int res = ::send(sockId, data, size, MSG_DONTWAIT);
	return res > 0 ? res : 0;
}


int Socket::tcpRecvAsync(int sockId, void *data, int size) {
	int res = ::recv(sockId, data, size, MSG_DONTWAIT);
	return res > 0 ? res : 0;
}


int Socket::udpBind(const Address &address) {
	int sockId = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sockId < 0) return -1;
	
	struct sockaddr_in addr;
	addressToInaddr(address, addr);
	if (0 != ::bind(sockId, (struct sockaddr*)&addr, sizeof(addr))) {
		::close(sockId);
		return -1;
	}
	
	return sockId;
}


int Socket::udpSend(int sockId, const Address &address, const void *data, int size) {
	struct sockaddr_in addr;
	addressToInaddr(address, addr);
	int res = ::sendto(sockId, data, size, 0, (struct sockaddr*)&addr, sizeof(addr));
	return res > 0 ? res : 0;
}


int Socket::udpRecvAsync(int sockId, Address &address, void *data, int size) {
	address = Address();
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	socklen_t len = sizeof(addr);
	int res = ::recvfrom(sockId, data, size, MSG_DONTWAIT, (struct sockaddr*)&addr, &len);
	if (res <= 0) return 0;
	address.ipUInt = addr.sin_addr.s_addr;
	address.port = ntohs(addr.sin_port);
	return res;
}


void Socket::close(int sockId)
	{ ::close(sockId); }