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? */ }
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; }