百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

systemd的socket激活机制 socket system

yuyutoo 2024-10-11 21:39 3 浏览 0 评论

1. 什么是socket激活机制

What was socket activation again? -- Basically, socket activation simply means that systemd sets up listening sockets (IP or otherwise) on behalf of your services (without these running yet), and then starts (activates) the services as soon as the first connection comes in. Depending on the technology the services might idle for a while after having processed the connection and possible follow-up connections before they exit on their own, so that systemd will again listen on the sockets and activate the services again the next time they are connected to. For the client it is not visible whether the service it is interested in is currently running or not. The service's IP socket stays continously connectable, no connection attempt ever fails, and all connects will be processed promptly.

  • systemd的socket激活机制是一种在有连接请求时才启动服务的技术。这种机制通过让systemd守护进程在应用程序启动前打开监听套接字来工作。当有客户端连接到达时,systemd会启动相应的服务并将套接字传递给它,从而减少资源使用并提高性能。
  • 当服务将客户端的请求处理完毕后,服务可以继续运行以处理更多请求,或者在处理完当前请求后退出(依赖于服务的实现)。
  • 当服务退出时,包括正常的退出或者服务的异常崩溃,systemd会检测到服务的退出状态,systemd会继续管理套接字并确保服务能够重新启动并处理新的连接请求。

这种机制的主要优点包括:

  1. 按需启动服务:服务只有在需要时才启动,减少了系统资源的浪费。
  2. 无缝重启:由于套接字由systemd管理,即使服务进程重启,套接字也不会关闭,从而避免了短暂的停机时间。
  3. 简化配置:通过统一的套接字管理,可以简化服务的配置和管理。

下图所示为systemd的socket激活机制的简单示例,

  • systemd首先替服务myservice创建并监听了socket,端口为8080,此时myservice服务还未启动;
  • 当客户端client的connect请求到来时,systemd才去启动服务myservice,同时将创建的socket描述符传递给myservice服务;
  • myservice服务接收到systemd的socket描述符后,直接执行accept()去接收client的连接请求,连接建立成功,然后和myservice服务端正常进行数据收发。


2. 服务如何获取systemd打开的socket描述符

sd_listen_fds() 是 systemd 库中的一个重要函数,主要用于套接字激活(socket activation)场景。如下所示,这个函数返回由 systemd 传递给服务的预打开文件描述符的数量。这些文件描述符通常是套接字,但也可能是其他类型的文件描述符

int sd_listen_fds(int unset_environment);
  • 参数unset_environment,如果为非零值,函数会清除相关的环境变量(LISTEN_PID 和 LISTEN_FDS)。这通常在多进程服务中很有用,可以防止子进程误认为这些描述符是为它们准备的。
  • 函数返回传递的文件描述符数量,如果没有文件描述符被传递或发生错误,返回 0 或负数。

接下来以一个例子,介绍该函数的用法,对于linux而言,一般进程在启动后, 0、1、2三个文件描述符已经被占用,分别是标准输入、输出和错误。所以,不管是文件描述符还是socket描述符,一般值都是从3开始,而这个3正是SD_LISTEN_FDS_START 宏。sd_listen_fds函数一般使用时需要搭配SD_LISTEN_FDS_START 宏和sd_* 相关函数(如 sd_is_socket、sd_is_socket_inet 等)来使用,sd_* 函数主要用来检查描述符的类型

#define SD_LISTEN_FDS_START 3
  • 假设服务要创建一个tcp的server socket,端口为8080,systemd已经帮服务先创建;
  • 调用sd_listen_fds,获取systemd 传递的套接字文件描述符的数量,数量为n,n是大于0的;
  • 接着轮询,从描述符3开始,直到3+n结束,调用sd_is_socket_inet函数轮询查找监听8080端口的tcp socket,如果找到,函数sd_is_socket_inet返回值大于0;
  • 则服务用找到的描述符handle进行accept等后续的操作。
#include <systemd/sd-daemon.h>
#include <stdio.h>
#include <unistd.h>

int main() {
 
 int last_handle = SD_LISTEN_FDS_START + sd_listen_fds (0);
 int handle;
 for (handle = SD_LISTEN_FDS_START; handle < last_handle; ++handle)
  if (sd_is_socket_inet (handle, AF_INET, SOCK_STREAM, 1,
    (uint16_t) 8080) > 0) {
   printf("this is a IPv4 TCP socket listening on port 8080\n");
   break;
  }
 //accept
    return 0;
}

3. sd_is_* 类函数

常用的sd_is_* 类函数如下所示,

int sd_is_socket(int fd, int family, int type, int listening);
int sd_is_socket_inet(int fd, int family, int type,
                             int listening, uint16_t port);
int sd_is_socket_unix(int fd, int type, int listening,
                             const char *path, size_t length);

sd_is_socket() may be called to check whether the specified file descriptor refers to a socket. If the family parameter is not AF_UNSPEC, it is checked whether the socket is of the specified family (AF_UNIX, AF_INET, ...). If the type parameter is not 0,it is checked whether the socket is of the specified type(SOCK_STREAM, SOCK_DGRAM, ...). If the listening parameter is positive, it is checked whether the socket is in accepting mode, i.e. listen() has been called for it. If listening is 0, it is checked whether the socket is not in this mode. If the parameter is negative, no such check is made. The listening parameter should only be used for stream sockets and should be set to a negative value otherwise.

  • sd_is_socket()函数,来检查指定的描述符fd指向的是对应参数的一个socket套接字。如果family不是AF_UNSPEC,则需要检测是否是指定的AF_UNIX, AF_INET等,type为socket的类型SOCK_STREAM,SOCK_DGRAM,如果listening为正值,则检测socket是否正在处于accept状态(已调用listen)。

sd_is_socket_inet() is similar to sd_is_socket(), but optionally checks the IPv4 or IPv6 port number the socket is bound to, unless port is zero. For this call family must be passed as either AF_UNSPEC, AF_INET, or AF_INET6.

  • sd_is_socket_inet()函数和sd_is_socket()类似,如果入参的port非0,则会去检测对应的socket fd是否在指定的port上进行了bind。

sd_is_socket_unix() is similar to sd_is_socket() but optionally checks the AF_UNIX path the socket is bound to, unless the path parameter is NULL. For normal file system AF_UNIX sockets, set the length parameter to 0. For Linux abstract namespace sockets,set the length to the size of the address, including the initial 0 byte, and set the path to the initial 0 byte of the socket address.

  • sd_is_socket_unix()函数,如果path非空,则会去检测对应的socket fd是否为AF_UNIX 域套接字,同时是否在指定的path上进行绑定。一般length参数设置为0。

4. 在ubuntu上创建一个带socket激活机制的服务

服务端代码,myservice.c,

  • log采用systemd中的journal相关的log接口sd_journal_print()函数;
  • 如果systemd没有传递过来socket描述符,自己创建一个(原生启动,不依赖systemd,仅为了测试);
  • 如果systemd传递了socket描述符,判断是否是tcp 8080端口的监听socket对应的描述符,如果是,直接调用accept()函数去处理客户端的连接请求;
  • 该代码的功能只是简单的示例,每次处理一个客户端的请求,先读取client的数据,读取到数据后,然后发送"Hello, client!"给客户端,处理完一个客户端的请求后(等待client调用close关闭socket,此时recv或者send阻塞函数返回)关闭对应的socket,继续accept来处理新的client的请求。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <signal.h>
#include <systemd/sd-daemon.h>
#include <netinet/in.h>
#include <systemd/sd-journal.h>

#define SERVER_PORT 8080
#define BUFFER_SIZE 1024
#define true 1
#define false 0

typedef int bool;

int main() {
    
    int handle = -1;
    
    int n = sd_listen_fds(0);

    if (n <= 0) {
        sd_journal_print(LOG_INFO, "No file descriptors received,we need to creat a tcp socket.\n");
        handle = socket(AF_INET, SOCK_STREAM, 0);

        if (handle == -1) {
            perror("socket");
            exit(1);
        }

        struct sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(SERVER_PORT);

        if (bind(handle, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
            sd_journal_print(LOG_INFO, "server bind error\n");
            exit(1);
        }

        if (listen(handle, 10) == -1) {
            sd_journal_print(LOG_INFO, "server listen error\n");
            exit(1);
        }        
    }else{
        sd_journal_print(LOG_INFO, "Received %d file descriptor(s) from systemd \n", n);

        int last_handle = SD_LISTEN_FDS_START + n;

        bool fd_check_ok = false;

        for (handle = SD_LISTEN_FDS_START; handle < last_handle; ++handle)
        {
            if (sd_is_socket_inet (handle, AF_INET, SOCK_STREAM, 1,
                    (uint16_t) SERVER_PORT) > 0) {
                sd_journal_print(LOG_INFO, "this is a IPv4 TCP socket listening on port %d \n",SERVER_PORT);
                fd_check_ok = true;
                break;
            }
        }

        if(fd_check_ok != true)
        {
            sd_journal_print(LOG_ERR, "No file descriptors found for port %d .\n",SERVER_PORT);
            return 1;
        }
    }
    

    struct sockaddr_storage client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char buffer[BUFFER_SIZE];
    int client_fd;

    while (1) {
        client_fd = accept(handle, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_fd < 0) {
            sd_journal_print(LOG_INFO,"Accept failed\n");
            continue;
        }
         while (1) {
                int bytes_received = recv(client_fd, buffer, BUFFER_SIZE, 0);
                if (bytes_received <= 0) {
                    sd_journal_print(LOG_INFO,"Server Receive failed");
                    break;
                } else {
                    buffer[bytes_received] = '\0';
                    sd_journal_print(LOG_INFO,"Message received from client: %s\n", buffer);
                }

                strcpy(buffer, "Hello, client!");
                int bytes_send = send(client_fd, buffer, strlen(buffer), 0);
                if(bytes_send<=0)
                {
                    sd_journal_print(LOG_INFO,"Server Send failed");
                    break;
                }

         }     
        close(client_fd);
    }
    return 0;
}

代码编译

sudo gcc -o /usr/local/bin/myservice myservice.c -lsystemd

在ubuntu下,如果没有安装systemd,需要安装sudo apt install libsystemd-dev。

  • 在/etc/systemd/system/下创建myservice.socket文件:监听本地的tcp 8080端口。
[Unit]
Description=My Service Socket

[Socket]
ListenStream=127.0.0.1:8080

[Install]
WantedBy=sockets.target
  • 接着,在同一目录下创建myservice.service文件,其中的选项都比较好理解,After是启动顺序,Requires是依赖,ExecStart是服务端程序,标准输入设置为null,因为程序不需要输入,输出和错误设置为journal,用于利用systemd的journal系统打印log(journal是一个强大的日志系统),TimeoutStopSec指定停止服务最长的实际为5秒,如果5秒时间到了后还没有停止就强制停止并报告相关的失败。
[Unit]
Description=My Service
After=network.target myservice.socket
Requires=myservice.socket

[Service]
Type=simple
ExecStart= /usr/local/bin/myservice
StandardOutput=journal
StandardError=journal
StandardInput=null
TimeoutStopSec=5
  • 接下来,启动socket单元,并利用systemctl enable命令配置myservice服务在ubuntu系统启动时自动启动
sudo systemctl start myservice.socket
sudo systemctl enable myservice.socket

启动socket单元后,利用ss -ltnp命令检测端口是否在监听,从输出可以看到8080端口正在监听,

lyf@lyf:/etc/systemd/system$ ss -lntp
State            Recv-Q           Send-Q                      Local Address:Port                      Peer Address:Port          Process
LISTEN           0                4096                            127.0.0.1:8080                           0.0.0.0:*
LISTEN           0                4096                           127.0.0.54:53                             0.0.0.0:*
LISTEN           0                511                             127.0.0.1:60737                          0.0.0.0:*              users:(("node",pid=462,fd=19))
LISTEN           0                4096                        127.0.0.53%lo:53                             0.0.0.0:*
LISTEN           0                1000                       10.255.255.254:53                             0.0.0.0:*

实时显示systemd中服务的log,使用下面的命令,-u后跟myservice.service前半部分,-f选项来持续查看实时日志输出,类似于tail -f命令,

 sudo journalctl -u myservice -f

此时,myservice.socket对应的服务已经启动,如下所示,但是由于还没有客户端的实际请求,此时,myservice.service还没有启动,状态是inactive的(之前启动过)。

5. 客户端程序及测试

客户端程序myclient.c,

  • 编译 gcc -o myclient myclient.c;
  • 连接到本地的8080端口上,先发送"Hello, Server!"给server,然后从server读取数据。如此重复三次。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define SERVER_PORT 8080
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE] = {0};

    // 创建套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
        perror("Invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }
    for(int snd_count = 0; snd_count < 3;snd_count++)
    {
        // 发送消息到服务器
        strcpy(buffer, "Hello, Server!");
        send(sockfd, buffer, strlen(buffer), 0);
        printf("Message sent to server: %s\n", buffer);

        memset(buffer, '\0', BUFFER_SIZE);
        // 接收服务器的响应
        int bytes_received = recv(sockfd, buffer, BUFFER_SIZE,0);
        if (bytes_received < 0) {
            perror("Receive failed");
        } else {
            buffer[bytes_received] = '\0';
            printf("Message received from server: %s\n", buffer);
        }
        sleep(1);

    }

    // 关闭套接字
    close(sockfd);

    return 0;
}

