📄 tcpio.c
字号:
/* This file is part of GNUnet. (C) 2001, 2002, 2006, 2008 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file util/network/tcpio.c * @brief code for synchronized access to TCP streams * @author Christian Grothoff * * Generic TCP code for reliable, mostly blocking, record-oriented TCP * connections. GNUnet uses the "tcpio" code for trusted client-server * (e.g. gnunet-gtk to gnunetd via loopback) communications. Note * that an unblocking write is also provided since if both client and * server use blocking IO, both may block on a write and cause a * mutual inter-process deadlock. * * Since we do not want other peers (!) to be able to block a peer by * not reading from the TCP stream, the peer-to-peer TCP transport * uses unreliable, buffered, non-blocking, record-oriented TCP code * with a select call to reduce the number of threads which is * provided in transports/tcp.c. */#include "gnunet_util_network.h"#include "gnunet_util_os.h"#include "gnunet_util_config.h"#include "gnunet_protocols.h"#include "platform.h"#define DEBUG_TCPIO GNUNET_NO/** * Struct to refer to a GNUnet TCP connection. * This is more than just a socket because if the server * drops the connection, the client automatically tries * to reconnect (and for that needs connection information). */typedef struct GNUNET_ClientServerConnection{ /** * the socket handle, NULL if not live */ struct GNUNET_SocketHandle *sock; struct GNUNET_Mutex *readlock; struct GNUNET_Mutex *writelock; struct GNUNET_Mutex *destroylock; struct GNUNET_GE_Context *ectx; struct GNUNET_GC_Configuration *cfg; int dead;} ClientServerConnection;/** * Return the port-number (in host byte order) * @return 0 on error */static unsigned shortgetGNUnetPort (struct GNUNET_GE_Context *ectx, struct GNUNET_GC_Configuration *cfg){ char *res; char *pos; unsigned int port; res = NULL; if (-1 == GNUNET_GC_get_configuration_value_string (cfg, "NETWORK", "HOST", "localhost:2087", &res)) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_BULK, _ ("Could not find valid value for HOST in section NETWORK.")); return 2087; } pos = strstr (res, ":"); if (pos == NULL) { GNUNET_free (res); return 2087; } pos++; if (1 != SSCANF (pos, "%u", &port)) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_BULK, _ ("Syntax error in configuration entry HOST in section NETWORK: `%s'"), pos); GNUNET_free (res); return 2087; } GNUNET_free (res); return (unsigned short) port;}/** * Configuration: get the GNUnetd host where the client * should connect to (via TCP) * * @return the name of the host, NULL on error */static char *getGNUnetdHost (struct GNUNET_GE_Context *ectx, struct GNUNET_GC_Configuration *cfg){ char *res; char *pos; res = NULL; if (-1 == GNUNET_GC_get_configuration_value_string (cfg, "NETWORK", "HOST", "localhost:2087", &res)) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_BULK, _ ("Could not find valid value for HOST in section NETWORK.")); return NULL; } pos = strstr (res, ":"); if (pos != NULL) *pos = '\0'; return res;}struct GNUNET_ClientServerConnection *GNUNET_client_connection_create (struct GNUNET_GE_Context *ectx, struct GNUNET_GC_Configuration *cfg){ ClientServerConnection *result; result = GNUNET_malloc (sizeof (ClientServerConnection)); result->sock = NULL; result->readlock = GNUNET_mutex_create (GNUNET_NO); result->writelock = GNUNET_mutex_create (GNUNET_NO); result->destroylock = GNUNET_mutex_create (GNUNET_YES); result->ectx = ectx; result->cfg = cfg; return result;}voidGNUNET_client_connection_close_temporarily (struct GNUNET_ClientServerConnection *sock){ GNUNET_GE_ASSERT (NULL, sock != NULL); GNUNET_mutex_lock (sock->destroylock); if (sock->sock != NULL) { GNUNET_socket_close (sock->sock); GNUNET_mutex_lock (sock->readlock); GNUNET_mutex_lock (sock->writelock); GNUNET_socket_destroy (sock->sock); sock->sock = NULL; GNUNET_mutex_unlock (sock->writelock); GNUNET_mutex_unlock (sock->readlock); } GNUNET_mutex_unlock (sock->destroylock);}voidGNUNET_client_connection_close_forever (struct GNUNET_ClientServerConnection *sock){ GNUNET_GE_ASSERT (NULL, sock != NULL); GNUNET_mutex_lock (sock->destroylock); if (sock->sock != NULL) { GNUNET_socket_close (sock->sock); GNUNET_mutex_lock (sock->readlock); GNUNET_mutex_lock (sock->writelock); GNUNET_socket_destroy (sock->sock); sock->sock = NULL; sock->dead = GNUNET_YES; GNUNET_mutex_unlock (sock->writelock); GNUNET_mutex_unlock (sock->readlock); } else { sock->dead = GNUNET_YES; } GNUNET_mutex_unlock (sock->destroylock);}voidGNUNET_client_connection_destroy (struct GNUNET_ClientServerConnection *sock){ GNUNET_GE_ASSERT (NULL, sock != NULL); GNUNET_client_connection_close_forever (sock); GNUNET_mutex_destroy (sock->readlock); GNUNET_mutex_destroy (sock->writelock); GNUNET_mutex_destroy (sock->destroylock); GNUNET_free (sock);}intGNUNET_client_connection_test_connected (struct GNUNET_ClientServerConnection *sock){ return (sock->sock != NULL);}/** * Check a socket, open and connect if it is closed. This code * supports IPv4 and IPv6 (and may try both). It also waits a bounded * amount of time for the connection to succeed and may even retry the * same IP version a few times since gnunetd may just be starting or * out of sockets; hence this code could fail on first attempt, will * then wait a few milliseconds, retry and conceivably succeed. Since * this is then done for multiple address families, the whole thing is * slightly more complicated then your ordinary connect call. Not to * mention that the code also supports another thread coming in in the * middle and closing the socket for good -- or even opening it! */intGNUNET_client_connection_ensure_connected (struct GNUNET_ClientServerConnection *sock){ /* list of address families to try for connecting, in order of preference */ static int addr_families[] = {#ifdef AF_UNSPEC AF_UNSPEC,#endif#ifdef AF_INET6 AF_INET6,#endif AF_INET, -1 }; GNUNET_CronTime select_start; struct sockaddr *soaddr; socklen_t socklen; fd_set rset; fd_set wset; fd_set eset; struct timeval timeout; int ret; int osock; unsigned short port; char *host; int af_index; int soerr; socklen_t soerrlen; int tries; GNUNET_GE_ASSERT (NULL, sock != NULL); if (sock->sock != NULL) return GNUNET_OK; if (sock->dead == GNUNET_YES) return GNUNET_SYSERR; port = getGNUnetPort (sock->ectx, sock->cfg); if (port == 0) return GNUNET_SYSERR; host = getGNUnetdHost (sock->ectx, sock->cfg); if (host == NULL) return GNUNET_SYSERR; af_index = 0; /* we immediately advance if there is a DNS lookup error * (which would likely persist) or a socket API error * (which would equally likely persist). We retry a * few times with a small delay if we may just be having * a connection issue. */#define TRIES_PER_AF 2#ifdef WINDOWS #define DELAY_PER_RETRY (5000 * GNUNET_CRON_MILLISECONDS)#else #define DELAY_PER_RETRY (50 * GNUNET_CRON_MILLISECONDS)#endif#define ADVANCE() do { af_index++; tries = TRIES_PER_AF; } while(0)#define RETRY() do { tries--; if (tries == 0) { ADVANCE(); } else { GNUNET_thread_sleep(DELAY_PER_RETRY); } } while (0) tries = TRIES_PER_AF; /* loop over all possible address families */ while (1) { if (addr_families[af_index] == -1) { GNUNET_GE_LOG (sock->ectx, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("Error connecting to %s:%u. Is the daemon running?\n"), host, port); return GNUNET_SYSERR; } soaddr = NULL; socklen = 0; if (GNUNET_SYSERR == GNUNET_get_ip_from_hostname (sock->ectx, host, addr_families[af_index], &soaddr, &socklen)) { ADVANCE ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -