当前位置: 首页 > news >正文

《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项

《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项

  • 《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
    • 套接字可选项和 I/O 缓冲大小
      • 套接字多种可选项
      • getsockopt & setsockopt
      • SO_SNDBUF & SO_RCVBUF
    • SO_REUSEADDR
      • 发生地址绑定错误(Binding Error)
      • TIME_WAIT 状态
      • 地址再分配
    • TCP_NODELAY
      • Nagle 算法
      • Nagle 算法的优缺点
      • 禁用 Nagle 算法
    • 基于 Windows 的实现
      • 基于 Windows 的套接字类型示例程序
      • 基于 Windows 的套接字 I/O 缓冲大小示例程序
      • 基于 Windows 的 TCP_NODELAY 示例程序
    • 习题
      • (1)下列关于Time-wait状态的说法错误的是?
      • (2)TCP_NODELAY可选项与Nagle算法有关,可通过它禁用Nagle算法。请问何时应考虑禁用Nagle算法?结合收发数据的特性给出说明。

《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项

套接字可选项和 I/O 缓冲大小

套接字多种可选项

前文关于套接字的描述仅仅是使用其默认套接字特性来进行数据通信,这对于简单的使用场景来说似乎是可以的,然而实际工作场景中的确需要配置相关套接字选项来满足一些特殊需求。下图所示是一些常用的套接字可选配置选项。

在这里插入图片描述

从图中可以看出,套接字可选项是分层的。IPPROTO_IP层可选项是IP协议相关事项,IPPROTO_TCP层可选项是TCP协议相关事项,SOL_SOCKET层是套接字相关的通用可选项。

getsockopt & setsockopt

针对上文所描述的套接字可选项,可分别通过getsockopt函数和setsockopt函数来进行读取(Get)和设置(Set)(有些选项可能仅支持一种操作)。

读取套接字可选项:

#include <sys/socket.h>int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

成功时返回0,失败时返回-1。

参数:

  • sock:要查看的套接字的文件描述符。
  • level:要查看的可选项的协议层。
  • optname:要查询的可选项的名称。
  • optval:指向一个缓冲区,该缓冲区将接收查询的选项值。
  • optlen:向optval缓冲区传递的缓冲大小。调用成功后,它会被设置为选项值的实际长度。

更改套接字可选项:

#include <sys/socket.h>int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

成功时返回0,失败时返回-1。

参数:

  • sock:要更改的套接字的文件描述符。
  • level:要更改的可选项的协议层。
  • optname:要更改的可选项的名称。
  • optval:要更改的选项信息的缓冲选项值。
  • optlen:向optval传递的可选项信息的字节数。

示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>void error_handling(char *message);int main(int argc, char *argv[])
{int tcp_sock, udp_sock;int sock_type;socklen_t optlen;int state;optlen = sizeof(sock_type);tcp_sock = socket(PF_INET, SOCK_STREAM, 0);udp_sock = socket(PF_INET, SOCK_DGRAM, 0);printf("SOCK_STREAM: %d \n", SOCK_STREAM);printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &optlen);if (state)error_handling("getsockopt() error!");printf("Socket type one: %d \n", sock_type);state = getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &optlen);if (state)error_handling("getsockopt() error!");printf("Socket type two: %d \n", sock_type);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

本程序分别生成TCP、UDP套接字,输出创建TCP、UDP套接字时传入的SOCK_STREAM、SOCK_DGRAM。获取套接字类型信息,如果是TCP套接字,将获得SOCK_STREAM常数值1;如果是UDP套接字,则获得SOCK_DGRAM的常数值2。

运行结果:

在这里插入图片描述

上述示例给出了调用getsockopt函数查看套接字信息的方法,另外,用于验证套接字类型的SO_TYPE是典型的只读可选项,即套接字类型只能在创建时决定,以后不能再更改。

SO_SNDBUF & SO_RCVBUF

前文中我们提到套接字的输入输出缓冲区,而SO_SNDBUF 和SO_RCVBUF便是与套接字缓冲区大小相关的两个可选项。通过这两个选项我们可以获取当前套接字的输入输出缓冲区大小,或者设置相应缓冲区的大小。如下是这两个选项使用的相关示例代码。

get_buf.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>void error_handling(char *message);int main(int argc, char *argv[])
{int sock;int snd_buf, rcv_buf, state;socklen_t len;sock = socket(PF_INET, SOCK_STREAM, 0);len = sizeof(snd_buf);state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&snd_buf, &len);if (state)error_handling("getsockopt() error");len = sizeof(rcv_buf);state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcv_buf, &len);if (state)error_handling("getsockopt() error");printf("Input buffer size: %d \n", rcv_buf);printf("Outupt buffer size: %d \n", snd_buf);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

运行结果:

在这里插入图片描述

注:每个人的电脑系统不同,运行结果可能会有差异。

set_buf.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>void error_handling(char *message);int main(int argc, char *argv[])
{int sock;int snd_buf = 1024 * 3, rcv_buf = 1024 * 3;int state;socklen_t len;sock = socket(PF_INET, SOCK_STREAM, 0);state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcv_buf, sizeof(rcv_buf));if (state)error_handling("setsockopt() error!");state = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&snd_buf, sizeof(snd_buf));if (state)error_handling("setsockopt() error!");len = sizeof(snd_buf);state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&snd_buf, &len);if (state)error_handling("getsockopt() error!");len = sizeof(rcv_buf);state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcv_buf, &len);if (state)error_handling("getsockopt() error!");printf("Input buffer size: %d \n", rcv_buf);printf("Output buffer size: %d \n", snd_buf);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

运行结果:

在这里插入图片描述

从运行结果可以看出,对于缓冲大小的设置并非完全生效。实际上这些设置只是传递了我们的要求,而最终的生效值操作系统会根据当前环境做出设置,不过配置值的大小趋势和我们期望的一致。

SO_REUSEADDR

发生地址绑定错误(Binding Error)

我们之前介绍了回声服务器端/客户端的实现。其中服务器端代码稍作改变如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define TRUE 1
#define FALSE 0void error_handling(char *message);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;char message[30];int option, str_len;socklen_t optlen, clnt_adr_sz;struct sockaddr_in serv_adr, clnt_adr;if (argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);if (serv_sock == -1)error_handling("socket() error");/*optlen=sizeof(option);option=TRUE;setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, optlen);*/memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)))error_handling("bind() error ");if (listen(serv_sock, 5) == -1)error_handling("listen error");clnt_adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);while ((str_len = read(clnt_sock, message, sizeof(message))) != 0){write(clnt_sock, message, str_len);write(1, message, str_len);}close(clnt_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

客户端通过输入“Q”消息,或是通过CTRL+C终止程序,两种方式客户端都会执行close函数向服务器端传递FIN消息,经过四次挥手后断开连接。

现在考虑另一种情况,如果服务器端和客户端在已建立连接的状态下,向服务器端执行CTRL+C终止程序,会发生什么?

这种情况,服务器端会主动向客户端发送FIN消息断开连接并退出程序。此时,如果再次以相同端口号启动服务器端则会发生错误(bind()报错:“Address already in use”),通常需要等待1~4分钟才能再次运行服务器端。

客户端主动发送FIN消息断开连接,不影响客户端或服务器端的再次运行;而服务器端主动发送FIN消息断开连接,则会影响服务器端的再次运行,为什么会出现这种现象呢?

TIME_WAIT 状态

四次挥手过程:

在这里插入图片描述

从图中可以看出,主动断开连接的主机(先发送FIN消息)会经过TIME_WAIT的状态,持续时间为2MSL(Maximum Segment Lifetime,最长分节生命期,时间为30s~2min)。而处于TIME_WAIT状态时,相应的端口号是正在使用状态,因此,若服务器端先断开连接则无法立即重新运行。

无论是服务器端还是客户端,套接字都会有TIME_WAIT过程。先断开连接的套接字必然会经过TIME_WAIT过程。与服务器端不同,客户端由于每次运行都会动态分配端口号,因此不受TIME_WAIT状态的影响。

TIME_WAIT状态的存在有两个理由:

  • 可靠地实现TCP全双工连接的终止
  • 允许老的重复分节在网络中消逝

第一个理由可以假设上述四次握手过程最终的ACK丢失了来解释。主机B将重新发送它的最终那个FIN,因此主机A必须维护状态信息,以允许它重新发送最终那个ACK。如果主机A不维护状态信息,它将以一个RST(另外一种类型的TCP分节)消息来响应,该分节将被主机B解释为一个错误消息。如果TCP打算执行所有必要的工作以彻底终止某个连接上两个方向的数据流(即全双工关闭),那么它必须正确处理连接终止序列4个分节中任何一个分节丢失的情况。本例也说明了为什么执行主动关闭的那一端需要处于TIME_WAIT状态,因为它可能不得不重传最终那个ACK。

