Esempio n. 1
0
File: net.cpp Progetto: ufoai/ufoai
static void do_accept (SOCKET sock)
{
	const int index = NET_StreamGetFree();
	if (index == -1) {
		Com_Printf("Too many streams open, rejecting inbound connection\n");
		netCloseSocket(sock);
		return;
	}

	struct net_stream* s = NET_StreamNew(index);
	s->socket = sock;
	s->inbound = dbufferptr(new dbuffer(4096));
	s->outbound = dbufferptr(new dbuffer(4096));
	s->family = server_family;
	s->addrlen = server_addrlen;
	s->func = server_func;

	maxfd = std::max(sock + 1, maxfd);
	FD_SET(sock, &read_fds);

	server_func(s);
	/** @todo close stream? */
}
Esempio n. 2
0
static struct net_stream* NET_DoConnect (const char* node, const char* service, const struct addrinfo* addr, int i, stream_onclose_func* onclose)
{
	struct net_stream* s;
	SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
	if (sock == INVALID_SOCKET) {
		Com_Printf("Failed to create socket: %s\n", netStringError(netError));
		return nullptr;
	}

	if (!NET_SocketSetNonBlocking(sock)) {
		netCloseSocket(sock);
		return nullptr;
	}

	if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
		const int err = netError;
#ifdef _WIN32
		if (err != WSAEWOULDBLOCK) {
#else
		if (err != EINPROGRESS) {
#endif
			Com_Printf("Failed to start connection to %s:%s: %s\n", node, service, netStringError(err));
			netCloseSocket(sock);
			return nullptr;
		}
	}

	s = NET_StreamNew(i);
	s->socket = sock;
	s->inbound = dbufferptr(new dbuffer(4096));
	s->outbound = dbufferptr(new dbuffer(4096));
	s->family = addr->ai_family;
	s->addrlen = addr->ai_addrlen;
	s->onclose = onclose;

	maxfd = std::max(sock + 1, maxfd);
	FD_SET(sock, &read_fds);

	return s;
}

/**
 * @brief Try to connect to a given host on a given port
 * @param[in] node The host to connect to
 * @param[in] service The port to connect to
 * @param[in] onclose The callback that is called on closing the returned stream. This is useful if
 * you hold the pointer for the returned stream anywhere else and would like to get notified once
 * this pointer is invalid.
 * @sa NET_DoConnect
 * @sa NET_ConnectToLoopBack
 * @todo What about a timeout
 */
struct net_stream* NET_Connect (const char* node, const char* service, stream_onclose_func* onclose)
{
	struct addrinfo* res;
	struct addrinfo hints;
	int rc;
	struct net_stream* s = nullptr;
	int index;

	OBJZERO(hints);
	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
	hints.ai_socktype = SOCK_STREAM;
	/* force ipv4 */
	if (net_ipv4->integer)
		hints.ai_family = AF_INET;

	rc = getaddrinfo(node, service, &hints, &res);
	if (rc != 0) {
		Com_Printf("Failed to resolve host %s:%s: %s\n", node, service, gai_strerror(rc));
		return nullptr;
	}

	index = NET_StreamGetFree();
	if (index == -1) {
		Com_Printf("Failed to connect to host %s:%s, too many streams open\n", node, service);
		freeaddrinfo(res);
		return nullptr;
	}

	s = NET_DoConnect(node, service, res, index, onclose);

	freeaddrinfo(res);
	return s;
}

/**
 * @param[in] onclose The callback that is called on closing the returned stream. This is useful if
 * you hold the pointer for the returned stream anywhere else and would like to get notified once
 * this pointer is invalid.
 * @sa NET_Connect
 */
struct net_stream* NET_ConnectToLoopBack (stream_onclose_func* onclose)
{
	struct net_stream* client, *server;
	int server_index, client_index;

	if (!server_running)
		return nullptr;

	server_index = NET_StreamGetFree();
	client_index = NET_StreamGetFree();

	if (server_index == -1 || client_index == -1 || server_index == client_index) {
		Com_Printf("Failed to connect to loopback server, too many streams open\n");
		return nullptr;
	}

	client = NET_StreamNew(client_index);
	client->loopback = true;
	client->inbound = dbufferptr(new dbuffer(4096));
	client->outbound = dbufferptr(new dbuffer(4096));
	client->onclose = onclose;

	server = NET_StreamNew(server_index);
	server->loopback = true;
	server->inbound = client->outbound;
	server->outbound = client->inbound;
	server->func = server_func;
	server->onclose = nullptr;

	client->loopback_peer = server;
	server->loopback_peer = client;

	server_func(server);

	return client;
}

/**
 * @brief Enqueue a network message into a stream
 * @sa NET_StreamDequeue
 */
void NET_StreamEnqueue (struct net_stream* s, const char* data, int len)
{
	if (len <= 0 || !s || s->closed || s->finished)
		return;

	if (s->outbound) {
		const ScopedMutex scopedMutex(netMutex);
		s->outbound->add(data, len);
	}

	/* on linux, socket is int, and INVALID_SOCKET -1
	 * on windows it is unsigned and  INVALID_SOCKET (~0)
	 * Let's hope that checking for INVALID_SOCKET is good enough for linux. */
	//if (s->socket >= 0)
	if (s->socket != INVALID_SOCKET)
		FD_SET(s->socket, &write_fds);

	if (s->loopback_peer) {
		loopback_ready = true;
		s->loopback_peer->ready = true;
	}
}

/**
 * @brief Returns the length of the waiting inbound buffer
 */
static int NET_StreamPeek (struct net_stream* s, char* data, int len)
{
	if (len <= 0 || !s)
		return 0;

	dbufferptr& dbuf = s->inbound;
	if ((s->closed || s->finished) && dbuffer_len(dbuf) == 0)
		return 0;

	return dbuf->get(data, len);
}

/**
 * @sa NET_StreamEnqueue
 */
int NET_StreamDequeue (struct net_stream* s, char* data, int len)
{
	if (len <= 0 || !s || s->finished)
		return 0;

	return s->inbound->extract(data, len);
}

/**
 * @brief Reads messages from the network channel and adds them to the dbuffer
 * where you can use the NET_Read* functions to get the values in the correct
 * order
 * @sa NET_StreamDequeue
 */
dbuffer* NET_ReadMsg (struct net_stream* s)
{
	unsigned int v;
	const ScopedMutex scopedMutex(netMutex);

	if (NET_StreamPeek(s, (char*)&v, 4) < 4)
		return nullptr;

	int len = LittleLong(v);
	if (NET_StreamGetLength(s) < (4 + len))
		return nullptr;

	char tmp[256];
	const int size = sizeof(tmp);
	NET_StreamDequeue(s, tmp, 4);

	dbuffer* buf = new dbuffer();
	while (len > 0) {
		const int x = NET_StreamDequeue(s, tmp, std::min(len, size));
		buf->add(tmp, x);
		len -= x;
	}

	return buf;
}