Este artículo explica en detalle los mecanismos de latido, retransmisión de pérdida de paquetes y tiempo de espera de conexión de la pila de protocolos TCP/IP.

1. Descripción general del problema

Aunque el módulo subyacente del software puede volver a conectarse automáticamente al servidor después de restaurar la red, la conferencia se cerró debido a problemas de red y es necesario volver a unirse. Debido al entorno operativo de red especial del cliente, se producirán fluctuaciones e inestabilidad de la red con frecuencia. El cliente requiere que la reunión se mantenga después de que se restablezca la red dentro de los 60 segundos para garantizar que el proceso de la reunión no se interrumpa.

El cliente insiste en realizar este punto de función especial. El proyecto está a punto de completarse y actualmente se encuentra en la etapa de prueba para el cliente. Si esta función no se implementa, el proyecto no pasará la aceptación y el cliente no pagará.

Los colegas en el frente informaron los problemas actuales y el progreso del proyecto a los líderes del departamento de I + D, y el departamento de I + D celebró una reunión de discusión de emergencia para discutir un plan que garantizara que no se perdiera la reunión durante 60 segundos. Esto implica dos tipos principales de conexiones de red, una es una conexión TCP que transmite señales de control y la otra es una conexión UDP que transmite secuencias de audio y video. El problema de la conexión UDP no es grande, el principal problema es la desconexión y reconexión de la conexión TCP. A continuación se analizan principalmente los problemas relacionados con la conexión TCP.

Cuando la red es inestable y se pierde la sesión, puede ser que la pila del protocolo TCPIP del sistema haya detectado una anomalía en la red y la capa de protocolo del sistema haya desconectado la red; también puede ser que el mecanismo de latido de la capa de aplicación de software haya detectado un fallo de red y se desconectó la conexión con el servidor. Para las anomalías de la red detectadas por la propia pila de protocolos TCPIP del sistema, puede haber dos situaciones: una es detectada por el mecanismo de latido de la propia pila de protocolos TCPIP y la otra es detectada por el mecanismo de pérdida y retransmisión de paquetes de la conexión TCP.

Para el mecanismo de detección de latidos de la capa de aplicación, podemos ampliar el tiempo de detección del tiempo de espera. En este artículo, analizamos principalmente los latidos, la retransmisión de pérdida de paquetes, el tiempo de espera de la conexión y otros mecanismos de la conexión TCP de la pila de protocolos TCPIP. Después de detectar una anomalía en la red, nuestra capa inferior puede iniciar automáticamente la reconexión o activar la reconexión automática enviando señales. El módulo empresarial guardará los recursos relacionados con la conferencia y no los liberará. Una vez restaurada la red, podrá continuar en la conferencia. y puede continuar recibiendo información de la conferencia, flujo de código de audio y video, ¡puede continuar realizando algunas operaciones en la conferencia!

2. Mecanismo de latido de la pila de protocolos TCPIP

2.1 Mecanismo ACK en TCP

El proceso de protocolo de enlace de tres vías durante el establecimiento del enlace TCP es el siguiente:

La razón por la que las conexiones TCP son confiables es que la conexión debe establecerse antes de enviar datos y luego, después de recibir los datos, se restaurará un paquete ACK a la otra parte, lo que indica que he recibido su paquete de datos. Para el extremo del envío de datos, si no se recibe ningún paquete ACK después de enviar los datos, se activará el mecanismo de retransmisión de pérdida de paquetes. Ya sea al establecer un enlace o al enviar y recibir datos después de establecer un enlace, hay paquetes ACK, y el paquete de latidos de la pila de protocolos TCP/IP no es una excepción.

2.2 Descripción del mecanismo de latido de la pila de protocolos TCPIP

La pila de protocolos TCP / IP tiene un mecanismo de latido TCP predeterminado, que está vinculado al socket (zócalo TCP), y el mecanismo de detección de latidos de la pila de protocolos se puede activar para el socket especificado. De forma predeterminada, el mecanismo de latido de la pila de protocolos está cerrado para los sockets. Si desea utilizarlo, debe activarlo manualmente. En Windows, el valor predeterminado es enviar un paquete de latidos cada 2 horas. Después de que el programa cliente envía el paquete de latidos al servidor, habrá dos situaciones:

1) Cuando la red es normal: cuando el servidor recibe el paquete de latidos, responderá inmediatamente con un paquete ACK. Después de que el cliente reciba el paquete ACK, esperará otras 2 horas para enviar el siguiente paquete de latidos. Entre ellos, el intervalo de envío de paquetes de latidos keepalivetime es de 2 horas de forma predeterminada en los sistemas Windows y se puede configurar. Si hay interacción de datos entre el cliente y el servidor dentro del intervalo de 2 horas, el cliente recibirá un paquete ACK del servidor, que también se cuenta como un paquete de latido para el mecanismo de latido, y el intervalo de 2 horas será reprogramado. 2) Cuando la red es anormal: el servidor no puede recibir el paquete de latidos enviado por el cliente y no puede responder ACK. El tiempo de espera predeterminado en el sistema Windows es de 1 segundo y el paquete de latidos se reenviará después de 1 segundo. Si no se recibe el ACK del paquete de latidos, el paquete de latidos se reenviará después de 1 segundo. Si aún no se recibe el paquete de latidos, se alcanzará el límite superior del sistema después de enviar 10 paquetes de latidos y se considerará la red. estar defectuoso La pila de protocolos La conexión se desconectará directamente. Entre ellos, el período de tiempo de espera para enviar un paquete de latidos y no recibir un ACK se llama keepaliveinterval. El valor predeterminado en los sistemas Windows es de 1 segundo y es configurable; el número de retransmisiones de paquetes ACK que no se pueden recibir corresponde a la sonda del paquete de latidos. está arreglado en sistemas Windows 10 veces, no configurable.

Por lo tanto, el mecanismo de latido de la pila de protocolos TCP/IP también puede detectar anomalías de la red, pero puede llevar mucho tiempo detectarlo en la configuración predeterminada, a menos que la anomalía de la red ocurra mientras se espera una respuesta del par después de enviar un latido. paquete En este caso, si hay varios Si no se recibe una respuesta ACK después de reenviar el paquete de latidos, la pila de protocolos determinará que la red está defectuosa y cerrará activamente la conexión.

2.3 Modificar los parámetros de latido predeterminados de la pila del protocolo TCP/IP

El mecanismo de latido predeterminado de la pila de protocolos TCP/IP no permite la supervisión de latidos para toda la pila de protocolos del sistema, sino para un socket determinado. Después de activar el mecanismo de latido, también puede modificar los parámetros de tiempo del latido. Desde la perspectiva del código, primero llame a setsockopt para habilitar el mecanismo de monitoreo de latidos del socket de destino y luego llame a WSAIoctl para modificar los parámetros de tiempo predeterminados de detección de latidos. El código relevante es el siguiente:

  SOCKET socket;
  // ......(中间代码省略)
 
  int optval = 1;
  int nRet = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&optval, 
                                  sizeof(optval));
  if (nRet != 0)
       return;
 
  tcp_keepalive alive;
  alive.onoff = TRUE;
  alive.keepalivetime = 10*1000;
  alive.keepaliveinterval = 2*1000;
 
  DWORD dwBytesRet = 0;
  nRet = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, 
                            &dwBytesRet, NULL, NULL);
  if (nRet != 0)
      return;

Como puede ver en el código anterior, primero se llama a la función setsockopt, se pasa el parámetro SO_KEEPALIVE y se activa el interruptor de latido de la conexión TCP. En este momento, el parámetro de latido utiliza el valor del parámetro de latido predeterminado del sistema. Luego, llame a la función WSAIoCtrl, pase el parámetro SIO_KEEPALIVE_VALS y pase la estructura del parámetro de latido con el valor de tiempo establecido. La siguiente es una explicación detallada de la estructura de parámetros de latido tcp_keepalive: (tomando el sistema Windows como ejemplo)

1) keepalivetime: de forma predeterminada, se envía un paquete keepalive cada 2 horas. Por ejemplo, después de enviar el primer paquete keepalive, el siguiente paquete keepalive se enviará después de un intervalo de 2 horas. Si hay interacción de datos durante este período, se considera un paquete keep-alive válido. No se enviará ningún paquete keep-alive durante este período. El intervalo de tiempo para enviar el siguiente paquete keep-alive comenzará desde 0 nuevamente desde el momento del último dato enviado y recibido. 2) keepaliveinterval: después de enviar un paquete de mantenimiento de vida, el tiempo de espera para no recibir un acuse de recibo del par es de 1 segundo de forma predeterminada. Supongamos que hay un problema con la red con el par. Envíe el primer paquete de mantenimiento de vida al par. Si no se recibe ningún acuse de recibo del par en 1 segundo, envíe el segundo paquete de mantenimiento de vida. No se recibe ningún paquete de mantenimiento de vida. recibido del par dentro de 1 segundo., y luego envía el siguiente paquete de mantenimiento de actividad,..., hasta después de enviar el décimo paquete de mantenimiento de actividad, si no se recibe ninguna respuesta de reconocimiento dentro de 1 segundo, el límite superior de tiempos de detección de Se alcanza el envío de 10 paquetes de mantenimiento de actividad y se considera que la red tiene un problema. 3) Número de detecciones de sondas: la cantidad de sondas en los sistemas Windows está fijada en 10 veces y no se puede modificar.

La descripción en MSDN de las anomalías de red detectadas por el mecanismo de latido es la siguiente:

Si se interrumpe una conexión como resultado de mantener activa, se devuelve el código de error WSAENETRESET a cualquier llamada en curso en el socket, y cualquier llamada posterior fallará con WSAENOTCONN.

Debido a que el recuento de mantenimiento de conexión alcanza el límite superior y la conexión se descarta, todas las interfaces de socket que se llamen devolverán el código de error WSAENETRESET y las llamadas posteriores a la función API de socket devolverán WSAENOTCONN.

3. El mecanismo de latido en la biblioteca de código abierto libwebsockets utiliza el mecanismo de latido de la pila de protocolos TCPIP.

Cuando nuestros productos usaban websocket antes, encontramos el problema de que el mecanismo de latido no estaba configurado, lo que provocó que el dispositivo de red liberara conexiones TCP largas sin ningún motivo. Cuando nuestro programa cliente inicia sesión, se conectará al servidor de registro de una determinada empresa y establecerá una conexión websocket larga. Esta conexión larga siempre se mantiene, esta conexión solo se usa cuando se utiliza el negocio de este módulo comercial y los datos se intercambian en esta conexión. Después de iniciar sesión en el software, si el módulo empresarial no se ha operado, esta conexión larga permanecerá inactiva, es decir, no hay interacción de datos en esta conexión. Como resultado, ocurrió un problema durante una determinada prueba. Después de la investigación, se descubrió que el dispositivo de red intermedio cerró esta conexión larga porque no hubo interacción de datos durante mucho tiempo. Más tarde, para resolver este problema, configuramos los parámetros de latido al inicializar la biblioteca websocket, de modo que la conexión websocket larga mencionada anteriormente pueda ejecutar un paquete de latidos cuando está inactiva, asegurando así que la conexión larga no se cierre por No hay motivo porque no hay datos ejecutándose durante mucho tiempo. Cuando llamamos a la interfaz lws_create_context para crear un contexto de sesión de websockets, el parámetro de estructura lws_context_creation_info de la interfaz tiene un campo para configurar los parámetros de latido:

/**
 * struct lws_context_creation_info - parameters to create context with
 *
 * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS
 * is not given, then for backwards compatibility one vhost is created at
 * context-creation time using the info from this struct.
 *
 * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created
 * at the same time as the context, they are expected to be created afterwards.
 *
 * @port:	VHOST: Port to listen on... you can use CONTEXT_PORT_NO_LISTEN to
 *		suppress listening on any port, that's what you want if you are
 *		not running a websocket server at all but just using it as a
 *		client
 * @iface:	VHOST: NULL to bind the listen socket to all interfaces, or the
 *		interface name, eg, "eth2"
 *		If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is
 *		the pathname of a UNIX domain socket. you can use the UNIX domain
 *		sockets in abstract namespace, by prepending an @ symbole to the
 *		socket name.
 * @protocols:	VHOST: Array of structures listing supported protocols and a protocol-
 *		specific callback for each one.  The list is ended with an
 *		entry that has a NULL callback pointer.
 *		It's not const because we write the owning_server member
 * @extensions: VHOST: NULL or array of lws_extension structs listing the
 *		extensions this context supports.  If you configured with
 *		--without-extensions, you should give NULL here.
 * @token_limits: CONTEXT: NULL or struct lws_token_limits pointer which is initialized
 *		with a token length limit for each possible WSI_TOKEN_***
 * @ssl_cert_filepath:	VHOST: If libwebsockets was compiled to use ssl, and you want
 *			to listen using SSL, set to the filepath to fetch the
 *			server cert from, otherwise NULL for unencrypted
 * @ssl_private_key_filepath: VHOST: filepath to private key if wanting SSL mode;
 *			if this is set to NULL but sll_cert_filepath is set, the
 *			OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called
 *			to allow setting of the private key directly via openSSL
 *			library calls
 * @ssl_ca_filepath: VHOST: CA certificate filepath or NULL
 * @ssl_cipher_list:	VHOST: List of valid ciphers to use (eg,
 * 			"RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"
 * 			or you can leave it as NULL to get "DEFAULT"
 * @http_proxy_address: VHOST: If non-NULL, attempts to proxy via the given address.
 *			If proxy auth is required, use format
 *			"username:password@server:port"
 * @http_proxy_port:	VHOST: If http_proxy_address was non-NULL, uses this port at
 * 			the address
 * @gid:	CONTEXT: group id to change to after setting listen socket, or -1.
 * @uid:	CONTEXT: user id to change to after setting listen socket, or -1.
 * @options:	VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields
 * @user:	CONTEXT: optional user pointer that can be recovered via the context
 *		pointer using lws_context_user
 * @ka_time:	CONTEXT: 0 for no keepalive, otherwise apply this keepalive timeout to
 *		all libwebsocket sockets, client or server
 * @ka_probes:	CONTEXT: if ka_time was nonzero, after the timeout expires how many
 *		times to try to get a response from the peer before giving up
 *		and killing the connection
 * @ka_interval: CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes
 *		attempt
 * @provided_client_ssl_ctx: CONTEXT: If non-null, swap out libwebsockets ssl
 *		implementation for the one provided by provided_ssl_ctx.
 *		Libwebsockets no longer is responsible for freeing the context
 *		if this option is selected.
 * @max_http_header_data: CONTEXT: The max amount of header payload that can be handled
 *		in an http request (unrecognized header payload is dropped)
 * @max_http_header_pool: CONTEXT: The max number of connections with http headers that
 *		can be processed simultaneously (the corresponding memory is
 *		allocated for the lifetime of the context).  If the pool is
 *		busy new incoming connections must wait for accept until one
 *		becomes free.
 * @count_threads: CONTEXT: how many contexts to create in an array, 0 = 1
 * @fd_limit_per_thread: CONTEXT: nonzero means restrict each service thread to this
 *		many fds, 0 means the default which is divide the process fd
 *		limit by the number of threads.
 * @timeout_secs: VHOST: various processes involving network roundtrips in the
 *		library are protected from hanging forever by timeouts.  If
 *		nonzero, this member lets you set the timeout used in seconds.
 *		Otherwise a default timeout is used.
 * @ecdh_curve: VHOST: if NULL, defaults to initializing server with "prime256v1"
 * @vhost_name: VHOST: name of vhost, must match external DNS name used to
 *		access the site, like "warmcat.com" as it's used to match
 *		Host: header and / or SNI name for SSL.
 * @plugin_dirs: CONTEXT: NULL, or NULL-terminated array of directories to
 *		scan for lws protocol plugins at context creation time
 * @pvo:	VHOST: pointer to optional linked list of per-vhost
 *		options made accessible to protocols
 * @keepalive_timeout: VHOST: (default = 0 = 60s) seconds to allow remote
 *		client to hold on to an idle HTTP/1.1 connection
 * @log_filepath: VHOST: filepath to append logs to... this is opened before
 *		any dropping of initial privileges
 * @mounts:	VHOST: optional linked list of mounts for this vhost
 * @server_string: CONTEXT: string used in HTTP headers to identify server
 *		software, if NULL, "libwebsockets".
 */
struct lws_context_creation_info {
	int port;					/* VH */
	const char *iface;				/* VH */
	const struct lws_protocols *protocols;		/* VH */
	const struct lws_extension *extensions;		/* VH */
	const struct lws_token_limits *token_limits;	/* context */
	const char *ssl_private_key_password;		/* VH */
	const char *ssl_cert_filepath;			/* VH */
	const char *ssl_private_key_filepath;		/* VH */
	const char *ssl_ca_filepath;			/* VH */
	const char *ssl_cipher_list;			/* VH */
	const char *http_proxy_address;			/* VH */
	unsigned int http_proxy_port;			/* VH */
	int gid;					/* context */
	int uid;					/* context */
	unsigned int options;				/* VH + context */
	void *user;					/* context */
	int ka_time;					/* context */
	int ka_probes;					/* context */
	int ka_interval;				/* context */
#ifdef LWS_OPENSSL_SUPPORT
	SSL_CTX *provided_client_ssl_ctx;		/* context */
#else /* maintain structure layout either way */
	void *provided_client_ssl_ctx;
#endif
	short max_http_header_data;			/* context */
	short max_http_header_pool;			/* context */
	unsigned int count_threads;			/* context */
	unsigned int fd_limit_per_thread;		/* context */
	unsigned int timeout_secs;			/* VH */
	const char *ecdh_curve;				/* VH */
	const char *vhost_name;				/* VH */
	const char * const *plugin_dirs;		/* context */
	const struct lws_protocol_vhost_options *pvo;	/* VH */
	int keepalive_timeout;				/* VH */
	const char *log_filepath;			/* VH */
	const struct lws_http_mount *mounts;		/* VH */
	const char *server_string;			/* context */
	/* Add new things just above here ---^
	 * This is part of the ABI, don't needlessly break compatibility
	 *
	 * The below is to ensure later library versions with new
	 * members added above will see 0 (default) even if the app
	 * was not built against the newer headers.
	 */
	void *_unused[8];
};

Los tres campos ka_time, ka_probes y ka_interval son los parámetros de configuración relacionados con los latidos. Nuestro código para inicializar el contexto de websockets es el siguiente:

static lws_context* CreateContext()
{
    lws_set_log_level( 0xFF, NULL );
    lws_context* plcContext = NULL;
 
    lws_context_creation_info tCreateinfo;
    memset(&tCreateinfo, 0, sizeof tCreateinfo);
 
    tCreateinfo.port = CONTEXT_PORT_NO_LISTEN;
    tCreateinfo.protocols = protocols;
    tCreateinfo.ka_time = LWS_TCP_KEEPALIVE_TIME;
    tCreateinfo.ka_interval = LWS_TCP_KEEPALIVE_INTERVAL;
    tCreateinfo.ka_probes = LWS_TCP_KEEPALIVE_PROBES;
	tCreateinfo.options = LWS_SERVER_OPTION_DISABLE_IPV6;
 
    plcContext = lws_create_context(&tCreateinfo);
 
    return plcContext;
}

Al consultar el código de la biblioteca de código abierto libwebsockets, sabemos que el latido configurado aquí utiliza el mecanismo de latido de la pila de protocolos TCPIP, como se muestra a continuación:

LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
{
	int optval = 1;
	int optlen = sizeof(optval);
	u_long optl = 1;
	DWORD dwBytesRet;
	struct tcp_keepalive alive;
	int protonbr;
#ifndef _WIN32_WCE
	struct protoent *tcp_proto;
#endif
 
	if (vhost->ka_time) {
		/* enable keepalive on this socket */
		// 先调用setsockopt打开发送心跳包(设置)选项
		optval = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
					     (const char *)&optval, optlen) < 0)
			return 1;
 
		alive.onoff = TRUE;
		alive.keepalivetime = vhost->ka_time*1000;
		alive.keepaliveinterval = vhost->ka_interval*1000;
 
		if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
					      NULL, 0, &dwBytesRet, NULL, NULL))
			return 1;
	}
 
	/* Disable Nagle */
	optval = 1;
#ifndef _WIN32_WCE
	tcp_proto = getprotobyname("TCP");
	if (!tcp_proto) {
		lwsl_err("getprotobyname() failed with error %d\n", LWS_ERRNO);
		return 1;
	}
	protonbr = tcp_proto->p_proto;
#else
	protonbr = 6;
#endif
 
	setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen);
 
	/* We are nonblocking... */
	ioctlsocket(fd, FIONBIO, &optl);
 
	return 0;
}

 Information Direct: ruta de aprendizaje de tecnología del código fuente del kernel de Linux + video tutorial sobre el código fuente del kernel

Learning Express: Código fuente del kernel de Linux Ajuste de memoria Sistema de archivos Gestión de procesos Controlador de dispositivo/Pila de protocolo de red

4. Mecanismo de retransmisión de pérdida de paquetes TCPIP

Si hay una falla en la red y hay interacción de datos TCP entre el cliente y el servidor, y el cliente no puede recibir el paquete ACK del servidor debido a la falla de la red después de enviar el paquete de datos al servidor, la pérdida del paquete TCP del cliente y se activará la retransmisión y se producirá la pérdida de paquetes.El mecanismo de retransmisión también puede determinar si hay una anomalía en la red. Para las conexiones TCP, si el cliente no recibe un paquete ACK del servidor después de enviar datos al servidor, se activará la pérdida y retransmisión de paquetes. El intervalo de tiempo para cada retransmisión se duplicará. Cuando el número de retransmisiones alcance el límite superior del sistema (el límite superior predeterminado para Windows es 5 veces y el límite superior predeterminado para Linux es 15 veces), la pila de protocolos pensará que el La red ha fallado y transferirá directamente los datos correspondientes. La conexión está cerrada. Por lo tanto, cuando hay interacción de datos cuando la red falla, la pila de protocolos detectará una anomalía en la red en decenas de segundos y cerrará la conexión directamente. La descripción detallada del mecanismo de retransmisión de pérdida de paquetes es la siguiente:

Para el mecanismo de retransmisión de pérdida de paquetes, puede verificarlo conectando y desconectando el cable de red a la PC, o puede usar Wirehark para capturar el paquete. Al conectar y desconectar rápidamente el cable de red (primero desenchufe el cable de red, espere unos segundos y luego conéctelo), las instrucciones de operación enviadas al servidor recibirán datos debido a la pérdida y retransmisión de paquetes.

5. Utilice un socket sin bloqueo y seleccione la interfaz para implementar el control de tiempo de espera de la conexión de conexión.

5.1 Descripción de las interfaces de conexión y selección en MSDN