为理解存在TIME_WAIT状态的第二个理由,我们假设在12.106.32.254的1500端口和206.168.112.219的21端口之间有一个TCP连接。我们关闭这个连接,过一段时间后在相同的IP地址和端口之间建立另一个连接。后一个连接称为前一个连接的化身(incarnation),因为它们的IP地址和端口号都相同。TCP必须防止来自某个连接的老的重复分组在该连接已终止后再现,从而被误解成属于同一连接的某个新的化身。为做到这一点,TCP将不给处于TIME_WAIT状态的连接发起新的化身。既然TIME_WAIT状态的持续时间是MSL的2倍,这就足以让某个方向上的分组最多存活MSL秒即被丢弃,另一个方向上的应答最多存活MSL秒也被丢弃。通过实施这个规则,我们就能保证每成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已在网络中消逝了(单向传输一个分节的最长生命周期是MSL,TIME-WAIT状态的2MSL是考虑了一次双向信息交互的最长时间。比如最后的ACK丢失后,来自对端重发的FIN消息也会在2MSL内消逝)。

地址再分配

从上文的描述来看,TIME_WAIT状态在可靠通信过程中似乎起到了重要的作用,但它也有其自身的缺点。

比如下图的情况,收到FIN消息的主机A发送ACK消息至主机B并启动Time-wait定时器,如果网络状态不好致使ACK消息不断丢失,则主机B重传FIN消息,收到FIN消息的主机A将重启Time-wait定时器,TIME-WAIT状态可能一直持续下去。

在这里插入图片描述

另一种情况,考虑正在工作中的服务器突然故障停机而需要快速重启,这时由于TIME_WAIT状态则必须等几分钟,也会带来严重的影响。

针对以上TIME_WAIT状态所带来的影响,可以通过配置可选项SO_REUSEADDR来解决。默认情况下,SO_REUSEADDR选项处于关闭状态(值为0,假),即无法分配处于TIME_WAIT状态下套接字端口。因此,我们需要将该选项置为1(真)即可。

int opt_val = 1;    
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt_val, sizeof(opt_val));

SO_REUSEADDR可选项有效解决了以上问题,UNP中也有这么一句描述:“所有TCP服务器都应该指定本套接字选项,以允许服务器在这种情形下被重新启动”。同时我们也应该意识到SO_REUSEADDR其实无视了TIME_WAIT状态的一些作用,此时如果收到一些不期望的数据(旧连接的分片)可能会导致服务程序混乱,不过这种可能性极低。

TCP_NODELAY

Nagle 算法

Nagle算法的出现是为了防止因数据包过多而导致的网络过载,它应用于TCP层,其作用如下图所示。

在这里插入图片描述

不难看出,只有收到前一数据的ACK消息时,Nagle算法才会发送下一数据。TCP套接字默认使用Nagle算法,因此可以最大限度地进行缓冲,直到收到ACK。

上图的演示中,使用Nagle算法发送一个字符串消息需要传递4个数据包,而不使用Nagle算法则需要传递10个数据包,对网络流量(Traffic,网络负载或混杂程度)产生了较大的影响。当然,上图的演示只是一种极端的情况(特定场景下,字符串中的字符需要间隔一定的时间来传输至缓冲区),实际程序中将字符串传输至缓冲区并非逐个字符进行的。

根据数据传输的特性,网络流量未受太大影响时,不使用Nagle算法反而更快。典型的场景就是“传输大文件数据”,此时即使不使用Nagle算法,也会在填满缓冲区时传输数据。这种情况并没有增加数据包的数量,反而由于无需等待ACK而可以连续传输,大大提高了传输速度。

是否使用Nagle算法,需要根据使用与否对网络流量影响的差别大小确定。通常情况,不使用Nagle算法可以提高传输速度。但为了保证网络流量,在未准确判断数据特性时不应该禁止Nagle算法。

Nagle 算法的优缺点

优点:

  • 减少网络拥塞: 通过减少小包的数量,可以降低网络拥塞,特别是在高延迟网络中。
  • 提高带宽利用率: 通过发送更大的数据包,可以更有效地利用可用带宽。

缺点:

  • 增加交互式应用的延迟: 对于需要快速响应的交互式应用(如远程登录或在线游戏),Nagle算法可能会导致感知上的延迟,因为它延迟了小数据包的发送。
  • 与某些应用协议不兼容: 一些应用协议依赖于小数据包的快速传输,Nagle算法可能会干扰这些协议的正常工作。

禁用 Nagle 算法

如果有必要,就应禁用Nagle算法。

Nagle 算法使用与否在网络流量上差别不大。若使用了Nagle 算法,传输速度更慢。

禁止Nagle算法的方法就是将套接字可选项TCP_NODELAY改为1(真):

int opt_val = 1;    
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val, sizeof(opt_val));

也可以查看Nagle算法的设置状态:

int opt_val;
socklen_t opt_len = sizeof(opt_val);  
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val, &opt_len);

如果正在使用Nagle算法,opt_val = 0;否则为 1。

基于 Windows 的实现

套接字可选项及其相关内容与操作系统无关。

#include <winsock2.h>int getsockopt(SOCKET sock, int level, int optname, char *optval, int *optlen);
int setsockopt(SOCKET sock, int level, int optname, const char *optval, int optlen);

成功时返回0,失败时返回 SOCKET_ERROR。

基于 Windows 的套接字类型示例程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>void ErrorHanding(char *message);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET tcp_sock, udp_sock;int sock_type;int opt_len;int state;if (argc != 2){printf("Usage : %s <addr>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");tcp_sock = socket(PF_INET, SOCK_STREAM, 0);udp_sock = socket(PF_INET, SOCK_DGRAM, 0);printf("SOCK_STREAM: %d \n", SOCK_STREAM);printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);opt_len = sizeof(sock_type);state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &opt_len);if (state)ErrorHanding("getsockopt() error!");printf("Socket type one: %d \n", sock_type);state = getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void *)&sock_type, &opt_len);if (state)ErrorHanding("getsockopt() error!");printf("Socket type two: %d \n", sock_type);closesocket(tcp_sock);closesocket(udp_sock);WSACleanup();return 0;
}void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

编译:

gcc sock_type_win.c -lwsock32 -o sockTypeWin

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 9>sockTypeWin
Usage : sockTypeWin <addr>C:\Users\81228\Documents\Program\TCP IP Project\Chapter 9>sockTypeWin 9190
SOCK_STREAM: 1
SOCK_DGRAM: 2
Socket type one: 1
Socket type two: 2

基于 Windows 的套接字 I/O 缓冲大小示例程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>void ErrorHanding(char *message);
void ShowSocketBufSize(SOCKET sock);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET sock;int sndBuf, rcvBuf, state;if (argc != 2){printf("Usage : %s <addr>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");sock = socket(PF_INET, SOCK_STREAM, 0);ShowSocketBufSize(sock);sndBuf = rcvBuf = 3 * 1024;state = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&sndBuf, sizeof(sndBuf));if (state == SOCKET_ERROR)ErrorHanding("setsockopt() error!");state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&rcvBuf, sizeof(rcvBuf));if (state == SOCKET_ERROR)ErrorHanding("setsockopt() error!");ShowSocketBufSize(sock);closesocket(sock);WSACleanup();return 0;
}void ShowSocketBufSize(SOCKET sock)
{int sndBuf, rcvBuf, state, len;len = sizeof(sndBuf);state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&sndBuf, &len);if (state == SOCKET_ERROR)ErrorHanding("getsockopt() error!");len = sizeof(rcvBuf);state = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&rcvBuf, &len);if (state == SOCKET_ERROR)ErrorHanding("getsockopt() error!");printf("Input buffer size: %d\n", rcvBuf);printf("Output buffer size: %d\n", sndBuf);
}void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

编译:

gcc buf_win.c -lwsock32 -o bufWin

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 9>bufWin 9190
Input buffer size: 65536
Output buffer size: 65536
Input buffer size: 3072
Output buffer size: 3072

基于 Windows 的 TCP_NODELAY 示例程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>void ErrorHanding(char *message);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET sock;int opt_val;int opt_len;int state;if (argc != 2){printf("Usage : %s <addr>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");sock = socket(PF_INET, SOCK_STREAM, 0);opt_len = sizeof(opt_val);state = getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt_val, &opt_len);if (state)ErrorHanding("getsockopt() error!");printf("TCP_NODELAY value: %d \n", opt_val);opt_val = 0;opt_len = sizeof(opt_val);state = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt_val, opt_len);if (state)ErrorHanding("setsockopt() error!");opt_len = sizeof(opt_val);state = getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&opt_val, &opt_len);if (state)ErrorHanding("getsockopt() error!");printf("TCP_NODELAY value: %d \n", opt_val);closesocket(sock);WSACleanup();return 0;
}void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

编译:

gcc tcp_nodelay_win.c -lwsock32 -o nagleWin

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 9>nagleWin 9190
TCP_NODELAY value: 6356992
TCP_NODELAY value: 0

习题

(1)下列关于Time-wait状态的说法错误的是?

a. Time-wait状态只在服务器端的套接字中发生。
b. 断开连接的四次握手过程中,先传输FIN消息的套接字将进入Time-wait状态。
c. Time-wait状态与断开连接的过程无关,而与请求连接过程中SYN消息的传输顺序有关。
d. Time-wait状态通常并非必要,应尽可能通过更改套接字可选项防止其发生。

答:a、c、d。

(2)TCP_NODELAY可选项与Nagle算法有关,可通过它禁用Nagle算法。请问何时应考虑禁用Nagle算法?结合收发数据的特性给出说明。

根据数据传输的特性,网络流量未受太大影响时,可以考虑禁用Nagle算法。

典型的场景就是“传输大文件数据”,此时即使不使用Nagle算法,也会在填满缓冲区时传输数据。这种情况并没有增加数据包的数量,反而由于无需等待ACK而可以连续传输,大大提高了传输速度。

相关文章:

《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项

《TCP/IP网络编程》学习笔记 | Chapter 9&#xff1a;套接字的多种可选项 《TCP/IP网络编程》学习笔记 | Chapter 9&#xff1a;套接字的多种可选项套接字可选项和 I/O 缓冲大小套接字多种可选项getsockopt & setsockoptSO_SNDBUF & SO_RCVBUF SO_REUSEADDR发生地址绑定…...

渗透测试---网络基础之HTTP协议与内外网划分

声明&#xff1a;学习素材来自b站up【泷羽Sec】&#xff0c;侵删&#xff0c;若阅读过程中有相关方面的不足&#xff0c;还请指正&#xff0c;本文只做相关技术分享,切莫从事违法等相关行为&#xff0c;本人一律不承担一切后果 目录 一、HTTP协议各版本介绍 二、HTTP请求的方…...

15分钟学 Go 第 45 天 : 使用Docker容器

第45天&#xff1a;使用Docker容器 目标 在本节中&#xff0c;我们将深入了解Docker及其基本用法&#xff0c;掌握如何使用Docker容器来简化开发和部署流程。 背景知识 Docker是一个开源平台&#xff0c;用于开发、运输和运行应用程序。它使我们能够使用容器技术将应用程序…...

DriveLM 论文学习

论文链接&#xff1a;https://arxiv.org/pdf/2312.14150 代码链接&#xff1a;https://github.com/OpenDriveLab/DriveLM 解决了什么问题&#xff1f; 当前&#xff0c;自动驾驶方案的性能仍然不足。一个必要条件就是泛化能力&#xff0c;需要模型能处理未经训练的场景或不熟…...

YoloV10改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码

论文介绍 CARAFE模块概述&#xff1a;本文介绍了一种名为CARAFE&#xff08;Content-Aware ReAssembly of FEatures&#xff09;的模块&#xff0c;它是一种用于特征上采样的新方法。应用场景&#xff1a;CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程&#xff0…...

光模块基础知识

1. 光模块的封装 光模块是光收发模块的简称&#xff0c;主要根据不同的外型来区分&#xff0c;而在同一外型中&#xff0c;又有着多种规格&#xff1b;在数据通信领域&#xff0c;最常见的光模块&#xff08;根据外型区分&#xff09;分别是SFF、GBIC、SFP、和XFP、QSFP 、XEN…...

【go从零单排】Closing Channels通道关闭、Range over Channels

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;通道&#xff08;channel&#xff09;的关闭是一个重要…...

初始JavaEE篇 —— 文件操作与IO

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 文件介绍 Java标准库中提供操作文件的类 文件系统操作 File类的介绍 File类的使用 文件内容操作 二进制文件的读写操作…...

GitLab实现 HTTP 访问和 SMTP 邮件发送

GitLab实现 HTTP 访问和 SMTP 邮件发送 本教程详细记录了如何配置 SMTP 邮件通知、实现外网 HTTP 访问&#xff0c;并分享在配置过程中遇到的问题及解决方法。 一、准备工作 安装 Docker&#xff1a;确保在 Synology NAS 上安装 Docker 应用。下载 GitLab 镜像&#xff1a;在…...

HarmonyOS ArkTS 下拉列表组件

Entry Component struct Index {defaultValue: string 下拉列表;// 定义选项数组&#xff0c;包含 value 和可选的 labeloptions: Array<SelectOption> [{ value: aaa },{ value: bbb },{ value: ccc },{ value: ddd },{ value: eee },{ value: fff },{ value: ggg },{…...

zabbix监控Linux系统

1. zabbix agent安装 #sudo rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/8/x86_64/zabbix-release-6.0-4.el8.noarch.rpm #sudo dnf clean all #yum install zabbix-agent -y Running transaction test Transaction test succeeded. Running transactionPreparing …...

线性表-数组描述补充 迭代器(C++)

补充线性表数组实现的迭代器部分 知识点&#xff1a; typedef是C语言中的一个关键字&#xff0c;它的主要作用是为一种数据类型定义一个新的名字&#xff08;别名&#xff09;。 在 C 的 STL&#xff08;Standard Template Library&#xff09;中&#xff0c;迭代器是连接容…...

vue3 + element-plus 的 upload + axios + django 文件上传并保存

之前在网上搜了好多教程&#xff0c;一直没有找到合适自己的&#xff0c;要么只有前端部分没有后端&#xff0c;要么就是写的不是很明白。所以还得靠自己摸索出来后&#xff0c;来此记录一下整个过程。 其实就是不要用默认的 action&#xff0c;要手动实现上传方式 http-reque…...

dm 创建数据库实例【window】

参考链接&#xff1a;配置实例 1&#xff09;打开 DM 数据库配置助手 2&#xff09;按照默认的进行 字符串大小写敏感&#xff1a;譬如 mysql 默认是大小写不敏感&#xff0c;如果在迁移中还选择了 保持对象大小写&#xff0c;那么就会出现一种情况就是每次查询等带有表名的都…...

Docker实践与应用举例:从入门到进阶

Docker实践与应用举例&#xff1a;从入门到进阶 在云计算和微服务架构日益盛行的今天&#xff0c;Docker作为一种轻量级的容器化技术&#xff0c;凭借其高效、灵活、可移植的特点&#xff0c;迅速成为了开发和运维团队的首选工具。本文将通过深入浅出的方式&#xff0c;探讨Do…...

【LeetCode】【算法】560. 和为 K 的子数组

LeetCode 560. 和为 K 的子数组 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 思路 思路&#xff1a;前缀和 定义数组preSum[nums.length1]&#xff0c;在里面计算nums…...

Webots控制器编程

本文主要内容是如何编写Webots控制器&#xff0c;使用语言为Python。 文章目录 1. 新增控制器2. Hello World Example3. 读取传感器4. 使用执行器5. 理解step和robot.step函数6. 同时使用传感器和执行器7. 控制器参数 1. 新增控制器 对机器人Robot新增控制器的方式&#xff1…...

舷外机,高效动力的选择,可靠性能的保障_鼎跃安全

舷外机是现代船只动力系统中的核心设备&#xff0c;广泛应用于娱乐船、渔船、巡逻船、救援船等多种场景。它不仅提供船只的动力支持&#xff0c;还因其结构简便、操作灵活和维护方便&#xff0c;成为水上作业的重要组成部分。 一、舷外机的功能作用 1. 强劲动力源 舷外机是船…...

计算机新手练级攻略——如何搜索问题

目录 计算机学生新手练级攻略——如何搜索问题1.明确搜索意图2.使用精确关键词3.使用专业引擎搜索4.利用好技术社区1. Stack Overflow2. GitHub3. IEEE Xplore4. DBLP 5.使用代码搜索工具1. GitHub 代码搜索2. Stack Overflow 代码搜索3. Papers with Code4. IEEE Xplore 6.查阅…...

echarts-gl 3D柱状图配置

1. 源码 此demo可以直接在echarts的编辑器中运行 option {title: {text: 产量图,textStyle: {color: rgba(255, 255, 255, 1),fontSize: 17},left: center},tooltip: {},legend: {show: false,orient: vertical,x: left,top: 0,right: 20,textStyle: {fontSize: 12}},visualM…...

【芯片仿真中的X值:隐藏的陷阱与应对之道】

在芯片设计的世界里&#xff0c;X值&#xff08;不定态&#xff09;就像一个潜伏的幽灵。它可能让仿真测试顺利通过&#xff0c;却在芯片流片后引发灾难性后果。本文将揭开X值的本质&#xff0c;探讨其危害&#xff0c;并分享高效调试与预防的实战经验。    一、X值的本质与致…...

SeaweedFS S3 Spring Boot Starter

SeaweedFS S3 Spring Boot Starter 源码特性环境要求快速开始1. 添加依赖2. 配置文件3. 使用方式方式一&#xff1a;注入服务类方式二&#xff1a;使用工具类 API 文档SeaweedFsS3Service 主要方法SeaweedFsS3Util 工具类方法 配置参数运行测试构建项目注意事项集成应用更多项目…...

RMQ 算法详解(区间最值问题)

RMQ 算法详解&#xff08;区间最值问题&#xff09; 问题介绍解决方法暴力法ST表法基本思想算法步骤C实现 问题介绍 RMQ问题是OI中经常遇到的问题&#xff0c;主要是一下形式&#xff1a; 给你一堆数&#xff0c;不断的对里面的数进行操作&#xff0c;例如&#xff1a;让某个…...

Kotlin REPL初探

文章目录 1. Kotlin REPL 简介2. 在命令行中玩Kotlin REPL2.1 下载Kotlin编译器压缩包2.2 安装配置Kotlin编译器2.3 启动Kotlin交互式环境2.4 在命令行玩Kotlin REPL 3. 在IDEA里玩Kotlin REPL3.1 打开Kotlin REPL窗口3.2 在Kotlin REPL窗口玩代码 4. Kotlin REPL 的优势 1. Ko…...

【网络安全】fastjson原生链分析

fastjson 原生链 前言 说起 fastjson 反序列化&#xff0c;大部分的利用都是从 type 把 json 串解析为 java 对象&#xff0c;在构造方法和 setter、getter 方法中&#xff0c;做一些文件或者命令执行的操作。当然&#xff0c;在 fastjson 的依赖包中&#xff0c;也存在着像 …...

26考研——数据的表示和运算_整数和实数的表示(2)

408答疑 文章目录 二、整数和实数的表示1、整数的表示1.1、无符号整数的表示1.2、有符号整数的表示1.3、C 语言中的整数类型及类型转换1.3.1、C 语言中的整型数据类型1.3.2、有符号数和无符号数的转换1.3.3、不同字长整数之间的转换 2、实数的表示2.1、浮点数的相关概念2.2、浮…...

​​​​​​​6板块公共数据典型应用场景【政务服务|公共安全|公共卫生|环境保护|金融风控|教育科研]

1. 政务服务 1.1 城市规划与管理 公共数据在城市规划与管理中可发挥关键作用。通过汇聚自然资源、建筑物、人口分布等基础数据,构建数字孪生城市模型,辅助城市总体规划编制、决策仿真模拟。在城市基础设施建设、安全运营、应急管理等方面,公共数据也是不可或缺的基础支撑。例…...

使用 Ansible 在 Windows 服务器上安装 SSL 证书系列之二

今天带大家实战一下如何通过ansible在windows 服务器上给iis web site安装证书。 前提条件: 准备一张pfx证书,可以通过openssl工具来生成,具体的步骤请参考帮助文档。一台安装了iis 的windows 服务器 准备inventory文件 [windows] solarwinds ansible_host=20.47.126.72 a…...

MCP(Model Context Protocol)与提示词撰写

随着大模型&#xff08;LLM&#xff09;在复杂任务中的普及&#xff0c;如何让模型高效调用外部工具和数据成为关键挑战。传统函数调用&#xff08;Function Calling&#xff09;依赖开发者手动封装 API&#xff0c;而 MCP&#xff08;Model Context Protocol&#xff09; 通过…...

Elasticsearch:spring2.x集成elasticsearch8.x

相关安装就不介绍了直接代码集成 <!-- elasticsearch版本需要和你安装的版本一致 --><properties><elasticsearch.version>8.11.1</elasticsearch.version><jakarta-json.version>2.1.2</jakarta-json.version><logstash.version>7…...