天天看点

Linux编程入门笔记---多进程网络编程。

多进程编程的一些回忆:

1.进程的创建

最常用fork();

pid_t pid;
pid=fork();                               
if(pid==0){//pid=0表示该进程为子进程                           
 
}
else if(pid>0){//否则为父进程                                
    
}
else{//创建进程失败
	
} 
           

2.进程的回收

回收对象:孤儿进程、僵尸进程

进程

#include<sys/types.h>
#include<sys/wait.h>
 pid_t wait(int *status);
 //等待回收子进程
 pid_t waitpid(pid_t pid,int * status,int options);
 //waitpid会暂时停止目前进程的执行,直到有信号来到或子进程结束。
 //pid为等待回收进程的Id
 //status存储子进程结束后返回的状态
 //options 有两个宏
 //WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
//WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。
           

多进程开发示例

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
#include<stdlib.h>

#define SERV_PORT 8000

//捕捉的SIGCHLD信号,然后对其进行回收,当进程结束后会向其父进程发送信号,等待回收
void do_sigchild(int num)
{
    while (waitpid(0, NULL, WNOHANG) > 0);
}

int main(void)
{
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t cli_addr_len;
    int listen_fd, conn_fd;
    pid_t pid;
    char str[INET_ADDRSTRLEN];
    //初始化ser_addr,ser_addr用于保存服务器端的套接字信息
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(SERV_PORT);

    //用于监听的套接字
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);

    int opt = 1;
    // setsockopt函数目的是使得当端口调用close后,依然可以重新使用该端口
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    //将serv_addr和listen_fd进行绑定
    bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

    //监听。最多可连接20个客户端
    listen(listen_fd, 20);

    while (1) 
    {
        //cli_addr用于保存连接的客户端信息
        cli_addr_len = sizeof(cli_addr);
        conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
		
		//父进程会一直阻塞在accept函数等待客户端连接,但是当有子进程退出时,
		//父进程将会去进行执行信号处理函数,执行完成后并不会继续阻塞,而是往下继续子执行,且此时
		//accpt将返回-1,所以用while(conn_fd==-1)让父进程继续等待客户端。	
		while(conn_fd==-1)
		{
			conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
		};

        // 有新的连接则创建一个进程
        pid = fork();
        if (pid == 0) 
        {
            
			//打印客户端信息
			printf("%s with port %d connected\n",
	                        inet_ntop(AF_INET, &cli_addr.sin_addr, str, sizeof(str)),
	                        ntohs(cli_addr.sin_port));
	
		    //关闭监听端口
	        close(listen_fd);
	           
	        //用于交换信息的缓冲区
	        char buf[1024];
	        while (1) 
	        {
			int n=0;
			memset(buf,0,1024);
	        n = read(conn_fd, buf, sizeof(buf));
	        if (n==0) 
	        {
	            printf("client has been closed.\n");
	            break;
	        }
			else if(n<0)
			{
				printf("read err\n");
				break;
		   	}
                
           //打印客户端端口信息
            memset(str,0,INET_ADDRSTRLEN);
            printf("received massage from %s with port %d\n",
                    
			inet_ntop(AF_INET, &cli_addr.sin_addr, str, sizeof(str)),
                    ntohs(cli_addr.sin_port));
            printf("Massage read:%s\n",buf);
            
            //缓冲区,并写入信息
            char buf1[256]="Hello,this is server end";
        	//发送信息到服务器端
            write(conn_fd, buf1, sizeof(buf1));
        }
        close(conn_fd);
        return 0;
        } 
        //父进程负责回收子进程
        else if (pid > 0) 
        {
            struct sigaction act;
            act.sa_flags = 0;
            act.sa_handler = do_sigchild;
            sigemptyset(&act.sa_mask);
            sigaction(SIGCHLD, &act, NULL);
            close(conn_fd);
        }  
        else
        {
			printf("fork() error\n");
			exit(1);
        }
    }
    return 0;
}
           

继续阅读