int CSocket::sendData(CString& data)
{
	int intError = 0;
	int size = 0;

	// Make sure the socket is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED)
		return SOCKET_INVALID;

	do
	{
		// See if we can send data.
		// If we can't, return how many bytes we did send.
		fd_set set;
		struct timeval tm;
		tm.tv_sec = tm.tv_usec = 0;
		FD_ZERO(&set);
		FD_SET(properties.handle, &set);
		select(properties.handle + 1, 0, &set, 0, &tm);
		if (!FD_ISSET(properties.handle, &set))
			return size;

		// Send our data, yay!
		int sent = 0;
		if ((sent = ::send(properties.handle, data.text(), data.length(), 0)) == SOCKET_ERROR)
		{
			intError = identifyError();
			switch (intError)
			{
				case ENETDOWN:
				case ENETRESET:
				case ENOTCONN:
				case EHOSTUNREACH:
				case ECONNABORTED:
				case ECONNRESET:
				case ETIMEDOUT:
					// Destroy the bad socket and create a new one.
					disconnect();
					return intError;
					break;
			}
			if (intError == EAGAIN || intError == EWOULDBLOCK || intError == EINPROGRESS) return size;
			disconnect();
			return intError;
		}

		// Remove what we sent.
		// Increase size by how much we sent.
		if (sent >= data.length())
			data.clear();
		else if (sent > 0)
			data.removeI(0, sent);
		size += sent;

	// Repeat while data is still left.
	} while (data.length() > 0 && intError == 0);

	// Return how much data was ultimately sent.
	return size;
}
char* CSocket::getData(unsigned int* dsize)
{
	int size = 0;
	int intError = 0;

	// Create the buffer.
	const int BUFFLEN = 0x8000;	// 32KB.
	static char buff[0x8000];	// 32KB.

	// Make sure it is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED || properties.handle == INVALID_SOCKET)
	{
		*dsize = 0;
		return 0;
	}

	// Allocate buff.
	memset((void*)buff, 0, BUFFLEN);

	// Get our data
	if (properties.protocol == SOCKET_PROTOCOL_UDP)
		size = recvfrom(properties.handle, buff, BUFFLEN, 0, 0, 0);
	else
		size = recv(properties.handle, buff, BUFFLEN, 0);

	// Check for error!
	if (size == SOCKET_ERROR)
	{
		size = 0;
		intError = identifyError();
		switch (intError)
		{
			case ENETDOWN:
			case ENETRESET:
			case ENOTCONN:
			case EHOSTUNREACH:
			case ECONNABORTED:
			case ECONNRESET:
			case ETIMEDOUT:
			case ESHUTDOWN:
				// Destroy the bad socket and create a new one.
				SLOG("%s - Connection lost!  Reason: %s\n", properties.description, errorMessage(intError));
				disconnect();
				break;
			default:
				break;
		}
	}

	// If size is 0, the socket was disconnected.
	if (size == 0)
		disconnect();

	// Set dsize to how much data was returned.
	*dsize = size;

	// Return the data.
	return buff;
}
CSocket* CSocket::accept()
{
	// Make sure the socket is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED)
		return 0;

	// Only server type TCP sockets can accept new connections.
	if (properties.type != SOCKET_TYPE_SERVER || properties.protocol != SOCKET_PROTOCOL_TCP)
		return 0;

	sockaddr_storage addr;
	int addrlen = sizeof(addr);
	SOCKET handle = 0;

	// Try to accept a new connection.
	handle = ::accept(properties.handle, (struct sockaddr*)&addr, (socklen_t*)&addrlen);
	if (handle == INVALID_SOCKET)
	{
		int error = identifyError();
		if (error == EWOULDBLOCK || error == EINPROGRESS) return 0;
		SLOG("[CSocket::accept] accept() returned error: %s\n", errorMessage(error));
		return 0;
	}

	// Create the new socket to store the new connection.
	CSocket* sock = new CSocket();
	sock_properties props;
	memset((void*)&props, 0, sizeof(sock_properties));
	memset((void*)&properties.address, 0, sizeof(struct sockaddr_storage));
	memcpy((void*)&props.address, &addr, sizeof(addr));
	props.protocol = properties.protocol;
	props.type = SOCKET_TYPE_CLIENT;
	props.state = SOCKET_STATE_CONNECTED;
	props.handle = handle;
	sock->setProperties(props);
	sock->setDescription(sock->getRemoteIp());

	// Disable the nagle algorithm.
	if (props.protocol == SOCKET_PROTOCOL_TCP)
	{
		int nagle = 1;
		setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, (char*)&nagle, sizeof(nagle));
	}

	// Set as non-blocking.
#if defined(_WIN32) || defined(_WIN64)
	u_long flags = 1;
	ioctlsocket(handle, FIONBIO, &flags);
#else
	int flags = fcntl(properties.handle, F_GETFL, 0);
	fcntl(handle, F_SETFL, flags | O_NONBLOCK);
#endif

	// Accept the connection by calling getsockopt.
	int type, typeSize = sizeof(int);
	getsockopt(handle, SOL_SOCKET, SO_TYPE, (char*)&type, (socklen_t*)&typeSize);

	return sock;
}
char* CSocket::peekData()
{
	//int recvsize = 0x10000;
	int recvsize = 0x2000;
	int intError;
	char *buff = 0;

	// Make sure it is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED)
		return 0;

	// Make a buffer to store data!
	buff = new char[ recvsize ];
	memset((char *)buff, 0, recvsize);

	// Get our data
	if (properties.protocol == SOCKET_PROTOCOL_UDP)
		intError = recvfrom(properties.handle, buff, recvsize, MSG_PEEK, 0, 0);
	else
		intError = recv(properties.handle, buff, recvsize, MSG_PEEK);

	// Check for error!
	if (intError == SOCKET_ERROR)
	{
		intError = identifyError();
		switch (intError)
		{
			case ENETDOWN:
			case ENETRESET:
			case ENOTCONN:
			case EHOSTUNREACH:
			case ECONNABORTED:
			case ECONNRESET:
			case ETIMEDOUT:
			case ESHUTDOWN:
				// Destroy the bad socket and create a new one.
				disconnect();
				break;
			default:
				break;
		}
		if (buff)
		{
			delete [] buff;
			buff = 0;
		}
		return 0;
	}

	// Return the data.
	return buff;
}
int CSocket::sendData(char* data, unsigned int* dsize)
{
	int intError = 0;

	// Make sure the socket is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED || properties.handle == INVALID_SOCKET)
	{
		*dsize = 0;
		return 0;
	}

	// Send our data, yay!
	int sent = 0;
	if ((sent = ::send(properties.handle, data, *dsize, MSG_NOSIGNAL)) == SOCKET_ERROR)
	{
		sent = 0;
		intError = identifyError();
		switch (intError)
		{
			case ENETDOWN:
			case ENETRESET:
			case ENOTCONN:
			case EHOSTUNREACH:
			case ECONNABORTED:
			case ECONNRESET:
			case ETIMEDOUT:
				// Destroy the bad socket and create a new one.
				SLOG("%s - Connection lost!  Reason: %s\n", properties.description, errorMessage(intError));
				disconnect();
				return 0;
				break;
			case EAGAIN:
				return 0;
				break;
		}
		disconnect();
		return 0;
	}

	// Remove what we sent from the total size.
	*dsize -= sent;

	// Return how much data was ultimately sent.
	return sent;
}
CSocket* CSocket::accept()
{
	// Make sure the socket is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED)
		return 0;

	// Only server type TCP sockets can accept new connections.
	if (properties.type != SOCKET_TYPE_SERVER || properties.protocol != SOCKET_PROTOCOL_TCP)
		return 0;

	sockaddr_storage addr;
	int addrlen = sizeof(addr);
	SOCKET handle = 0;

	// Try to accept a new connection.
	handle = ::accept(properties.handle, (struct sockaddr*)&addr, (socklen_t*)&addrlen);
	if (handle == INVALID_SOCKET)
	{
		int error = identifyError();
		if (error == EWOULDBLOCK || error == EINPROGRESS) return 0;
		return 0;
	}

	// Create the new socket to store the new connection.
	CSocket* sock = new CSocket();
	sock_properties props;
	memset((void*)&props, 0, sizeof(sock_properties));
	memset((void*)&properties.address, 0, sizeof(struct sockaddr_storage));
	memcpy((void*)&props.address, &addr, sizeof(addr));
	props.options = properties.options;
	props.protocol = properties.protocol;
	props.type = SOCKET_TYPE_CLIENT;
	props.state = SOCKET_STATE_CONNECTED;
	props.handle = handle;
	sock->setProperties(props);
	sock->setDescription(sock->tcpIp());

	// Accept the connection by calling getsockopt.
	int type, typeSize = sizeof(int);
	getsockopt(handle, SOL_SOCKET, SO_TYPE, (char*)&type, (socklen_t*)&typeSize);

	return sock;
}
void CSocket::socketSystemDestroy()
{
//	errorOut("debuglog.txt", ":: Destroying socket system...");
#if defined(_WIN32) || defined(_WIN64)
	int intTimeCheck = 0;

	while (intTimeCheck++ < 3)
	{
		if (WSACleanup() == SOCKET_ERROR)
			SLOG("[CSocket::socketSystemDestroy] WSACleanup() returned error: %s\n", errorMessage(identifyError()));
		sleep(1000);
	}
#endif
}
void CSocket::disconnect()
{
	// Shut down the socket.
	if (shutdown(properties.handle, SHUT_RDWR) == SOCKET_ERROR)
	{
		int error = identifyError();
		if (error == ENOTSOCK)
		{
			properties.handle = INVALID_SOCKET;
			properties.state = SOCKET_STATE_DISCONNECTED;
			return;
		}
		SLOG("[CSocket::destroy] shutdown returned error: %s\n", errorMessage(error));
	}

	// Mark socket as terminating.
	properties.state = SOCKET_STATE_TERMINATING;

	// Gracefully shut it down.
	/*
	if (properties.protocol == SOCKET_PROTOCOL_TCP)
	{
		int count = 0;
		char buff[ 0x2000 ];
		int size;
		while (++count < 3)
		{
			size = recv(properties.handle, buff, 0x2000, 0);
			if (size == 0) break;
			if (size == SOCKET_ERROR) break;
		}
	}
	*/

	// Destroy the socket of d00m.
#if defined(_WIN32) || defined(_WIN64)
	if (closesocket(properties.handle) == SOCKET_ERROR)
	{
		SLOG("[CSocket::destroy] closesocket ");
#else
	if (close(properties.handle) == SOCKET_ERROR)
	{
		SLOG("[CSocket::destroy] close ");
#endif
		SLOG("returned error: %s\n", errorMessage(identifyError()));
	}

	// Reset the socket state.
	properties.handle = INVALID_SOCKET;
	properties.state = SOCKET_STATE_DISCONNECTED;
}

int CSocket::reconnect(long delay, int tries)
{
	int socket_reconnect_delay = delay;
	int socket_reconnect_attempts = tries;

	if (delay == 0)
		delay = socket_reconnect_delay;
	if (tries == 0)
		tries = socket_reconnect_attempts;

	for (int i = 0; i < tries; i++)
	{
		switch (this->connect())
		{
			case SOCKET_OK:
			case SOCKET_ALREADY_CONNECTED:
				return SOCKET_OK;
				break;
			case SOCKET_INVALID:
			case SOCKET_BIND_ERROR:
			case SOCKET_CONNECT_ERROR:
			default:
				// Do nothing.
				break;
		}
		if (delay != 0) sleep(delay);
	}
	return SOCKET_CONNECT_ERROR;
}
int CSocket::connect()
{
	// Make sure the socket is disconnected.
	if (properties.state != SOCKET_STATE_DISCONNECTED)
		return SOCKET_ALREADY_CONNECTED;

	// Flag the socket as connecting.
	properties.state = SOCKET_STATE_CONNECTING;

	// Create socket.
	if (properties.protocol == SOCKET_PROTOCOL_TCP)
		properties.handle = socket(AF_INET, SOCK_STREAM, 0);
	else
		properties.handle = socket(AF_INET, SOCK_DGRAM, 0);

	// Make sure the socket was created correctly.
	if (properties.handle == INVALID_SOCKET)
	{
		SLOG("[CSocket::connect] socket() returned INVALID_SOCKET.\n");
		properties.state = SOCKET_STATE_DISCONNECTED;
		return SOCKET_INVALID;
	}

	// Bind the socket if it is a server-type socket.
	if (properties.type == SOCKET_TYPE_SERVER)
	{
		// Let us reuse the address.  Freaking bind.
		int value = 1;
		setsockopt(properties.handle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value));

		// Bind the socket.
		if (::bind(properties.handle, (struct sockaddr *)&properties.address, sizeof(properties.address)) == SOCKET_ERROR)
		{
			SLOG("[CSocket::connect] bind() returned error: %s\n", errorMessage(identifyError()));
			disconnect();
			return SOCKET_BIND_ERROR;
		}
	}

	// Connect the socket.
	if (properties.type != SOCKET_TYPE_SERVER)
	{
		if (::connect(properties.handle, (struct sockaddr *)&properties.address, sizeof(properties.address)) == SOCKET_ERROR)
		{
			SLOG("[CSocket::connect] connect() returned error: %s\n", errorMessage(identifyError()));
			disconnect();
			return SOCKET_CONNECT_ERROR;
		}
	}

	// Disable the nagle algorithm.
	if (properties.protocol == SOCKET_PROTOCOL_TCP)
	{
		int nagle = 1;
		setsockopt(properties.handle, IPPROTO_TCP, TCP_NODELAY, (char*)&nagle, sizeof(nagle));
	}

	// Set as non-blocking.
#if defined(_WIN32) || defined(_WIN64)
	u_long flags = 1;
	ioctlsocket(properties.handle, FIONBIO, &flags);
#else
	int flags = fcntl(properties.handle, F_GETFL, 0);
	fcntl(properties.handle, F_SETFL, flags | O_NONBLOCK);
#endif

	// Socket connected!
	properties.state = SOCKET_STATE_CONNECTED;

	// Listening sockets.
	if (properties.type == SOCKET_TYPE_SERVER)
	{
		if (properties.protocol == SOCKET_PROTOCOL_UDP)
			properties.state = SOCKET_STATE_LISTENING;
		else if (properties.protocol == SOCKET_PROTOCOL_TCP)
		{
			if (::listen(properties.handle, SOMAXCONN) == SOCKET_ERROR)
			{
				SLOG("[CSocket::connect] listen() returned error: %s\n", errorMessage(identifyError()));
				disconnect();
				return SOCKET_CONNECT_ERROR;
			}

			properties.state = SOCKET_STATE_LISTENING;
		}
	}
	return SOCKET_OK;
}
Exemple #10
0
int CSocket::getData()
{
	int size = 0;
	int intError = 0;
	//char buff[ 0x10000 ]; // 65536 bytes, 64KB
	char buff[ 0x2000 ]; // 8192 bytes, 8KB
	int bufflen = 0x2000;
	CString temp;

	// Make sure it is connected!
	if (properties.state == SOCKET_STATE_DISCONNECTED)
		return SOCKET_ERROR;

	do
	{
		// Make sure there is data to be read.
		// If size == bufflen, that means there may be more data.  Just in case,
		// call select so blocking sockets don't block.
		if (size == 0 || size == bufflen)
		{
			fd_set set;
			struct timeval tm;
			tm.tv_sec = tm.tv_usec = 0;
			FD_ZERO(&set);
			FD_SET(properties.handle, &set);
			select(properties.handle + 1, &set, 0, 0, &tm);
			if (!FD_ISSET(properties.handle, &set))
				return temp.length();
		}

		// Allocate buff.
		memset((void*)buff, 0, bufflen);

		// Get our data
		if (properties.protocol == SOCKET_PROTOCOL_UDP)
			size = recvfrom(properties.handle, buff, bufflen, 0, 0, 0);
		else
			size = recv(properties.handle, buff, bufflen, 0);

		// Add to the buffer.
		if (size > 0)
			temp.write(buff, size);

		// Check for error!
		if (size == SOCKET_ERROR)
		{
			intError = identifyError();
			switch (intError)
			{
				case ENETDOWN:
				case ENETRESET:
				case ENOTCONN:
				case EHOSTUNREACH:
				case ECONNABORTED:
				case ECONNRESET:
				case ETIMEDOUT:
				case ESHUTDOWN:
					// Destroy the bad socket and create a new one.
					disconnect();
					break;
				default:
					break;
			}
		}
	} while (size > 0 && intError == 0);

	// If size is 0, the socket was disconnected.
	if (size == 0)
		disconnect();

	// Add the data we just got to the buffer.
	if (temp.length() > 0)
		buffer.write(temp.text(), temp.length());

	// Return the amount of data obtained.
	return temp.length();
}