之前程序存在着一个不确定性的因素。之前服务器代码中。子进程在死亡后,会向父进程发送SIGCHLD信号。这个信号会被父进程捕获,然后父进程调用wait函数对死亡子进程处理防止其变成僵尸进程(僵尸进程会一直占用内存资源在它被回收之前。如果父进程在死亡之前没有回收僵尸进程,那么在unix下,僵尸进程会被init进程托管并回收。无主进程都会被托管给init)。
执行之前的代码,然后执行ps -aux可以看到不会有服务器派生的子进程成为僵死进程,但如果将客户端代码改成
[root@liumengli net]# cat echo_client2.c
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, char ** argv) {
int sockfd[5], i;
struct sockaddr_in serv_socket;
for(i = 0; i < 5; i++) {
sockfd[i] = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serv_socket, sizeof(serv_socket));
serv_socket.sin_family = AF_INET;
serv_socket.sin_port = htons(atoi(argv[1]));
inet_pton(AF_INET, "192.168.1.235", &serv_socket.sin_addr);
connect(sockfd[i], (struct sockaddr *)&serv_socket, sizeof(serv_socket));
}
getchar();
exit(0);
}
这个代码也很简单,只是一次对服务器进行了5次链接,而由此导致服务器会产生5个子进程为其服务,当我们在客户端按下回车,终止客户端进程后。再到服务器上输入ps -aux会看到有4个僵尸进程(一般是4个)。产生的原因是因为:unix不会对信号排队。
所谓信号不排队,在这里是指,当子进程SIGHCLD到达时,父进程开始响应这个信号,并作出处理。同时会关闭对SIGCHLD的响应,在处理过程中到达的SIGHCLD信号不会被响应,但也不会排队等待,信号会被丢失。
之前5个SIGCHLD信号几乎是同时到达,其中之一个被响应,而其它信号则会丢失,从而导致只调用了一次处理函数,剩下的进程就会变成僵尸进程。
要想解决这个问题,首先要看wait函数和waitpid这2者之间的区别。(当然也可以从信号不排队处着手,但这貌似难度很大)。
pid_t wait(int * statloc)
返回终止进程的pid和状态,状态存在于statloc中,可以通过sys/wait.h定义的宏操作获取。当父进程调用wait时候,如果所有的子进程都在运行,则父进程会被阻塞(当然,这里我们是在收到SIGCHLD信号后才调用,所以不会被阻塞)。如果没有子进程则wait会出错并立即返回。
我们再次分析之前的服务器情况,当子进程SIGCHLD信号到达时候,父进程开始调用my_op函数开始处理僵尸子进程。但此时共有5个子进程死亡,并有5个SIGCHLD到达,其它4个信号丢失,wait只是处理其中一个子进程从而有4个僵尸进程遗留。
当我们采用waitpid则可以用适当的手段解决这类问题。
pid_t waitpid(pid_t pid, int * statloc, int options)
返回终止的子进程的pid。参数pid == -1时候等待任意子进程,pid > 0或pid < 0等待与pid绝对值相同的子进程。pid == 0等待其组ID和调用者组ID相同的任一进程。statloc同上。options == WNOHANG若由PID等待的子进程并不立即可用,waitpid不等待,立即返回0.另一个不多做解释。
当我们把my_op函数改成
void my_op(int signum, siginfo_t * info, void * myact) {
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return;
}
就可以有效避免因信号不排队到导致的某些僵尸子进程无法回收的不可预料因素。当然信号丢失的情况仍在发生,但我们关心的是僵尸子进程的回收,这里只是通过其它办法回收了僵尸子进程。
分析这段函数,当这个函数被调用时候,也就是收到了SIGCHLD信号,也就是有子进程死亡。不管是多个还是1个信号到达。执行到循环体,就会将所有僵尸子进程回收后,退出。当然,极端情况当循环体执行完后,所有僵尸进程回收结束,进入return时候,此时也有可能有SIGCHLD到达,这并不要紧,因为即使这次没有回收,等下次有子进程发出SIGCHLD信号时,上次遗留的依然会被清除。
其实此时服务器代码还可以改成,不加载信号处理代码,直接将循环体写到创建子进程的循环内部,当然不能写到子进程代码里面。当然此时,所有子进程死亡不会引起waitpid被调用,而是当下次子进程创建时候,遇到这段代码,则上次死亡的僵尸进程会被回收。这种方式没有加载信号处理的方式有效。
分享到:
相关推荐
【JavaSocket多客户端并发通信聊天程序的设计与实现】 在Java编程中,Socket是进行网络通信的核心组件,尤其在构建多客户端并发聊天程序时,Socket扮演着至关重要的角色。本文将详细探讨基于JavaSocket的多客户端...
标题 "c++实现socket:一个服务器对应多个客户端相互传递信息" 指的是利用C++的socket API创建一个服务器程序,该服务器可以同时处理来自多个客户端的连接请求,并允许这些客户端之间交换数据。这种设计模式通常称为...
基于 socket 编程的基础上,对比了 Linux 系统下三种多路复用 I/O 接口:select、poll、epoll,确定了以 socket、epoll 机制以及线程池为基础来设计与实现一个客户端/服务器(client/server)模型的高并发服务器。...
本文将深入探讨Zinx框架如何与Golang结合,构建高效且稳定的MMO游戏服务器,并简单介绍客户端的相关内容。 Zinx框架的核心设计理念是模块化和可扩展性。它提供了基本的网络通信模块,支持TCP和UDP协议,方便开发者...
本资源提供的是一套基于C#语言实现的TCP服务器和客户端代码,利用了完成端口(IO Completion Ports, I/OCP)技术来优化高并发和高性能的网络通信。 完成端口是Windows系统中的一种高级I/O模型,它通过将I/O操作的...
总结一下,Socket服务器与客户端编程结合链表是一种常见的网络编程模式,特别是在处理并发连接时。通过链表,服务器可以高效地管理多个客户端连接,而Socket则提供了底层的通信机制。改端口可以根据实际需求调整...
本项目提供的"客户端与服务器的对话程序,有UI界面"就是一个典型示例,它允许用户通过图形用户界面(UI)与服务器进行交互。以下是关于该程序及所涉及技术的详细解释: 1. **客户端**:客户端是用户直接操作的部分...
TCP并发服务器是网络编程中的一个重要概念,它是指服务器可以同时处理多个客户端连接请求的机制。在高并发场景下,为了高效地服务大量客户端,服务器必须具备处理并发的能力。本篇将详细探讨TCP并发服务器的工作原理...
在标题"socket 一个服务器对应多个客户端使用多线程"中,提到的是使用Socket编程来设计一个服务器,该服务器能够同时处理来自多个客户端的连接请求,这通常被称为“一对一”或“一对多”模型。在Linux环境下,这种...
本篇文章将深入探讨使用C#进行服务器与客户端通信的关键概念、技术和实践方法。 一、C#网络编程基础 C#提供了丰富的库来支持网络通信,主要通过System.Net命名空间。在这个命名空间中,我们可以找到Socket类,它是...
本文将深入探讨如何使用SOCKET API实现一个单客户端与多个服务器的通讯,重点在于多线程和阻塞I/O模式的运用,以及在MFC(Microsoft Foundation Classes)环境下进行编程。 首先,我们来理解“单客户端与多服务器...
在Windows客户端中,你可以选择使用官方提供的命令行工具taos shell,或者利用SDK开发自定义应用,与服务器进行交互。SDK包括C、Java、Python、Go等语言版本,选择适合项目需求的语言进行开发。 对于Windows客户端...
Java NIO(Non-blocking Input/Output)是一种在Java中处理I/O操作的新方式,相比于传统的BIO(Blocking I/O),NIO提供了更高效的数据传输能力,尤其适合于高并发、低延迟的网络应用,如聊天服务器。在这个场景下,...
在IT领域,Socket编程是网络通信的核心技术之一,主要用于实现客户端和服务器之间的连接与通信。本项目名为"socket 单服务器/多客户端",显然关注的是如何构建一个能够处理多个客户端连接的单一服务器系统,并且修复...
在本文中,我们将深入探讨如何使用C#的Socket编程来实现一个简单的聊天应用程序,其中包含服务器和客户端的交互。...通过学习和实践这些概念,你可以创建出自己的网络应用程序,实现服务器与客户端之间的高效通信。
2. **QTcpSocket**:每个客户端连接都会有一个对应的`QTcpSocket`实例,用于实际的数据交换。你可以使用`write()`发送数据,`read()`接收数据,并连接到`readyRead()`信号,当有数据可读时,这个信号会被触发。 3. ...
当服务器接收到新的客户端连接请求时,可以创建一个新的线程或启动一个异步操作来处理这个连接,使得服务器可以并发处理多个客户端。 总的来说,"客户端/服务器通信示例程序"是一个演示如何使用VC++ 6.0实现TCP...
下面将详细阐述客户端与服务器端的基本概念、工作原理以及如何实现简单的消息传递。 1. **基本概念**: - **客户端**:客户端是用户操作的设备或程序,负责发起请求,通常执行用户界面功能,如输入、显示和交互。 ...
开发者可能会讲解如何创建服务器端的监听线程,接收和广播消息,以及客户端如何连接到服务器,发送和接收消息。 4. **多线程**:在服务器端,为了处理多个客户端的并发连接,通常会使用多线程技术。每个客户端连接...
在多线程模型中,每个客户端连接对应一个线程,这样服务器可以同时处理多个并发连接。另一种常见的方式是使用IOCP(I/O完成端口),它可以更高效地处理大量并发连接。 4. **消息传递**:服务器接收到客户端发送的...