在前一篇基礎服務器代碼基礎上,本文加入 Linux 系統特有的進程管理機制,解決僵屍進程問題。
fork() 只有 Unix/Linux 才有(無線程概念,只有子進程)。使用 fork() 必須配合 waitpid() 回收子進程,否則會產生僵屍進程。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
為了只在子進程退出時調用 waitpid(),需使用 sigaction() 捕獲 SIGCHLD 信號。
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction 結構,設置 sa_handler、sa_mask、sa_flags。/**
* Version: 0.2
* Description: Add signal handling for zombie process reaping
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h> /* exit */
#include <unistd.h> /* fork */
#define SERVPORT "2349"
void sigchld_handler() {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main(int argc, char *argv[])
{
struct addrinfo hints, *res;
int status;
int sockfd;
int connFd;
struct sockaddr_storage clientAddr; /* 支持 IPv4 和 IPv6 */
struct sigaction sa;
int sendSta;
char *msg;
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
status = getaddrinfo(NULL, SERVPORT, &hints, &res);
if (status != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
return 2;
}
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd == -1) {
perror("socket");
return 3;
}
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
perror("bind");
return 4;
}
if (listen(sockfd, 5) == -1) {
perror("listen");
return 5;
}
printf("======== Please Wait Client =========\n");
/* 設置信號處理 */
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* 系統調用被信號中斷後自動重啟 */
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(3);
}
while (1) {
char ipstr[INET_ADDRSTRLEN];
void *addr;
int len = sizeof(clientAddr);
connFd = accept(sockfd, (struct sockaddr *)&clientAddr, &len);
if (connFd == -1) {
perror("accept");
continue;
}
/* 顯示客戶端 IP (假設 IPv4) */
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientAddr;
addr = &(ipv4->sin_addr);
inet_ntop(AF_INET, addr, ipstr, sizeof(ipstr));
printf("Client: %s\n", ipstr);
if (!fork()) { /* 子進程 */
close(sockfd); /* 子進程不需要監聽套接字 */
msg = "The server simply displays a message!";
sendSta = send(connFd, msg, strlen(msg), 0);
if (sendSta == -1)
fprintf(stderr, "send fail!\n");
close(connFd);
exit(0);
}
close(connFd); /* 父進程關閉已連接套接字 */
}
close(sockfd);
return 0;
}
摘自 xiaobin_HLJ80 的專欄