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

systemd的socket激活机制 socket system

yuyutoo 2024-10-11 21:39 9 浏览 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对每个建立的连接都创建了一个新的进程来处理客户端的请求。

相关推荐

自卑的人容易患抑郁症吗?(自卑会导致抑郁吗)

Filephoto[Photo/IC]Lowself-esteemmakesusfeelbadaboutourselves.Butdidyouknowthatovert...

中考典型同(近)义词组(同义词考题)

中考典型同(近)义词组...

WPF 消息传递简明教程(wpf messagebox.show)

...

BroadcastReceiver的原理和使用(broadcast-suppression)

一、使用中注意的几点1.动态注册、静态注册的优先级在AndroidManifest.xml中静态注册的receiver比在代码中用registerReceiver动态注册的优先级要低。发送方在send...

Arduino通过串口透传ESP 13板与java程序交互

ESP13---是一个无线板子,配置通过热点通信Arduino通过串口透传ESP13板与java程序交互...

zookeeper的Leader选举源码解析(zookeeper角色选举角色包括)

作者:京东物流梁吉超zookeeper是一个分布式服务框架,主要解决分布式应用中常见的多种数据问题,例如集群管理,状态同步等。为解决这些问题zookeeper需要Leader选举进行保障数据的强一致...

接待外国人英文口语(接待外国友人的英语口语对话)

接待外国人英文口语询问访客身份:  MayIhaveyourname,please?  请问您贵姓?  Whatcompanyareyoufrom?  您是哪个公司的?  Could...

一文深入理解AP架构Nacos注册原理

Nacos简介Nacos是一款阿里巴巴开源用于管理分布式微服务的中间件,能够帮助开发人员快速实现动态服务发现、服务配置、服务元数据及流量管理等。这篇文章主要剖析一下Nacos作为注册中心时其服务注册与...

Android面试宝典之终极大招(android面试及答案)

以下内容来自兆隆IT云学院就业部,根据多年成功就业服务经验,以及职业素养课程部分内容,归纳总结:18.请描述一下Intent和IntentFilter。Android中通过Intent...

除了Crontab,Swoole Timer也可以实现定时任务的

一般的定时器是怎么实现的呢?我总结如下:1.使用Crontab工具,写一个shell脚本,在脚本中调用PHP文件,然后定期执行该脚本;2.ignore_user_abort()和set_time_li...

Spark源码阅读:DataFrame.collect 作业提交流程思维导图

本文分为两个部分:作业提交流程思维导图关键函数列表作业提交流程思维导图...

使用Xamarin和Visual Studio开发Android可穿戴设备应用

搭建开发环境我们需要做的第一件事情是安装必要的工具。因此,你需要首先安装VisualStudio。如果您使用的是VisualStudio2010,2012或2013,那么请确保它是一个专业版本或...

Android开发者必知的5个开源库(android 开发相关源码精编解析)

过去的时间里,Android开发逐步走向成熟,一个个与Android相关的开发工具也层出不穷。不过,在面对各种新鲜事物时,不要忘了那些我们每天使用的大量开源库。在这里,向大家介绍的就是,在这个任劳任怨...

Android事件总线还能怎么玩?(android实现事件处理的步骤)

顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity、Fragment、Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码...

Android 开发中文引导-应用小部件

应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截...

取消回复欢迎 发表评论: