TCP/IP协议栈的心跳、丢包重传、连接超时机制实例详解

news2024/11/18 7:47:46

大家好,本文结合具体的问题实例,详细讲解一下TCP/IP协议栈的心跳机制、丢包重传机制等内容,给大家提供一个借鉴和参考。

1、问题概述

虽然软件底层模块在网络恢复后能自动重连上服务器,但会议因为网络问题已经退出,需要重新加入会议。

因为客户特殊的网络运行环境,会频繁出现网络抖动不稳定的情况,客户要求必须要实现60秒内网络恢复后能依然保持在会议中,保证会议流程不被中断。

客户坚持要实现这个特殊的功能点,项目已经接近尾声,目前处于客户试用阶段,不实现该功能,项目无法通过验收,客户不给钱。

前方同事将当前问题及项目进展情况向研发部门领导反馈,研发部紧急召开讨论会议,商讨60秒不掉会的实现方案。

这里面涉及到两大类的网络连接,一类是传输控制信令的TCP连接,另一类是传输音视频码流的UDP连接。UDP连接的问题不大,主要是TCP连接的断链与重连问题,下面主要讨论TCP连接相关问题。

在出现网络不稳定掉会时,可能是系统TCPIP协议栈已经检测到网络异常,系统协议层已经将网络断开了;也可能软件应用层的心跳机制检测到网络故障,断开了与服务器的链接。

对于系统TCPIP协议栈自身检测出来的网络异常,则可能存在两种情况,一是TCPIP协议栈自身的心跳机制检测出来的;二是TCP连接的丢包重传机制检测出异常。

对于应用层的心跳检测机制,我们可以放大超时检测时间。本文我们主要讨论一下TCPIP协议栈的TCP连接的心跳、丢包重传、连接超时等机制。

在检测到网络异常后,我们底层可以自动发起重连或者信令发送触发自动重连,业务模块将会议相关资源保存不释放,在网络恢复后可以继续保持在会议中,可以继续接收到会议中的音视频码流,可以继续进行会议中的一些操作!

2、TCPIP协议栈的心跳机制

2.1、TCP中的ACK机制

TCP建链时的三次握手流程如下所示:

图片

 

之所以说TCP连接是可靠的,首先是发送数据前要建立连接,再就是收到数据后都会给对方恢复一个ACK包,表明我收到你的数据包了。对于数据发送端,如果数据发出去后没有收到ACK包,则会触发丢包重传机制。

不管是建链时,还是建链后的数据收发时,都有ACK包,TCP/IP协议栈的心跳包也不例外。

2.2、TCPIP协议栈的心跳机制说明

TCP/IP协议栈有个默认的TCP心跳机制,这个心跳机制是和socket套接字(TCP套接字)绑定的,可以对指定的套接字开启协议栈的心跳检测机制。默认情况下,协议栈的心跳机制对socket套接字是关闭的,如果要使用需要人为开启的。

在Windows中,默认是每隔2个小时发一次心跳包,客户端程序将心跳包发给服务器后,接下来会有两种情况:

1)网络正常时:服务器收到心跳包,会立即回复ACK包,客户端收到ACK包后,再等2个小时发送下一个心跳包。其中,心跳包发送时间间隔时间keepalivetime,Windows系统中默认是2小时,可配置。如果在2个小时的时间间隔内,客户端和服务器有数据交互,客户端会收到服务器的ACK包,也算作心跳机制的心跳包,2个小时的时间间隔会重新计时。

2)网络异常时:服务器收不到客户端发过去的心跳包,没法回复ACK,Windows系统中默认的是1秒超时,1秒后会重发心跳包。如果还收不到心跳包的ACK,则1秒后重发心跳包,如果始终收不到心跳包,则在发出10个心跳包就达到了系统的上限,就认为网络出故障了,协议栈就会直接将连接断开了。其中,发出心跳包收不到ACK的超时时间称为 keepaliveinterval,Windows系统中默认是1秒,可配置;收不到心跳包对应的ACK包的重发次数probe,Windows系统是固定的,是固定的10次,不可配置的。

所以TCP/IP协议栈的心跳机制也能检测出网络异常,不过在默认配置下可能需要很久才能检测出来,除非网络异常出现在正在发送心跳包后等待对端的回应时,这种情况下如果多次重发心跳包都收不到ACK回应,协议栈就会判断网络出故障,主动将连接关闭掉。

2.3、修改TCP/IP协议栈的默认心跳参数

TCP/IP协议栈的默认心跳机制的开启,不是给系统整个协议栈开启心跳监测,而是对某个socket套接字开启。

开启心跳机制后,还可以修改心跳的时间参数。从代码上看,先调用setsockopt给目标套接字开启心跳监测机制,再调用WSAIoctl去修改心跳检测的默认时间参数,相关代码如下所示:

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;

上面的代码可以看到,先调用setsockopt函数,传入SO_KEEPALIVE参数,打开TCP连接的心跳开关,此时心跳参数使用系统默认的心跳参数值。

紧接着,调用WSAIoCtrl函数,传入SIO_KEEPALIVE_VALS参数,同时将设置好时间值的心跳参数结构体传进去。

下面对心跳参数结构体tcp_keepalive做个详细的说明:(以Windows系统为例)

1)keepalivetime:默认2小时发送一次心跳保活包,比如发送第1个保活包之后,间隔2个小时后再发起下一个保活包。如果这期间有数据交互,也算是有效的保活包,这个时间段就不再发送保活包,发送下个保活包的时间间隔会从收发的最后一条数据的时刻开始重新从0计时。

2)keepaliveinterval:发送保活包后,没有收到对端的ack的超时时间默认为1秒。假设和对端的网络出问题了,给对端发送第1个保活包,1秒内没有收到对端的ack,则发第2个保活包,1秒内没有收到对端的保活包,再发送下一个保活包,.....,直到发送第10个保活包后,1秒钟还没收到ack回应,则达到发送10次保活包的探测次数上限,则认为网络出问题了。

3)probe探测次数:Windows系统上的探测次数被固定为10次,不可修改。

MSDN上对心跳机制检测出的网络异常的说明如下:

If a connection is dropped as the result of keep-alives the error code WSAENETRESET is returned to any calls in progress on the socket, and any subsequent calls will fail with WSAENOTCONN.

因为保活次数达到上限导致连接被丢弃掉,所有正在调用中的套接字接口会返回WSAENETRESET错误码,后续的套接字api函数的调用都会返回WSAENOTCONN。

3、libwebsockets开源库中的心跳机制使用的就是TCPIP协议栈的心跳机制

我们的产品之前在使用websocket时,就遇到没有设置心跳机制导致TCP长连接被网络设备无故释放的问题。

我们客户端程序在登录时,会去连接某业务的注册服务器,建立的是websocket长连接。这个长连接一直保持着,只有使用该业务模块的业务时才会使用到该连接,在该连接上进行数据交互。软件登录后,如果一直没有操作该业务模块的业务,这个长连接会一直处于闲置状态,即这个连接上没有数据交互。

结果在某次测试过程中出现了问题,排查下来发现,这个长连接因为长时间没有数据交互,被中间的网络设备关闭了。后来为了解决这个问题,我们在初始化websocket库时设置心跳参数,这样上述websocket长连接在空闲的时候能跑一跑心跳包,这样就能确保该长连接不会因为长时间没有跑数据被无故关闭的问题了。

我们在调用lws_create_context接口创建websockets会话上下文时,该接口的结构体参数lws_context_creation_info中,有设置心跳参数的字段:

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

其中的ka_time、ka_probes和ka_interval三个字段就是心跳相关的设置参数。我们初始化websockets上下文的代码如下:

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;
}

通过查阅libwebsockets开源库代码得知,此处设置的心跳使用的就是TCPIP协议栈的心跳机制,如下所示:

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;
}

4、TCPIP丢包重传机制

如果网络出故障时,客户端与服务器之间正在进行TCP数据交互,客户端给服务器发送数据包后因为网络故障收不到服务器的ACK包,就会触发客户端的TCP丢包重传,丢包重传机制也能判断出网络出现异常。

对于TCP连接,客户端给服务器发送数据后没有收到服务器的ACK包,会触发丢包重传。每次重传的时间间隔会加倍,当重传次数达到系统上限(Windows默认的上限是5次,Linux默认的上限是15次)后,协议栈就认为网络出故障了,会直接将对应的连接关闭了。         

所以当网络出现故障时有数据交互,协议栈会在数十秒内检测到网路出现异常,就会直接将连接直接关闭掉。丢包重传机制的详细描述如下所示:

图片

 

图片

 

对于丢包重传机制,可以通过给PC插拔网线来查看,可以使用wireshark抓包看一下。快速插拔网线时(先拔掉网线,等待几秒钟再将网线插上),给服务器发送的操作指令会因为丢包重传会收到数据的。

5、使用非阻塞socket和select接口实现connect连接的超时控制

5.1、MSDN上对connect和select接口的说明

对于tcp套接字,我们需要调用套接字函数connect去建立TCP连接。我们先来看看微软MSDN上对套接字接口connect的描述:

图片

图片

图片

图片

 

On a blocking socket, the return value indicates success or failure of the connection attempt.

对于阻塞式的socket,通过connect的返回值就能确定有没有连接成功,返回0表示连接成功。

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.

对于非组赛式的socket,connect调用会立即返回,但连接操作还没有完成。connect返回SOCKET_ERROR,对于非阻塞式socket,返回SOCKET_ERROR并不表示失败,需要调用WSAGetLastError获取connect函数执行后的LastError值,一般此时WSAGetLastError会返回WSAEWOULDBLOCK:

图片

 

表明连接正在进行中。可以使用select接口检测一下套接字是否可写(套接字是否在writefds集合中),如果可写,则表示连接成功。如果套接字在exceptfds集合中,则说明连接出现了异常,如下所示:

图片

 

5.2、使用非阻塞socket和select实现连接超时的控制

对于阻塞式的socket,在Windows下,如果远端的IP和Port不可达,则会阻塞75s后返回SOCKET_ERROR,表明连接失败。所以当我们测试远端的IP和Port是否可以连接时,我们不使用阻塞式的socket,而是使用非阻塞式socket,然后调用select,通过select添加连接超时时间,实现连接超时的控制。

select函数因为超时返回,会返回0;如果发生错误,则返回SOCKET_ERROR,所以判断时要判断select返回值,如果小于等于0,则是连接失败,立即将套接字关闭掉。

如果select返回值大于0,则该返回值是已经准备就绪的socket个数,比如连接成功的socket。我们判断套接字是否在可写集合writefds中,如果在该集合中,则表示连接成功。

根据MSDN上的相关描述,我们就能大概知道该如何实现connect的超时控制了,相关代码如下:

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;
}
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1018331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录|121. 买卖股票的最佳时机,122.买卖股票的最佳时机II,123.买卖股票的最佳时机III,188.买卖股票的最佳时机IV

121. 买卖股票的最佳时机 dp含义 dp[i][0] 表示第i天持有股票所得最多现金 ,dp[i][1] 表示第i天不持有股票所得最多现金 其实一开始现金是0&#xff0c;那么加入第i天买入股票现金就是 -prices[i]&#xff0c; 这是一个负数。 递推公式 第i天持有股票即dp[i][0],可以由两个…

Oracle数据库体系结构(三)_逻辑结构

Oracle逻辑存储结构,主要描述oracle 数据库内部数据的组织和管理方式&#xff0c;即在数据库管理系统的层面中如何组织和管理数据&#xff0c;与操作系统没有关系。逻辑存储结构时候物理存储机构的抽象体现&#xff0c;是不可见的&#xff0c;可以通过查询数据库数据字典了解逻…

RocketMq(一)安装部署

一、linux单机部署&#xff1a; 1、到apache官网下载 | RocketMQ (apache.org)下载binary zip包&#xff0c;如我下载的4.9.6版本。 上传到建好的/usr/local/rocketmq目录下。 2、解压zip包 unzip rocketmq-all-4.9.6-bin-release.zip 3、进入解压后的文件夹,启动 Name Serv…

TCP详解之流量控制

TCP详解之流量控制 发送方不能无脑的发数据给接收方&#xff0c;要考虑接收方处理能力。 如果一直无脑的发数据给对方&#xff0c;但对方处理不过来&#xff0c;那么就会导致触发重发机制&#xff0c;从而导致网络流量的无端的浪费。 为了解决这种现象发生&#xff0c;TCP 提…

关于QGC Landing Pattern规划的计算过程

固定翼飞机在规划航线时&#xff0c;QGC提供了自动生成降落阶段航线功能。在地图上选择降落点之后&#xff0c;根据默认的下滑坡度或下滑距离、盘旋点半径&#xff0c;自动生成航线。最后的降落航向实际由三个点组成&#xff0c;开始降落点&#xff08;MAV_CMD_DO_LAND_START&a…

SSM整合01

SSM01搭建SSM项目 1.创建maven的web工程 1.1pom.xml配置 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apac…

【每日一题】981. 基于时间的键值存储

981. 基于时间的键值存储 - 力扣&#xff08;LeetCode&#xff09; 设计一个基于时间的键值数据结构&#xff0c;该结构可以在不同时间戳存储对应同一个键的多个值&#xff0c;并针对特定时间戳检索键对应的值。 实现 TimeMap 类&#xff1a; TimeMap() 初始化数据结构对象void…

开始为 Android 开发 PWA 或混合 Web 应用

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 PWA 或混合 Web 应用的功能 Apache Cordova Ionic 通过安装所需工具开始使用 Ionic 使用 Ionic Cordova 和 Ang…

小白新手一文完成Git+Github/GITEE傻瓜式入门详解部署教程(内含TortoiseGit配置)

本文创作时版本为 Git-2.41.0&#xff0c;使用目标为笔记存储和代码库&#xff0c;部分公司向使用的设置可能不一样 一 Git 1.1 何为 Git Git是一款免费、开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理项目、项目版本管理。 原本是为了帮助管理 Linux 内核开发而开…

Docker 网络学习

docker的网络模式 当你开始大规模使用Docker时&#xff0c;你会发现需要了解很多关于网络的知识。Docker作为目前最火的轻量级容器技术&#xff0c;有很多令人称道的功能&#xff0c;如Docker的镜像管理。然而&#xff0c;Docker同样有着很多不完善的地方&#xff0c;网络方面…

ARM64汇编基础

ARM64汇编基础 主要内容 到目前为止&#xff0c;大部分的移动设备都是64位的arm架构&#xff0c;一直想抽个时间系统学习下&#xff0c;这个周末就专门来学习下。毕竟两天的时间&#xff0c;也只是简单的入门了解下&#xff0c;为后续工作和学习打下基础。 本次学习的主要内容…

Pytorch学习笔记(GPU训练)

GUP训练 配置pytorch的gup版本主要是在网络模型、输入和标记的数据、损失函数 方式一 直接.cuda()调用&#xff0c;在原有的模型训练代码中的网络模型、输入和标记的数据、损失函数部分直接调用即可 方式二 事先定义好设备device,然后直接.to(device)调用&#xff0c;在原…

基于SSM的学生宿舍管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

[vulntarget靶场] vulntarget-a

靶场地址&#xff1a;https://github.com/crow821/vulntarget 拓扑结构 信息收集 主机发现 netdiscover -r 192.168.127.0/24 -i eth0端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.127.130访问目标80&#xff0c;发现为通达oa WIN7漏洞利用 通达oa后台…

运行软件报错msvcr100.dll丢失的解决方法,全面分析msvcr100.dll丢失问题

随着科技的飞速发展&#xff0c;计算机已经成为人们生活和工作中不可或缺的重要工具。然而&#xff0c;在使用计算机的过程中&#xff0c;难免会遇到一些令人困扰的问题&#xff0c;如计算机丢失 msvcr100.dll 文件就是其中之一。本文将详细介绍计算机丢失 msvcr100.dll 的困扰…

文件打开表有几个?——参考《王道考研》

一、 真题试练 解析&#xff1a; 二、关于文件打开表 三、 疑问&#xff1f; 不是说好的只维护一个文件打开表吗&#xff1f; 四、解答 OS维护的是总的文件打开表&#xff0c;各自用户由对应各自的打开表&#xff0c;所有用户的打开表组成OS总的打开表。

layui框架学习(45: 工具集模块)

layui的工具集模块util支持固定条、倒计时等组件&#xff0c;同时提供辅助函数处理时间数据、字符转义、批量事件处理等操作。   util模块中的fixbar函数支持设置固定条&#xff08;2.7版本的帮助文档中叫固定块&#xff09;&#xff0c;是指固定在页面一侧的工具条元素&…

swift 事件

多个元素链接到单个IBAction 并区分

图书信息管理系统

#include<stdio.h> #include<string.h> #include<stdlib.h> #define MAXSIZE 10000typedef struct {char no[100];//图书ISBNchar name[100];//图书名字double price;//图书价格 } Book;typedef struct {Book data[MAXSIZE];int length; } SqList,*PSqList;P…

算法基础:图

图论 图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形&#xff0c;这种图形通常用来描述某些事物之间的某种特定关系&#xff0c;用点代表事物&#xff0c;用连接两点的线表示相应两个事物间具有这种关系。 …