客户端发送请求,收取server端的数据,发送三次"Hello, Server!",收取3次"Hello, client!"。

收到客户端connect请求后,service对应的log输出,

  • 接收到客户端发来的3次"Hello, Server!",并做回复;
  • 最终阻塞在recv上,当客户端关闭socket后,recv函数返回0,退出;

收到客户端的connect请求后,systemd会自动启动myservice服务,如下所示,

6. 停止服务

  • 关于服务的停止,这里牵扯到2个服务,myservice.socket和myservice.service。myservice.socket服务去启动myservice服务。因此如果要彻底停止服务,需要将两个服务都停掉;
  • 单纯停止myservice.service服务后,因为systemd socket激活机制,systemd继续在后台监听8080端口,必须将myservice.socket服务停止,才能彻底结束监听8080端口。
lyf@lyf:/etc/systemd/system$ sudo systemctl stop myservice
[sudo] password for lyf:
Stopping 'myservice.service', but its triggering units are still active:
myservice.socket
lyf@lyf:/etc/systemd/system$ ss -lntp
State            Recv-Q           Send-Q                      Local Address:Port                      Peer Address:Port          Process
LISTEN           0                4096                            127.0.0.1:8080                           0.0.0.0:*
LISTEN           0                4096                           127.0.0.54:53                             0.0.0.0:*
LISTEN           0                511                             127.0.0.1:60737                          0.0.0.0:*              users:(("node",pid=462,fd=19))
LISTEN           0                4096                        127.0.0.53%lo:53                             0.0.0.0:*
LISTEN           0                1000                       10.255.255.254:53                             0.0.0.0:*
lyf@lyf:/etc/systemd/system$ sudo systemctl stop myservice.socket
lyf@lyf:/etc/systemd/system$ ss -lntp
State            Recv-Q           Send-Q                      Local Address:Port                      Peer Address:Port          Process
LISTEN           0                4096                           127.0.0.54:53                             0.0.0.0:*
LISTEN           0                511                             127.0.0.1:60737                          0.0.0.0:*              users:(("node",pid=462,fd=19))
LISTEN           0                4096                        127.0.0.53%lo:53                             0.0.0.0:*
LISTEN           0                1000                       10.255.255.254:53                             0.0.0.0:*

7. 让systemd去accept

The name of the .service unit is by default the same as the name of the .socket unit, but can be altered with the Service= option described below. Depending on the setting of the Accept= option described below, this .service unit must either be named like the .socket unit, but with the suffix replaced, unless overridden with Service=; or it must be a template unit named the same way. Example: a socket file foo.socket needs a matching service foo.service if Accept=no is set. If Accept=yes is set, a service template foo@.service must exist from which services are instantiated for each incoming connection.

  • 如果在.socket文件中,设置了Accept=no,默认值是no,不设置既是no。foo.socket对应的服务的文件名为foo.service;
  • 如果在.socket文件中,设置了Accept=yes,foo.socket对应的服务应该设置为foo@.service,注意这个@符号;

