c++ socket (3) IPv4、IPv6兼容处理
nanshan 2024-10-04 18:03 24 浏览 0 评论
IP6是大势所趋(十多年前我也是这样听说的, 哈), 所以要考虑IPv4\IPv6的兼容性.
代码示例
a、IPv6兼容IPv4,所以在服务器监听时,尽量使用IPv6.
b、客户端通过getaddrinfo 获取服务端地址
公共部分头文件(socket.h)
#ifndef __SOCKET_H__
#define __SOCKET_H__
#include <memory>
#include <string>
#include <memory.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <ws2tcpip.h>
#define socket_t SOCKET
#define invalid_socket INVALID_SOCKET
#define socklen_t int
#pragma comment(lib, "ws2_32.lib")
#endif // _WIN32
#ifdef __linux__
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define socket_t int
#define invalid_socket -1
#define closesocket close
#endif
class Socket {
static bool init_socket()
{
#ifdef _WIN32
WSADATA ws;
static bool _init = (0 == WSAStartup(MAKEWORD(2, 2), &ws));
return _init;
#endif
return true;
}
public:
//创建服务器(优先使用IPv6)
static socket_t CreateServer(int port, int type = SOCK_STREAM)
{
init_socket();
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // 任何地址家族
hints.ai_socktype = type; // 任何套接字类型
hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
int ret = 0;
struct addrinfo* res;
if ((ret = getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &res)) != 0) {
std::cout << "err getaddrinfo:" << gai_strerror(ret) << std::endl;
return invalid_socket;
}
int reuseAddr = 1;
socket_t skt = invalid_socket;
for (struct addrinfo* p = res; p != 0; p = p->ai_next) {
if ((p->ai_family != AF_INET && p->ai_family != AF_INET6)
|| (skt != invalid_socket && p->ai_family == AF_INET)) {
continue;
}
//关闭之前的IPv4
if (skt != invalid_socket) {
closesocket(skt);
skt = invalid_socket;
}
//创建socket 及 bind
if (invalid_socket == (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
|| 0 != setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddr, sizeof(int))
|| 0 != bind(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {
if (skt != invalid_socket) {
closesocket(skt);
skt = invalid_socket;
}
continue;
}
//如果是IPv6,就不必再继续了
if (skt != invalid_socket && p->ai_family == AF_INET6) {
break;
}
}
freeaddrinfo(res);
return skt;
}
//连接服务端
static socket_t ConnectServer(const char* addr, int port)
{
init_socket();
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int ret = 0;
struct addrinfo* res;
if ((ret = getaddrinfo(addr, std::to_string(port).c_str(), &hints, &res)) != 0) {
std::cerr << "getaddrinfo:" << gai_strerror(ret) << std::endl;
return invalid_socket;
}
int reuseAddr = 1;
socket_t skt = invalid_socket;
for (struct addrinfo* p = res; p != 0 && skt == invalid_socket; p = p->ai_next) {
if ((p->ai_family != AF_INET && p->ai_family != AF_INET6)) {
continue;
}
//创建socket 及 connect
if (invalid_socket != (skt = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
&& 0 != connect(skt, (const struct sockaddr*)p->ai_addr, p->ai_addrlen)) {
std::cerr << "connect:" << strerror(errno);
closesocket(skt);
skt = invalid_socket;
}
}
freeaddrinfo(res);
return skt;
}
};
#endif
服务器
int main()
{
socket_t skt;
if (invalid_socket == (skt = Socket::CreateServer(80))) {
std::cerr << "CreateServer : " << strerror(errno) << std::endl;
return 0;
}
if (0 != listen(skt, 255)) {
return 0;
}
char addr[128] = { 0 };
socklen_t addrLen = 28;
socket_t client = accept(skt, (struct sockaddr*)addr, &addrLen);
//获取对端地址
char node[128] = { 0 };
char port[32] = { 0 };
if (0 == getnameinfo((const sockaddr*)addr, addrLen, node, 128, port, 32, 0)) {
if (0 == memcmp(node, "::ffff:", strlen("::ffff:"))) {
std::cout << "IPv4 connect : " << node + strlen("::ffff:") << " : " << port << std::endl;
}
else {
std::cout << "IPv6 connect :" << node << " : " << port << std::endl;
}
}
//.....
closesocket(client);
closesocket(skt);
return 0;
}
客户端
int main()
{
socket_t skt;
if (invalid_socket != (skt = Socket::ConnectServer("www.baidu.com", 80))) {
std::cout << "connect www.baidu.com ok" << std::endl;
closesocket(skt);
}
if (invalid_socket != (skt = Socket::ConnectServer("learn.microsoft.com", 80))) {
std::cout << "connect learn.microsoft.com ok" << std::endl;
closesocket(skt);
}
if (invalid_socket != (skt = Socket::ConnectServer("fe80::53bd:b44a:cf4a:d202", 80))) {
std::cout << "connect fe80::53bd:b44a:cf4a:d202 ok" << std::endl;
closesocket(skt);
}
if (invalid_socket != (skt = Socket::ConnectServer("192.168.1.21", 80))) {
std::cout << "connect 192.168.1.21 ok" << std::endl;
closesocket(skt);
}
return 0;
}
文心一言代码解析:
init_socket()函数: 这个函数是用来初始化套接字库的。
如果代码在Windows平台上运行(通过#ifdef _WIN32判断),它将调用WSAStartup()函数来初始化Windows套接字库,并检查是否成功初始化。如果成功,返回true;否则返回false。在其他平台上,这个函数总是返回true。
CreateServer()函数: 这个函数尝试创建一个服务器套接字并绑定到指定的端口。函数的参数包括端口号和套接字类型(默认为SOCK_STREAM,即TCP)。
- 首先,它调用init_socket()来初始化套接字库。
- 然后,它使用getaddrinfo()函数来获取与给定端口和地址家族相关的地址信息。这个函数返回一个地址信息链表,包含了所有可能的地址(IPv4和IPv6)。
- 接着,函数遍历这个地址链表,尝试为每个地址创建一个套接字并绑定它。如果成功,它会关闭之前创建的所有套接字(如果有的话),然后继续处理下一个地址。
- 如果成功创建了套接字并绑定了某个地址,并且这个地址是IPv6地址,那么函数会停止处理其他地址,并返回创建的套接字。
- 最后,无论成功与否,都会使用freeaddrinfo()函数释放之前获取的地址信息。 CreateServer主要目的是为了确保服务器套接字优先使用IPv6地址。如果系统支持IPv6,并且可以绑定到IPv6地址,那么它就会停止处理其他地址并返回套接字。否则,它将尝试使用IPv4地址。这有助于确保应用程序可以同时支持IPv4和IPv6,从而提供更好的网络连接性。
ConnectServer(): 这个函数用于连接到服务器。
- 它首先调用init_socket()来初始化套接字库。然后,它使用getaddrinfo()函数来获取与给定地址和端口相关的地址信息。如果getaddrinfo()返回非零值,表示出现错误,该错误消息会被打印到标准错误输出,并返回一个无效的套接字描述符。
- 接下来,代码会遍历获取到的地址信息,尝试为每个地址创建一个套接字并连接到该地址。如果成功,它会继续尝试下一个地址。如果套接字创建或连接失败,它会关闭套接字并继续处理下一个地址。
- 最后,使用freeaddrinfo()函数释放之前获取的地址信息,并返回创建的套接字描述符。如果连接成功,它将是一个有效的套接字描述符;如果连接失败,它将是一个无效的套接字描述符。
函数说明:
getaddrinfo 函数 (ws2tcpip.h) - Win32 apps | Microsoft Learn
getnameinfo 函数 (ws2tcpip.h) - Win32 apps | Microsoft Learn
getaddrinfo(3) - Linux manual page
getnameinfo(3) - Linux manual page
getaddrinfo()是一个用于网络编程的函数,它用于将主机名和服务名解析为网络地址结构。这个函数是在套接字编程中经常使用的,特别是在处理DNS查找和地址解析时。
函数原型通常如下:
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
参数说明:
- node:主机名或IP地址字符串。如果为NULL,表示使用通配地址。
- service:服务名或端口号字符串。如果为NULL,表示使用默认服务。
- hints:指向一个addrinfo结构体的指针,该结构体包含用于指导地址解析的提示信息。例如,可以指定地址族、套接字类型和协议。
- res:指向一个指向addrinfo结构的指针的指针,该结构体将包含解析出的地址信息。 返回值:
- 如果成功,返回0,并将res指向一个链表,链表中包含解析出的地址信息。
- 如果失败,返回一个非零错误码,并将res设置为NULL。
使用getaddrinfo()函数,你可以将主机名和服务名解析为网络地址结构,这在创建套接字、绑定地址和进行网络通信时非常有用。该函数能够处理各种地址类型和协议,使得在网络编程中更加灵活和方便。
getnameinfo()是一个用于网络编程的函数,它用于将网络地址结构(通常由getaddrinfo()函数返回)转换为主机名和服务名。这个函数是在套接字编程中经常使用的,特别是在处理DNS查找和地址解析时。
函数原型通常如下:
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *serv, size_t servlen, int flags);
参数说明:
- sa:指向一个sockaddr结构的指针,该结构包含要解析的网络地址。
- salen:sockaddr结构的大小。
- host:指向一个字符数组的指针,用于存储解析出的主机名。
- hostlen:host数组的大小。
- serv:指向一个字符数组的指针,用于存储解析出的服务名。
- servlen:serv数组的大小。
- flags:一组标志位,用于控制函数的行为。例如,NI_NUMERICHOST表示主机名应以数字形式返回,而NI_NUMERICSERV表示服务名应以数字形式返回。 返回值:
- 如果成功,返回0。
- 如果失败,返回一个非零错误码。
使用getnameinfo()函数,你可以将网络地址转换为可读的主机名和服务名,这在调试、日志记录或用户界面中可能是非常有用的。
getaddrinfo一些资料
它的由来:
getaddrinfo函数的由来可以追溯到IPv4和IPv6共存的时期。在早期,网络编程主要使用gethostbyname系列函数来将主机名转换为IP地址。然而,随着IPv6的普及和网络协议的发展,传统的gethostbyname函数逐渐无法满足需求。由于IPv6地址是128位,传统的点分十进制表示法不再适用,需要使用新的表示法。此外,随着网络协议的多样化和复杂化,应用程序需要更灵活的方式来处理不同的协议和套接字类型。
为了解决这些问题,getaddrinfo函数被引入到网络编程中。它是在1990年代末期设计的,并在POSIX.1-2001标准中被正式定义。该函数最初是为了支持IPv6而设计的,但后来也被扩展到支持IPv4。
getaddrinfo函数的设计目标是提供一个统一的接口,用于将主机名和服务名转换为套接字地址结构,同时支持IPv4和IPv6。它隐藏了底层协议的复杂性,使得应用程序可以更加方便地处理网络地址和端口的转换。
该函数最初是在Unix-like操作系统中实现的,但后来也被移植到了其他操作系统中,如Windows。在Windows系统中,getaddrinfo函数是通过Winsock2 API提供的,并且与IPv6的兼容性更好。
综上所述,getaddrinfo函数的由来是为了解决IPv4和IPv6共存时期的需求,提供一种更加灵活、方便的网络地址转换接口。它是在1990年代末期设计的,并在POSIX标准中被定义。该函数最初是为了支持IPv6而设计的,但后来也被扩展到支持IPv4,并且被广泛应用于各种操作系统中。
该函数的作用主要包括:
1. 名字到地址以及服务到端口的转换。它返回一个sockaddr结构的链表,这些sockaddr地址结构随后可有套接口函数(socket、bind、connect、listen等)直接调用,将协议相关性隐藏在该函数内部。
2. 隐藏协议相关性。应用程序只要处理由getaddrinfo函数填写的套接口地址结构,不需要考虑数据尾端,地址转换等。
getaddrinfo的原理是:根据hints参数指定的参数要求,获取服务器信息,并将获取的结果存储到res中。如果应用程序需要使用主机名代替IP地址,或服务名代替端口号,需要先把对应关系增加到对应的配置文件中,否则getaddrinfo会解析出错。
此外,getaddrinfo函数在POSIX规范中定义,它能够处理协议无关的转换,既可用于IPv4也可用于IPv6。它能够根据给定的主机名和服务名,返回一个包含用于创建套接字对象的五元组(包含地址族、套接字类型、协议等)。
getaddrinfo函数还有一些其他的特点和作用:
1. 地址和端口号转换:除了将主机名转换为IP地址外,getaddrinfo还可以将服务名转换为端口号。这对于一些非标准的服务名(如“http”或“ftp”)尤其有用,这些服务名无法通过传统的getservbyname函数直接转换。
2. 套接字类型和协议处理:getaddrinfo可以根据需要进行协议和套接字类型的转换。这使得应用程序可以更灵活地处理不同的网络协议和套接字类型,而无需自行处理底层细节。
3. 错误处理:getaddrinfo函数在出现错误时会返回一个特定的错误码,而不是直接失败。这使得应用程序能够更好地处理错误情况,并采取适当的措施。
4. 异步操作:getaddrinfo函数支持异步操作,这意味着它可以在后台进行地址解析,而不会阻塞应用程序的主线程。这对于需要快速响应的应用程序非常有用。
5. 可移植性:getaddrinfo函数的设计考虑到了跨平台可移植性。无论是在IPv4还是IPv6环境下,它都能够返回适用于任何协议族的地址。这使得使用getaddrinfo的应用程序更加易于移植和维护。
总的来说,getaddrinfo函数在网络编程中起着重要的作用,它使得应用程序可以更方便、灵活地处理网络地址和端口的转换,同时还隐藏了底层协议的复杂性。
除了上述提到的特点,getaddrinfo函数还有以下一些重要功能:
1. 负载均衡和故障转移:通过getaddrinfo函数,可以获取主机名对应的多个IP地址,从而实现负载均衡和故障转移。当某个IP地址出现故障时,可以自动切换到其他可用的IP地址上。
2. 可重入性:getaddrinfo函数的可重入性取决于其内部调用的gethostbyname和getservbyname函数是否是它们的可重入版本。这意味着该函数可以在多线程环境中安全使用,而不会产生竞态条件。
3. IPv6支持:getaddrinfo函数是协议无关的,既可用于IPv4也可用于IPv6。在IPv6环境中,它能够处理IPv6地址的转换,使得应用程序能够更好地支持IPv6协议。
4. 地址结构链表:getaddrinfo函数返回的是一个addrinfo结构的链表,每个结构体包含了一个地址的信息,包括IP地址、端口号、协议类型等。这个链表可以由套接字函数直接使用,简化了网络编程的复杂性。
5. 兼容性:getaddrinfo函数兼容了传统的gethostbyname和getservbyname函数,这意味着在某些情况下,可以使用这些函数作为替代方案。然而,由于getaddrinfo函数具有更多的功能和更好的性能,因此推荐使用该函数进行网络名称解析。
综上所述,getaddrinfo函数在网络编程中起着非常重要的作用,它提供了一种方便、灵活、可靠的方式来处理网络地址和端口的转换。通过使用该函数,应用程序可以更好地支持各种网络协议和地址类型,同时简化网络编程的复杂性。
函数的原理:
getaddrinfo函数的原理是通过DNS(Domain Name System)进行主机名解析。DNS是一个分布式的命名系统,用于将主机名转换为IP地址。当使用getaddrinfo函数时,它会调用系统的DNS解析库,向DNS服务器发送查询请求,获取主机名对应的IP地址。
getaddrinfo函数的设计目标是提供一个统一的接口,用于将主机名和服务名转换为套接字地址结构,同时支持IPv4和IPv6。它隐藏了底层协议的复杂性,使得应用程序可以更加方便地处理网络地址和端口的转换。
工作原理可以分为以下几个步骤:
1. 参数检查:getaddrinfo函数首先会检查传入的参数是否有效。这些参数包括主机名、服务名、套接字类型和协议类型等。如果参数无效,函数将返回错误码。
2. 模板检查:getaddrinfo函数可以接受一个名为hints的参数,该参数指定了期望的地址类型。函数会根据hints参数中的值来过滤返回的地址结构。如果hints参数为空,函数将返回所有可用的地址结构。
3. DNS查询:getaddrinfo函数调用系统的DNS解析库,向DNS服务器发送查询请求,以获取主机名对应的IP地址。如果主机名是一个域名,函数将尝试将其解析为IP地址。如果主机名是一个IP地址,函数将直接使用该地址。
4. 地址转换:在获取到IP地址后,getaddrinfo函数会根据hints参数中的值进行地址转换。如果hints参数指定了特定的套接字类型或协议类型,函数将尝试将其转换为对应的地址结构。
5. 链表构建:getaddrinfo函数将返回一个addrinfo结构的链表,每个结构体包含了一个地址的信息,包括IP地址、端口号、协议类型等。这个链表可以由套接字函数直接使用,简化了网络编程的复杂性。
6. 错误处理:如果在查询过程中出现错误,getaddrinfo函数将返回一个错误码。应用程序可以通过检查返回值来判断是否出现了错误,并根据需要进行相应的处理。
综上所述,getaddrinfo函数的原理是通过DNS进行主机名解析,并返回一个addrinfo结构的链表。它通过隐藏底层协议的复杂性,使得应用程序可以更加方便地处理网络地址和端口的转换。
getaddrinfo函数虽然具有很多优点,但也存在一些缺点。以下是一些可能的缺点:
1. 性能开销:相对于传统的gethostbyname函数,getaddrinfo函数需要进行DNS查询,这会增加一定的性能开销。对于频繁进行地址转换的应用程序,可能会影响性能。
2. 依赖DNS:getaddrinfo函数依赖于DNS进行主机名解析,如果DNS服务器不可用或者网络连接出现问题,可能会导致函数调用失败。这对于需要可靠的网络连接的应用程序可能会造成问题。
3. 参数复杂:getaddrinfo函数的参数相对较多,使用起来可能比传统的gethostbyname函数更复杂。对于不熟悉该函数的应用程序开发者,可能需要花费更多的时间和精力来理解和使用它。
4. IPv4和IPv6的兼容性:虽然getaddrinfo函数可以同时支持IPv4和IPv6,但在处理不同协议的地址时需要进行相应的转换。这可能增加了代码的复杂性和开发难度。
5. 错误处理:虽然getaddrinfo函数在出现错误时会返回一个特定的错误码,但对于某些错误情况可能没有提供足够的上下文信息。这可能使得错误处理变得困难,特别是当错误发生在异步操作中时。
综上所述,虽然getaddrinfo函数具有很多优点,但也存在一些缺点。在使用该函数时,需要权衡其优点和缺点,并根据应用程序的需求进行选择。在一些特定的情况下,可能需要使用其他的网络编程接口或方法来满足需求。
应用场景:
getaddrinfo函数广泛应用于各种需要进行网络连接的场景中,尤其是在处理主机名和服务名到套接字地址的转换时。以下是几个常见的应用场景:
1. 简单的HTTP客户端:可以使用getaddrinfo函数来实现一个简单的HTTP客户端,用于向Web服务器发送HTTP请求。通过将主机名和服务名转换为套接字地址结构,可以方便地建立与远程服务器的连接。
2. DNS查询:在DNS查询过程中,getaddrinfo函数可以用于将域名解析为IP地址。当应用程序需要将主机名转换为IP地址时,可以使用该函数来获取对应的IP地址列表。
3. 网络连接建立:在网络应用程序中,当需要建立与服务器的连接时,可以使用getaddrinfo函数来获取服务器端的IP地址和端口号。然后,使用这些信息来创建套接字并建立连接。
4. 负载均衡和故障转移:通过getaddrinfo函数,可以获取主机名对应的多个IP地址,从而实现负载均衡和故障转移。当某个IP地址出现故障时,可以自动切换到其他可用的IP地址上。
5. 多协议支持:getaddrinfo函数能够处理协议无关的转换,既可用于IPv4也可用于IPv6。在多协议环境下,应用程序可以使用该函数来处理不同协议的网络地址转换。
综上所述,getaddrinfo函数在各种需要进行网络连接的场景中都有广泛的应用,无论是简单的HTTP客户端还是复杂的网络应用程序,都可以通过该函数来方便地处理网络地址和端口的转换。
相关推荐
- MongoDB 从入门到实战:.NET 平台完整指南
-
一、什么是MongoDBMongoDB是一种功能强大且灵活的NoSQL数据库,适用于处理大规模的半结构化数据和高并发场景。它不依赖于固定的表结构和关系模型,而是以文档的形式存储数据,每个文档可...
- NET Framework安装失败的原因及解决方法
-
大家好我是艾西,一个做服务器租用的游戏爱好者兼网络架构系统环境问题网络工具人。在我们平时使用PC安装某些程序会出现.NETFramework缺失的提示,那么也会有很多的小伙伴搞不懂什么原因导致的,这...
- 这可是全网eNSP安装最完整,最详细的图解,没有之一(常见问题)
-
eNSP安装大纲eNSP安装详细图解篇幅较长,会分三篇更完。急需安装的朋友可以在文末获取图解文档和所需软件工具。ENSP安装常见问题和解决方案Vbox安装错误eNSP在安装的过程当中,经常会出现一...
- 如何在windows 2012安装.NET Framework3.5
-
Windowsserver2012R2,自带的是.NETFramework4.5,如果想装SQLserver2008或者SQLserver2012需要安装.ENTFramework...
- 3款国内可用的「Chrome」扩展下载网站
-
身为程序员,有几个不使用Chrome浏览器提升下编码效率呢?Chrome拥有众多丰富强大的扩展程序,今天给大家分享三个国内可用的Chrome扩展下载网站,收藏一下吧,不然下次就找不到我咯!C...
- 下载 Windows 10 应用商店程序离线包方法
-
有厂商为了图方便,会把Windows10应用商店里面的UMP应用改成EXE程序版本。例如之前「网易云音乐」UMP版本简洁清爽,获得不少用户推荐,后来官方懒得更新了,直接把UMP版本...
- 极速安装!NET Framework 3.5零距离指南!
-
.NETFramework3.5是一款由微软开发的应用程序框架,它为许多Windows应用程序提供了基础支持。它的新版本带来了许多令人兴奋的功能和改进,比如增强的XML和JSON处理能力以及强大的...
- Microsoft.NET离线运行库合集发布 2021
-
软件介绍.NET是微软具有战略意义的框架,也是装机必不可少的框架,想要一个一个安装略显繁琐,再加上很多电脑小白不知道怎么下载,不小心就下载到某某高速加载器,这个运行库极大解决了这个问题,采用微软官方....
- 缺少.net framework 3.5怎么办?(缺少.net4.5.1或以上环境)
-
很多电脑用户在玩某些程序游戏时都会遇到一个头痛的问题,弹出缺少“NETFramework3.5”的提示。微软从Windows8开始默认屏蔽了“.NET3.5”,如果用户有需要就必须选择在线安装...
- Windows11无法正常安装.net 3.5组件的解决方法
-
最近因公司部分电脑升级至Windows11之后,重新安装某些需要加载.net3.5组件的应用软件时,都提示无法完成加载或安装.net3.5而导致无法完成安装。使用离线安装包亦一样无法完成安装。一...
- 离线安装.Net Framework 3.5(离线安装.net framework 4.0)
-
前言.Net3.5已经越来越少用到了,但是偶尔还是会遇到一些老软件需要。而Win10、Win11的系统,直接在控制面板的里添加,经常会添加失败!解决方法首先需要一个系统的ISO镜像来提取sxs文件夹:...
- Jenkins 11个使用技巧,90%以上的人没用过
-
一、Performance插件兼容性问题自由风格项目中,有使用Performance插件收集构建产物,但是截至到目前最新版本(Jenkinsv2.298,Performance:v3.19),此...
- 6款Linux常用远程连接工具,你最中意哪一款?
-
点击上方头像关注我,每周上午09:00准时推送,每月不定期赠送技术书籍。本文2106字,阅读约需6分钟Hi,大家好。远程连接的实现方法有很多,概括地说有两种,一种是用系统自带的远程连接,另外一种是用...
- Linux常用远程连接工具介绍,总有一款适合你
-
作为运维或者网工最常用就是ssh远程和远程桌面工具,本文就介绍几个常用的远程连接工具,你在用哪一款呢SecureCRT介绍:我觉得这个是最好的SSH工具,没有之一。SecureCRT支持SSH,同时支...
- 终极软路由网络设置,ESXi虚拟机安装iKuai+openWrt双路由系统
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:BigBubbleGum本文是软路由系列的第五篇,也是折腾时间最长的一篇,在ESXi下分别独立安装和使用iKuai和openWrt...
你 发表评论:
欢迎- 一周热门
-
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
极空间如何无损移机,新Z4 Pro又有哪些升级?极空间Z4 Pro深度体验
-
[常用工具] OpenCV_contrib库在windows下编译使用指南
-
Ubuntu系统Daphne + Nginx + supervisor部署Django项目
-
WindowsServer2022|配置NTP服务器的命令
-
WIN11 安装配置 linux 子系统 Ubuntu 图形界面 桌面系统
-
UOS服务器操作系统防火墙设置(uos20关闭防火墙)
-
10个免费文件中转服务站,分享文件简单方便,你知道几个?
-
解决Linux终端中“-bash: nano: command not found”问题
-
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)