Para sockets tcp, necesitamos llamar a la función de socket connect para establecer una conexión TCP. Primero echemos un vistazo a la descripción de la conexión de interfaz de socket en Microsoft MSDN:

En un socket de bloqueo, el valor de retorno indica el éxito o el fracaso del intento de conexión.

Para bloquear sockets, el valor de retorno de connect se puede utilizar para determinar si la conexión fue exitosa. Devolver 0 indica que la conexión fue exitosa.

With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return SOCKET_ERROR, and WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are three possible scenarios:

Use the select function to determine the completion of the connection request by checking to see if the socket is writeable.

Para los sockets que no son de grupo, la llamada de conexión regresará inmediatamente, pero la operación de conexión aún no se ha completado. connect devuelve SOCKET_ERROR. Para sockets sin bloqueo, devolver SOCKET_ERROR no significa falla. Debe llamar a WSAGetLastError para obtener el valor de LastError después de ejecutar la función de conexión. Generalmente, WSAGetLastError devolverá WSAEWOULDBLOCK en este momento:

5.2 Utilice sockets sin bloqueo y seleccione para controlar el tiempo de espera de la conexión

Para bloquear sockets, en Windows, si la IP remota y el puerto son inaccesibles, se devolverá SOCKET_ERROR después del bloqueo durante 75 segundos, lo que indica que la conexión falló. Entonces, cuando probamos si la IP remota y el puerto se pueden conectar, no usamos un socket de bloqueo, sino un socket sin bloqueo, y luego llamamos a select para agregar el tiempo de espera de la conexión a través de select para controlar el tiempo de espera de la conexión.

La función de selección devolverá 0 porque se agota el tiempo de espera; si ocurre un error, devolverá SOCKET_ERROR, por lo que el valor de retorno de selección debe juzgarse al realizar un juicio. Si es menor o igual a 0, la conexión falla y el socket se cierra inmediatamente. Si el valor de retorno seleccionado es mayor que 0, el valor de retorno es el número de sockets listos, como el socket con una conexión exitosa. Determinamos si el socket está en la colección de escritura writefds. Si está en esta colección, la conexión es exitosa.

Según la descripción relevante en MSDN, podemos saber aproximadamente cómo implementar el control de tiempo de espera de conexión. El código relevante es el siguiente:

bool ConnectDevice( char* pszIP, int nPort )
{
	// 创建TCP套接字
  SOCKET connSock = socket(AF_INET, SOCK_STREAM, 0);
    if (connSock == INVALID_SOCKET)
    {
	return false;
    }
 
	// 填充IP和端口
	SOCKADDR_IN devAddr;
	memset(&devAddr, 0, sizeof(SOCKADDR_IN));
	devAddr.sin_family = AF_INET;
	devAddr.sin_port = htons(nPort);
	devAddr.sin_addr.s_addr = inet_addr(pszIP);
 
	// 将套接字设置为非阻塞式的,为下面的select做准备
	unsigned long ulnoblock = 1;
	ioctlsocket(connSock, FIONBIO, &ulnoblock);
 
	// 发起connnect,该接口立即返回
	connect(connSock, (sockaddr*)&devAddr, sizeof(devAddr));
 
	FD_SET writefds;
	FD_ZERO(&writefds);
	FD_SET(connSock, &writefds);
 
    // 设置连接超时时间为1秒
	timeval tv;
	tv.tv_sec = 1; //超时1s
	tv.tv_usec = 0;
	// The select function returns the total number of socket handles that are ready and contained 
	// in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR(-1) if an error occurred. 
	if (select(0, NULL, &writefds, NULL, &tv) <= 0)
	{
		closesocket(connSock);
		return false; //超时未连接上就退出
	}
 
	ulnoblock = 0;
	ioctlsocket(connSock, FIONBIO, &ulnoblock);
 
	closesocket(connSock);
	return true;
}

Supongo que te gusta

Origin blog.csdn.net/youzhangjing_/article/details/132901678
Recomendado
Clasificación