本文主要是介绍TCP/IP网络编程学习(10):利用信号处理技术消灭僵尸进程和基于多任务的并发处理器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
利用信号处理技术消灭僵尸进程
子进程终止时产生SIGCHLD信号。就可以捕捉信号终止子进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>void read_childproc(int sig){//子进程终止调用该函数int status;pid_t id=waitpid(-1, &status, WNOHANG);//将子进程正常终止if(WIFEXITED(status)){printf("Removed proc id: %d \n", id);printf("Child send: %d \n", WEXITSTATUS(status));}
}int main(int argc, char *argv[])
{pid_t pid;struct sigaction act;act.sa_handler=read_childproc;sigemptyset(&act.sa_mask);act.sa_flags=0;sigaction(SIGCHLD, &act, 0);pid=fork();if(pid==0){puts("Hi! I'm child process");sleep(10);//等待10s终止return 12;}else{ printf("Child proc id: %d \n", pid);pid=fork();if(pid==0){puts("Hi! I'm child process");sleep(10);exit(24);}else{int i;printf("Child proc id: %d \n", pid);for(i=0; i<5; i++){puts("wait...");sleep(5);}}}return 0;
}
基于多任务的并发服务器
每当有客户端请求服务时,服务器都创建子进程以提供服务。
- 服务器端(父进程)通过调用 accept画数受理连接请求 。
- 此时获取的套接字文件描述符创建并传递给子进程 。(因为子进程会复制父进程拥有的所有资源 。实际上根本不用另外经过传递文件描述符的过程 。)
- 子进程利用传递来 的文件描述符提供服务 。
while(1){adr_sz=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);if(clnt_sock==-1)continue;elseputs("new client connected...");pid=fork();if(pid==-1){close(clnt_sock);continue;}if(pid==0){close(serv_sock);while((str_len=read(clnt_sock, buf, BUF_SIZE))!=0)write(clnt_sock, buf, str_len);close(clnt_sock);puts("client disconnected...");return 0;}elseclose(clnt_sock);}
通过fork函数复制文件描述符
套接字属于操作系统,fork子进程只是将父进程的文件描述符拿了过去。进程拥有代表相应套接字的文件描述符。父进程将2个套接字(一个是服务器端套接字,另一个是与客户端连接的套接字)文件描述符复制给子进程 。
1个套接字中存在2个文件描述符时,只有2个文件描述符都终止 (销毁)后,才能销毁套接字 。如果维持阁中的连接状态,即使子进程销毁了与客户端连接的套接字文件描述符,也无法完全销毁套接字(服务器端套接字同样如此)。 因此,调用fork函数后,要将无关的套接字文件描述符关掉,
一次
accept
就会创建一个套接字。
分割TCP的I/O程序
传输数据后需要等待服务器端返回的数据,因为程序代码中重复调用了 read和write函数 。 只能能这么写的原因之一是,程序在 1 个进程中运行 。 但现在可以创建多个进程,因此可以分割数据收发过程 。 客户端的父进程负责接收数据,额外创建的子进程负责发送数据 。 分割后,不同进程分别负责输入和输出,这样无论客户端是否从服务器端接收完数据都可以进行传输 。
之前的回声客户端要接收到服务器的数据才发数据,现在可以连发数据。
分割I/O后的客户端发送数据时不必考虑接收数据的情况,因此可以连续发送数据,由此提高同一时间内传输的数据量 。 这种差异在网速较慢时尤为明显 。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 30
void error_handling(char *message);
void read_routine(int sock, char *buf);
void write_routine(int sock, char *buf);int main(int argc, char *argv[])
{int sock;pid_t pid;char buf[BUF_SIZE];struct sockaddr_in serv_adr;if(argc!=3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock=socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=inet_addr(argv[1]);serv_adr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)error_handling("connect() error!");pid=fork();if(pid==0)write_routine(sock, buf);//子进程程用来写else read_routine(sock, buf);//父进程用来读close(sock);return 0;
}void read_routine(int sock, char *buf)
{while(1){int str_len=read(sock, buf, BUF_SIZE);if(str_len==0)return;buf[str_len]=0;printf("Message from server: %s", buf);}
}
void write_routine(int sock, char *buf)
{while(1){fgets(buf, BUF_SIZE, stdin);if(!strcmp(buf,"q\n") || !strcmp(buf,"Q\n")){ shutdown(sock, SHUT_WR);return;}write(sock, buf, strlen(buf));}
}
void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}
这篇关于TCP/IP网络编程学习(10):利用信号处理技术消灭僵尸进程和基于多任务的并发处理器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!