当在.socket 文件中设置 Accept=yes 时,参考上图,systemd 的行为如下:

  • systemd 会为每个新的连接创建一个新的服务实例,每个连接都在独立的进程中处理,提高了安全性和隔离性;
  • systemd负责创建socket,bind,listen,accept,然后当有client连接时,accept返回,此时将返回的socket描述符传递给新启动的服务实例;
  • .service 文件中应该包含 StandardInput=socket 和 StandardOutput=socket,表示将service服务进程的标准输入、标准输出和accept返回的socket描述符做了一定的“绑定/映射”;
  • 在启动的服务中,建立的连接,读,被重定向到标准输入,STDIN_FILENO,0。写,被重定向到标准输出,STDOUT_FILENO,1。service服务程序可以直接从 stdin 读取数据,写入 stdout(不需要执行 listen() 或 accept() 调用,直接拿到的就是0和1描述符);
  • 每个连接都在单独的进程中处理,自动实现并发。不需要在程序中实现多线程或多进程逻辑(典型的单连接单进程并发)。
  • 每个连接都有独立的进程,可能会增加系统资源使用,适合短连接或低并发场景

修改后的myservice.socket文件,

[Unit]
Description=My Service Socket

[Socket]
ListenStream=127.0.0.1:8080
Accept=yes

[Install]
WantedBy=sockets.target

修改后的.service文件,需要重命名为myservice@.service,注意StandardInput和StandardOutput都设置为了socket。

[Unit]
Description=My Service
After=network.target myservice.socket
Requires=myservice.socket

[Service]
Type=simple
ExecStart= /usr/local/bin/myservice
StandardInput=socket
StandardOutput=socket
StandardError=journal

service服务端程序myservice.c修改为(client客户端的代码保持一致),

  • 代码中没有任何的socket的相关接口,其实也可以将recv和send函数修改为read和write;
  • 从标准输入,STDIN_FILENO,读。往标准输出,STDOUT_FILENO,写;
  • 向客户端发送的数据中添加了服务端pid的字符串。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <signal.h>
#include <systemd/sd-daemon.h>
#include <systemd/sd-journal.h>

#define BUFFER_SIZE 1024

int main() {

        pid_t pid;
        pid = getpid();
        
        char buffer[BUFFER_SIZE];
        while (1) {
                int bytes_received = recv(STDIN_FILENO, buffer, BUFFER_SIZE-1,0);
                if (bytes_received <= 0) {
                    sd_journal_print(LOG_ERR,"Server recv() failed\n");
                    if(bytes_received == 0)
                        sd_journal_print(LOG_ERR,"Server recv() return 0, client close the socket \n");
                    break;
                } else {
                    buffer[bytes_received] = '\0';
                    sd_journal_print(LOG_ERR,"server %d  received client msg: %s \n", pid, buffer);
                }
                sprintf(buffer,"Hello, client!_%d",pid);
                int bytes_send = send(STDOUT_FILENO, buffer, strlen(buffer),0);
                if(bytes_send<=0)
                {
                    sd_journal_print(LOG_ERR,"Server send() failed\n");
                    if(bytes_send == 0)
                        sd_journal_print(LOG_ERR,"Server send() return 0, client close the socket \n");
                    break;
                }

         }     

    return 0;
}
  • 创建好后,重新编译代码,停止并重启myservice.socket服务。
  • 启动2个client客户端,对应的log如下所示,

client1,30511为对应启动的server端服务进程的pid。

lyf@lyf:~/systemd_test$ ./myclient
Message sent to server: Hello, Server!
Message received from server: Hello, client!_30511
......

client2,30502为对应启动的server端服务进程的pid。

lyf@lyf:~/systemd_test$ ./myclient
Message sent to server: Hello, Server!
Message received from server: Hello, client!_30502

service服务端的log,可以通过下面的命令查看,

journalctl -xe | grep -i myservice

输出如下,

