Socket-Programing-FAQ

简介: 引言 Socket编程FAQ,由Vic Metcalfs创建,这是一系列关于socket编程相关的常问的问题。这些问题整理自comp.unix.programmer新闻组,我省略了其中一些比较基本的socket编程问题,有的问题的解答已经过时,因此我更新了部分回答。 How can I tell when a socket is closed on the end? 如果对端调用close或

引言

Socket编程FAQ,由Vic Metcalfs创建,这是一系列关于socket编程相关的常问的问题。这些问题整理自comp.unix.programmer新闻组,我省略了其中一些比较基本的socket编程问题,有的问题的解答已经过时,因此我更新了部分回答。

How can I tell when a socket is closed on the end?

如果对端调用close或者exit退出,并且没有设置SO_LINGER选项,那么本端调用read会返回0,无论调用多少次都返回0第一次调用write后立即返回0,其实在内核层面会收到对端响应的RST报文,此时如果再次调用write会导致EPIPE错误,这个错误会引起SIGPIPE信号的产生,而这个信号的默认处理方式就是关闭程序。

注:更详细的分析详见网络编程最佳实践

What’s with the second parameter in bind()?

是一个struct sockaddr类型的参数,这是一个通用的套接字地址结构,根据你的套接字类型,应该传入不同的套接字地址结构,通常会有
sockaddr_in,sockaddr_un等,为了让bind可以接收不同类型的套接字地址结构就使用了通用类型sockaddr,通过强制转换,将其转换为
对应的套接字结构来使用。

How do I get the port number for a given service?

使用getservbyname,这个函数会返回一个指向struct servent结构体的指针,其中有一个字段s_port就是对应服务的端口号了(网络字节序)。
下面是一个小的例子:

//考虑到service可能是一个端口号字符串,会尝试将其转换为网络字节序
int atoport(const char* service,char *proto)
{
    int port;
    long int lport;
    struct servent *serv;
    char *errpos;
    serv = getservbyname(servivce,proto);
    if(serv != NULL)
        port = serv->s_port;
    else {
        lport = strtol(service,&errpos,0)
        if((errpos[0] != 0) || (lport < 1) || (lport > 5000))
            return -1; //invalid port address
        port = htons(lport);
    }
    return port;
}

 

if bind() fails,what should I do with the socket descriptor?

如果你选择exit,那么系统会负责将socket描述符关闭,如果你不exit,那么可以手动去调用close来关闭套接字描述符

When should I use shutdown()?

shutdown一个典型的应用场景就是向对端发送结束请求(内核负责发送FIN报文),表明请求结束了。close也具备这样的功效,那么close和shutdown的区别和联系是什么呢,close本质上只是减少套接字描述符的引用计数,当引用计数变为0,才会导致TCP/IP的四次挥手过程。因此在多进程场景下,close并不会导致请求结束(也就是导致内核发生四次挥手过程),只是会较少套接字描述符的引用计数。而shutdown则不一样,shutdown会导致内核发起四次挥手过程中的前半部分,也就是半关闭。因为socket是一个类似于pipe的机制,不过socket是双向的,pipe是单向的。通过shutdown可以进行半关闭。也就是关闭了写端,但是仍然可以从socket中读取对端发送过来的数据。

Please explain the TIME_WAIT state

TCP/IP协议就是这样设计的,是不可避免的。主要有两个原因:

  • 可靠地实现TCP全双工连接的终止

TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。

  • 允许老的重复分节在网络中消逝

TCP分节可能由于路由器异常而迷途,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的新连接前一个连接的迷途重复分组在“前一个连接”终止后到达,而被新连接收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。

Why does it take so long to detect that the peer died?

因为默认情况下,除了发送数据和ACK外,是没有任何其他数据发送的,因此如果你只是简单的等待对端数据的到来的化,那么如果对端静默的消失了就无法通知你了。通过设置SO_KEEPALIVE可以进行定期的进行检查,检查连接是否有效。但是需要注意的是这个定期检查的时间间隔至少是2个小时,幸好这个值是可以通过系统参数进行修改的。如果你发送数据给对端,对端响应了ACK那么就表示连接是有效的,但是如果连接是无效的,就会导致数据重传,重传一段时间后你才能得知连接是无效的。大多数互联网程序在服务器端采用超时读的方法,如果在指定时间间隔内没有收到请求那么服务器端就放弃这个客户端。那么如何去维护一个空闲的长连接呢?,通常有两个办法:

  • 使用SO_KEEPALIVE
  • 使用应用层的心跳(比如发送定期发送空请求)

What are the pros/cons of select(), non-blocking I/O and SIGIO?

使用非阻塞的IO意味着你需要不同的轮询socket去查询它是否有数据,轮询会消耗大量的CPU时间周期。因此应该尽量避免去使用轮询。SIGIO允许socket有数据可读的时候会由操作系统触发一个SIGIO的信号,然后在信号处理函数中可以接收数据处理数据,这个方法的缺点就是容易导致数据混乱(不知道是哪个socket的数据),当有多个socket需要读取数据的时候,你应该选择select机制。select可以同时监听多个套接字描述符的数据是否就绪,除此之外select还支持超时机制。

Why do I get EPROTO from accept()?

EPROTO意味着协议发生了不可恢复的错误,通常这个错误发生在accept的时候,在accept未返回之前,这个连接被重置了。

How can I force a socket to send the data in its buffer?

不能强制发送缓冲区中的socket数据,正常情况下调用write写入数据的确会导致TCP发送一个segment,但是不保证一定会这样。有很多原因会导致TCP不会发送segment, 比如关闭滑动窗口,比如Nagle algorithm算法等。Nagle算法会导致发送的多个少量数据被整合成一个包被发送出去。Nagle算法的出现是为了解决包头负荷的问题,当发送大量小数据的时候,每个小数据都会被附加固定大小的包头那么当要发送的数据小于包头大小的时候,很显然这是一种浪费,因此通过Nagle算法将大量小数据封装在一个包中进行发送。 然后nagle算法对于一些实时性要求高的场合还是不尽如人意的,会导致延迟的问题。可以通过发送带外数据来解决,但是带外数据的内容有限制,一次只能发送1个字节。综上所述要想刷新socket的缓冲,你必须要关闭Nagle算法。 还有另外一种情况就是当关闭滑动窗口,如果对端不进行数据读取那么发送端的数据就会一直在buffer中直到整个buffer填满,发送端阻塞。

Whats the difference between select() and poll()?

selectfd_set是一个bit mask,有固定大小,从用户空间到内核空间的拷贝开销少,而poll需要使用者分配pollfd数组。但是fd_set有大小限制而pollfd数组没有大小限制,select的移植性更好。

How do I send [this] over a socket?

对于整型值你需要使用htons做字节序的转换,而对于字符串来说就是一串单字节流。因此不会有任何问题,如果你要发送的是一个结构体,那么你需要在对端用同样的结构体来接收。在使用的结构体的时候要考虑结构体在不同的OS平台上对齐方式不同,应该通过避免字节对齐来屏蔽这些差异。如果你要发送浮点型的数据,那么可能你需要很多工作要做。

How do I use TCP_NODELAY?

首先你要确定你想使用这个选项,它将会关闭Nagle算法,导致网络拥堵,浪费带宽。如果通过关闭这个选项没有导致你的速度所有增长,那么请关闭这个选项,通过setsockopt可以设置TCP_NODELAY

What is the difference between read() and recv()?

read等同与recv的flags参数等于0的情况,write等同于send的flags参数等于0的情况。并且在一个non-unix系统上是不允许在socket上使用write和read,但是send和recv总是可以的。

I see that send()/write() can generate SIGPIPE,Is there any advantage to handling the signa,rather than just ignoring it and checking for the EPIPE error?Are there any useful parameters passed to the signal catching function?

通常来说只能传递一个信号的数值给信号处理函数,通过sigaction还可以传递一些额外的参数。我的建议是忽略SIGPIPE信号。通过errno值来处理这个错误要比通过信号处理器来处理更好。但是有一种情况需要设置SIGPIPESIG_DFL,就是当程序将调用exec簇函数的时候,内核会负责将所有已经设置信号处理函数的信号设置成SIG_DFL,为了保证其移植性在调用exec簇函数之前,手动设置SIGPIPESIG_DFL

After the chroot(),calls to socket() are failing. Why?

在某些类unix的系统上socket函数实际上会可能会打开/dev下某些特殊文件,因此你需要在chroot环境下创建dev目录和相关的特殊文件。同理一些daemon的进程会在chroot后调用syslog汗水和,这个syslog函数可能会使用UDP socket或者FIFO或者unix socket等,为了避免在chroot环境下调用失败,最好在chroot之前调用openlog

Why do I keep getting EINTR from the socket call?

这不是一个真正要退出的错误条件,这个错误意味着调用被信号打断了,任何可能会阻塞的调用都应该使用loop包裹起来,然后检查EINTR。

When will my application receive SIGPIPE?

当TCP连接关闭,并且接收到了对端的RST报文的时候。此时如果调用write会导致SIGPIPE错误,但是read是没有问题的,总是返回0.一般出现SIGPIPE的场景是客户端关闭了连接,但是对端不知情依然向套接字写数据,第一次写入的时候会导致接收到对端发送过来的RST报文,第二次就会产生EPIPE错误了。

What are socket exceptions? What is out-of-band data?

不想C++里面的异常,socket的异常不是表明发生了错误,socket的异常通常表示带外数据到达的通知。带外数据又称为紧急数据,看起来像是从主的数据流中分离出来的一种数据流。很好的将数据分为了两种不同类型。带外数据又称紧急数据,但是并不是说这种数据传送很快,而是说这种数据的优先级要高,还有一点需要注意的是如果你的程序没有即使读取带外数据,可能会导致数据丢失。而正常的数据流则不会出现丢失的情况。

running on? How can I find the full hostname(FQDN) of the system I’m

一些系统的hostname是FQDN的格式,然而另外一些系统则是unqualifield hotstname,BIND推荐使用FQDN格式的主机名,但是大多数
Solaris系统则是趋向于使用unqualifield hotstname。大多数支持posix语义的系统都提供了uname来获取主机名,但是一些老的BSD系统
仅仅提供gethostname.通过调用gethostbyname找到你的ip地址,然后传递给gethostbyaddr,得到hostent结构体,然后这个结构体中的
h_name成员就是你的FQDN了。

How do I convert a string into an internet address?

用户输入的地址可能是一个字符串的ip地址,也有可能是一个域名,因此需要考虑这两种情况,下面是例子:

struct in_addr *atoaddr(const char *address)
{
    struct hostent *host = NULL;
    static struct in_addr saddr;
    if(inet_pton(AF_INET, address, &saddr) != -1)
        return &addr;
    host = gethostbyname(address);
    if(host != NULL) {
        return (struct in_addr*)*host->h_addr_list;
    }

    return NULL;
}

Why does connect() succeed even before my server did an accept()?

只要你在socket上调用listen完成后,内核就已经做好接收连接的准备了,通过UNIX的实现会立即和发起syn请求的连接完成SYN握手,然后为其创建socket,并将其放到等待队列中,等待accept调用。因此在accept前socket已经就绪了。内核会维护两个队列,一个是半连接队列一个是连接队列,当一个连接发起syn请求后会被放入到半连接队列,等待完成三次握手后会被移入到连接队列中。listen的第二个参数就是用于设置这个连接队列的大小。当连接的数量超过这个连接队列的大小的时候连接会被忽略,导致对端连接超时,进行超时重连。connect调用会导致发起syn请求,当对端响应最后一个ACK的时候,connect才会返回表示三次握手完成。

Why do I sometimes lose a server’s address when using more than one server?

因为gethostbyname的内部实现问题,其内部实现是使用指针指向一个内部的staticstruct hostent,因此在多次调用gethostbyname的时候会导致,当前这次的结果覆盖上一次的结果。为了避免这个问题在通过gethostbyname得到结果后应该拷贝一份出来。

How can I set the timeout for the connect() system call?

最简单的方式就是connect+alarm通过alarm定时,时间到了会触发信号,通过信号打断connect调用,从而实现所谓的timeout功能,最好的方式应该是使用select+非阻塞socket的方式,在使用非阻塞connect的时候,先直接发起connect调用,如果连接成功就不需要使用select,如果没有连接成功再去使用select来接管connect。非阻塞socket连接的时候可能会有下面三种可能:

  • 连接成功,通常这种情况出现在服务器端和客户端在同一台机器上。
  • 连接失败
  • 连接返回-1,错误码是EINPROGRESS,这表明连接正在进行中,但是还没有完成

如果连接成功了,那么select认为是可读(如果有数据到达就是可写),如果连接失败了那么select认为是即可读也可写,并且设置对应的错误码。一个带超时功能的connect代码实现如下:

int connect_timeout(int fd,struct sockaddr_in *addr,int timeout)
{
    int ret = 0;
    setnonblocking(fd); //setup for nonblocking
    socklen_t len = sizeof(struct sockaddr_in);
    struct pollfd fds[1];
    fds[0].fd = fd;
    fds[0].events = POLLOUT;
    fds[0].revents = 0;
    ret = connect(fd,(struct sockaddr*)addr,len);
    if (ret < 0 && errno == EINPROGRESS)
    {
        do{
            ret = poll(fds,1,timeout);
        }while(ret < 0 && errno == EINTR);
        if (ret == 0) {
            errno = ETIMEDOUT;
            return -1;
        } else if (ret < 0)
            return -1;
        else if (ret == 1) {
         //两种可能,一种就是产生了错误,另外一种才是连接建立成功    
            int err;
            socklen_t socklen = sizeof(err);
            int sockoptret = getsockopt(fd,SOL_SOCKET,SO_ERROR,&err,&socklen);
            if (sockoptret == -1){
                return -1;
            }
            if (err == 0)
                return 0;
            else {
                errno = err;
                return -1;
            }
        }
    }
    return ret;
}

system choose one for me on the connect() call? Should I bind() a port number in my client program, or let the

通常客户端连接服务器端的时候会选择一个随机的端口进行连接,但是某些程序要求客户端连接的时候必须是从某个指定端口开发起的连接。但是这样的客户端会存在一个问题,当客户端主动断开连接的时候会导致TIME_WAIT状态,伺候2MSL期间,这个客户端都无法再次发起连接,如果是使用随机断开则不会出现这个问题。

Why do I get “connection refused” when the server isn’t running?

当内核的连接队列中没有任何已经就绪的连接的时候,connect就会被阻塞,如果对端在指定端口上没有server启动和监听,那么connect将会被拒绝,并返回错误信息,connection refused。

over the socket? Is there a way to have a dynamic buffer?What does one do when one does not know how much information os comming

要要读取的数据大小未知的时候,你可以让buffer尽可能的大,你也可以在读取数据的过程中动态的扩充buffer的大小,malloc分配一个大的地址空间但是这不是真正的物理内存,只有等这段地址空间被写入数据了才会真正映射到物理内存上。所以你可以不用担心过度的申请内存,导致的资源浪费。

Why don’t my sockets close?

close()只是关闭了你的socket接口,并不是socket本身,当close()调用后,会导致socket的描述符减少,一旦为0就会触发内核开始发起四次挥手的过程,因为某些技术原因会导致socket在调用close()后仍然处于活动状态数分钟,这是很正常的情况,例如上文中提到的socket处于TIME_WAIT状态,在这个状态下就会停留2MSL时间。

How can I make my server a daemon?

有两种方法,第一种方法就是使用inetdxinetd,第二种方法所有的工作都自己来实现。inetd是一个超级服务器可以负责帮我们监听,处理网络数据,因此如果使用了inetd那么我们的程序就不需要处理与网络相关的代码了,inetd会负责将接收到的数据通过标准输入传递到后端的程序进行处理。inetd也会接收来自于后端程序的标准错误和标准输出的内容。如果你选择自己来实现,那么下面是实现的主要代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>

volatile sig_atomic_t keep_going = 1;
void termination_handler(int signum);

int main()
{
    .....
    if(chdir(HOME_DIR))
    {
        fprintf(stderr,"%s':",HOME_DIR);
        perror(NULL);
        exit(1);
    }

    switch(fork())
    {
        case -1:
            perror("fork()");
            exit(3);
        case 0:
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
            if(setsid() == -1) {
                exit(4);
            }
            break;
        default:
            return 0;
    }
    if(signal(SIGTERM,termination_handler) == SIG_IGN)
        signal(SIGTERM,SIG_IGN);
    signal(SIGINT,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    while(keep_going)
    {
        .....
    }
    return 0;
}

void termination_handler(int signum)
{
    keep_going = 0;
    signal(signum,termination_handler);
}

 

How can I listen on more than one port at a time?

使用select,poll,epoll这样的IO服用机制即可,内核会负责帮我们监听这些描述符的事件变化,然后给我们通知。应用层依次处理这些描述符的事件变化即可。

What exactly does SO_REUSEADDR do?

这个选项告诉内核即使这个端口busy(TIME_WAIT状态)也要继续重用它。如果这个端口busy但是不是TIME_WAIT状态则会返回一个地址正在使用的错误。

What exactly does SO_LINGER do?

SO_LINGER选项的含义:
SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据,处理方式无非有两种: 丢弃或者将数据继续发送到对端,优雅的关闭链接

  • l_onoff零,l_linger忽略 close会立即返回,发送队列会一直保持,直到发送完成,系统保证将数据发送到对端 (默认行为)
  • l_onoff非零,l_linger零,close立即返回,发送队列立即放弃,直接发送RST包,自身立即复位,不用经过2MSL状态,对端收到复位错误号
  • l_onoff非零,l_linger非零 close会阻塞直到l_linger超时,或数据发送完成(套接字必须是阻塞的),在超时时间内保持尝试发送,若超时则立即放弃发送队列。

How do I get my server to find out the client’s address/hostname?

使用getpeername可以得到客户端的地址,代码如下:

int t;
int len;
struct sockaddr_in sin;
struct hostent *host;

len = sizeof sin;
if(getpeername(t,(struct sockaddr*)&sin,&len) < 0)
    perror("getpeername")
else {
    if((host = gethostbyaddr((char*)&sin.sin_addr,
                             sizeof sin.sin_addr,
                             AF_INET)) == NULL)
           perror("gethostbyaddr");
    else
        printf("remote host is '%s'\n",host->h_name);
}

 

What is the difference between SO_REUSEADDR and SO_REUSEPORT?

SO_REUSEADDR大家可能很熟悉,其用途就是可以重用处于TIME_WAIT状态的socket。因为linux系统本身不允许同时绑定多个socket到相同的地址和端口那么对于一个处于TIME_WAIT状态的socket来说,就无法再创建一个和这个socket具有相同地址和端口的socket了,如果启SO_REUSEADDR可以避免这问题。SO_REUSEPORTSO_REUSEADDR有些不同,SO_REUSEPORT允许多个AF_INETAF_INET6类型的socket绑定到同一个地址和端口,但是必须对每一个socket都要设置SO_REUSEPORT选项,为了防止hijacking,所有绑定相同地址的进程都必须是相同的EUID。这个选项可以用于TCP的socket,也可以用于UDP的socket。对于TCP的socket来说,这个选项可以允许accept实现多线程服务器的负载均衡,传统的负载均衡方式是主线程accept然后分发进行处理。对于UDP来说,使用这个选项可以为多进程(线程)提供更好的分发效果像比如传统的多进程从相同的socket接收数据包来说。

How can I write a multi-homed server?

使用INADDR_ANY来给socket绑定地址,也可以使用ioctl的SIOCGIFCONF获取有效的网络结构,然后有选择的进行bind。

When should I use UDP insead of TCP?

当你不需要确保包的顺序到达,不需要确保数据包必须达到对端的时候,那么UDP是一个很好的选择,如果你发现TCP太慢,你需要一个更快的方案你可以考虑使用UDP。

What is the difference between “connected” and “unconnected” sockets?

如果UDP socket是未连接的那么就无法使用send或write等系统调用发送数据,只能使用sendto来发送数据,如果使用了connect那么socket就被绑定了目标地址,此时就可以使用send和write等系统调用来发送数据。

of the socket? Does doing a connect() call affect the receive behaviour

当你给UDP使用了connect,那么使用read就只能读取,连接对端发送过来的数据,其他的发送者发过来的数据无法接受,但是最重要的是连接的UDP可以接收到ICMP的错误。

How can I read ICMP errors from “connected” UDP sockets?

如果对端没有服务等待接收数据,那么对端会丢弃发送过来的数据包,并响应ICMP错误,但是这个ICMP错误是异步的,不是返回到对应的套接字上也就是说对于未连接的UDP socket来说,发生了错误是无法感知的,只有使用 connected的socket才会让异步产生的ICMP错误返回到对应的套接字上,第一次调用send的时候发生了如果发生了ICMP错误,那么下次再调用send就会返回错误了。

Reference

  • Socket-Programing-FAQ
  • UNIX环境高级编程
  • TCP/IP详解(卷1)
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
10月前
|
人工智能 监控 自动驾驶
Apollo:Meta 联合斯坦福大学推出专注于视频理解的多模态模型,能够理解长达数小时的视频
Apollo是由Meta和斯坦福大学合作推出的大型多模态模型,专注于视频理解。该模型通过“Scaling Consistency”现象,在较小模型上的设计决策能够有效扩展至大型模型,显著提升了视频理解能力。
216 24
Apollo:Meta 联合斯坦福大学推出专注于视频理解的多模态模型,能够理解长达数小时的视频
|
9月前
|
敏捷开发 数据可视化 调度
燕云十六声开发团队看过来,高效协作软件选哪个?
在2025年新春来临之际,燕云十六声团队面临巨大工作量和挑战,选择合适的可视化协作软件至关重要。本文推荐6款提升团队效率的办公软件:板栗看板、Trello、Asana、Monday.com、Jira和Wrike。这些工具各具优势,如板栗看板的操作简洁、Trello的灵活看板、Asana的多层次任务管理、Monday.com的高度定制化、Jira的专业项目管理和Wrike的强大报告功能,助力游戏团队高效协作与运营。
156 2
|
10月前
|
运维 Ubuntu 应用服务中间件
自动化运维之路:使用Ansible进行服务器管理
在现代IT基础设施中,自动化运维已成为提高效率和可靠性的关键。本文将引导您通过使用Ansible这一强大的自动化工具来简化日常的服务器管理任务。我们将一起探索如何配置Ansible、编写Playbook以及执行自动化任务,旨在为读者提供一条清晰的路径,从而步入自动化运维的世界。
178 11
|
11月前
|
开发框架 移动开发 Dart
Flutter 框架的缺点
以上缺点并不意味着 Flutter 框架不优秀,只是在使用过程中需要开发者根据具体的项目需求和场景,充分考虑这些因素,并采取相应的措施来克服或缓解这些问题,以充分发挥 Flutter 的优势,开发出高质量的移动应用。
|
人工智能 分布式计算 DataWorks
连续四年!阿里云领跑中国公有云大数据平台
近日,国际数据公司(IDC)发布《中国大数据平台市场份额,2023:数智融合时代的真正到来》报告——2023年中国大数据平台公有云服务市场规模达72.2亿元人民币,其中阿里巴巴市场份额保持领先,占比达40.2%,连续四年排名第一。
569 12
|
11月前
|
机器学习/深度学习 自动驾驶 算法
深度学习在自动驾驶汽车中的应用
深度学习在自动驾驶汽车中的应用
|
人工智能 算法 安全
AI伦理:探索智能时代的道德边界
【9月更文挑战第10天】随着AI技术的发展,我们步入了智能时代,AI的应用为社会带来便利的同时,也引发了伦理道德的讨论。本文探讨了数据隐私、算法偏见及系统透明度等伦理问题,并提出制定法规、行业自律、伦理审查及跨学科合作等策略,旨在确保AI技术的健康发展,构建智能、公平、安全的未来。通过共同努力,我们能在技术进步与道德边界间找到平衡点,推动社会持续进步。
|
搜索推荐 数据可视化 Linux
【超乎想象】Archman Linux:一款基于Arch Linux的极致轻量、极速稳定、超凡体验的Linux发行版!
【8月更文挑战第22天】Archman Linux是一款基于Arch Linux的轻量级、快速且稳定的发行版,继承了Arch的技术优势并提供友好的桌面环境。安装步骤包括下载ISO镜像、创建启动盘、从USB启动进入Live环境、运行安装程序、分区、配置网络及安装基本系统、设置密码、安装引导程序并重启。配置涉及系统更新、安装桌面环境与常用软件、个性化设置、安装驱动、设置自动更新和备份计划。通过提供的Shell脚本可自动化完成部分配置工作。无论新手还是老手,Archman Linux都能提供出色的体验。
505 2
|
机器学习/深度学习 Python
使用Python实现超参数调优
使用Python实现超参数调优
218 0
|
存储 缓存 弹性计算
阿里云服务器通用型g5、g6、g7实例区别及选择参考
在我们选择阿里云服务器实例规格的时候,如果是选择通用型实例,会发现同样是通用型实例,有通用型g5、通用型g6和通用型g7可选(当然还有g8i、g8y等其他通用型实例可选),他们都属于企业级云服务器,都配有2核4G、4核8G和8核16G等处理器与内存比1:4的配置,那么它们之间有什么区别,下边就这三个实例各自的特点、网络、适用场景及最新活动价格来详细分析一下新手用户应该怎么选择。
阿里云服务器通用型g5、g6、g7实例区别及选择参考