Jul 10 19:10:54 lyf systemd[1]: Started myservice@16-127.0.0.1:8080-127.0.0.1:60802.service - My Service (127.0.0.1:60802).
?? Subject: A start job for unit myservice@16-127.0.0.1:8080-127.0.0.1:60802.service has finished successfully
?? A start job for unit myservice@16-127.0.0.1:8080-127.0.0.1:60802.service has finished successfully.
Jul 10 19:10:54 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:10:55 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:10:56 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:10:57 lyf systemd[1]: Started myservice@17-127.0.0.1:8080-127.0.0.1:60866.service - My Service (127.0.0.1:60866).
?? Subject: A start job for unit myservice@17-127.0.0.1:8080-127.0.0.1:60866.service has finished successfully
?? A start job for unit myservice@17-127.0.0.1:8080-127.0.0.1:60866.service has finished successfully.
Jul 10 19:10:57 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:10:57 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:10:58 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:10:58 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:10:59 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:10:59 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:11:00 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:00 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:11:01 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:01 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:11:02 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:02 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:11:03 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:03 lyf myservice[30502]: server 30502  received client msg: Hello, Server!
Jul 10 19:11:04 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:04 lyf myservice[30502]: Server recv() failed
Jul 10 19:11:04 lyf myservice[30502]: Server recv() return 0, client close the socket
Jul 10 19:11:04 lyf systemd[1]: myservice@16-127.0.0.1:8080-127.0.0.1:60802.service: Deactivated successfully.
?? The unit myservice@16-127.0.0.1:8080-127.0.0.1:60802.service has successfully entered the 'dead' state.
Jul 10 19:11:05 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:06 lyf myservice[30511]: server 30511  received client msg: Hello, Server!
Jul 10 19:11:07 lyf myservice[30511]: Server recv() failed
Jul 10 19:11:07 lyf myservice[30511]: Server recv() return 0, client close the socket
Jul 10 19:11:07 lyf systemd[1]: myservice@17-127.0.0.1:8080-127.0.0.1:60866.service: Deactivated successfully.
?? The unit myservice@17-127.0.0.1:8080-127.0.0.1:60866.service has successfully entered the 'dead' state.
  • 为了便于观察,2个client客户端各进行了10次的数据收发,对应server端的进程的pid和上文中对应,也就是30502和30511;
  • client1,systemd[1]: Started myservice@16-127.0.0.1:8080-127.0.0.1:60802.service - My Service (127.0.0.1:60802).,客户端的端口为60802。同理,client2客户端的端口为60866;
  • client1, 端口60802,server服务端进程的pid为30502。client2, 端口60866,server服务端进程的pid为30511;
  • client1发送完数据后,调用close,server服务端(30502)进程recv返回0,退出,systemd[1]: myservice@16-127.0.0.1:8080-127.0.0.1:60802.service: Deactivated successfully.,服务端进程工作已经完成,自动退出;
  • client2发送完数据后,调用close,server服务端(30511)进程recv返回0,退出,systemd[1]: myservice@17-127.0.0.1:8080-127.0.0.1:60866.service: Deactivated successfully.,服务端进程工作已经完成,自动退出;
  • 从以上log可以看出systemd对每个建立的连接都创建了一个新的进程来处理客户端的请求。

相关推荐

史上最全的浏览器兼容性问题和解决方案

微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...

平面设计基础知识_平面设计基础知识实验收获与总结
平面设计基础知识_平面设计基础知识实验收获与总结

CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...

2025-02-21 16:01 yuyutoo

写作排版简单三步就行-工具篇_作文排版模板

和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...

写一个2048的游戏_2048小游戏功能实现

1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...

今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……

  最近的天气暖和得让很多小伙伴们喊“热”!!!  昨天的气温到底升得有多高呢?你家有没有榜上有名?...

CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式

之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...

柠檬科技肖勃飞:大数据风控助力信用社会建设

...

你的自我界限够强大吗?_你的自我界限够强大吗英文

我的结果:A、该设立新的界限...

行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?

行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...

让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华

去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...

2025-02-21 16:00 yuyutoo

今年国家综合性消防救援队伍计划招录消防员15000名

记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...

一起盘点最新 Chrome v133 的5大主流特性 ?

1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...

竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使

style="text-align:center;"data-mce-style="text-align:...

学物理能做什么?_学物理能做什么 卢昌海

作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...

你不知道的关于这只眯眼兔的6个小秘密
你不知道的关于这只眯眼兔的6个小秘密

在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...

2025-02-21 16:00 yuyutoo

取消回复欢迎 发表评论: