进程之间的通信(本地通信)

管道, 同享内存(mmap), socket

Posted by chensong on 2017-11-04 00::00::41

前言

父子进程之间的通信的有五种

  1. 管道进程的pipe函数和dup2函数组合使用父子进程间通信
  2. 管道 mkfifio函数的使用
  3. mmap内存映射的机制
  4. socketpair函数的父子进程描述符传递
  5. socket本地通信

正文

#define  STDIN_FILENO     		0
#define  STDOUT_FILENO    		1
#define  STDERR_FILENO    		2

一,管道进程的pipe函数和dup2函数组合使用父子进程间通信

参数是数组管道文件描述符

pipe参数是:

  1. fd[0]是读文件描述符,
  2. fd[2]是写文件描述符
 int pipe(int pipefd[2]);

案例1:

/*************************************************************************
	> File Name: pipe.c
	> Author: songli
	> QQ: 2734030745
	> Mail: 15850774503@163.com
    > CSDN: http://my.csdn.net/Poisx
	> github: https://github.com/chensongpoixs
	> Created Time: Fri 20 Oct 2017 10:22:29 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>



int main(int argc, char *argv[])
{
    //管道数组文件描述符
    int fd[2];       
    int ret;
    

    int i, n = 2;
    //创建管道
    ret = pipe(fd);

    //文件描述符
    pid_t pid;

    if (ret < 0)
    {
        perror("pipe error");
        return -1;
    }

    for (i = 0; i < n; i++)
    {
        //创建子进程
        pid = fork();
        if (pid < 0) // 异常处理
        {
            perror("fork error");
            return -1;
        }
        else if (pid > 0) // 父进程的操作
        {
            printf("father fpid:%d, cpid:%d\n", getpid(), pid);
        }
        else if (pid == 0) //子进程的操作
        {
            printf("child fpid:%d, cpid:%d\n", getppid(), getpid());
            break; //防止子线程创建子进程
        }
    }
    //===============  子进程的操作=============================
    if (i == 0)
    {
        printf("no child n = %d, cpid:%d\n", i, getpid());
        //=================写入数据 =====================
        close(fd[0]); //关闭读取文件描述符
        //标准输入也是 文件描述符   中的1
        dup2(fd[1], STDOUT_FILENO);
        // 执行  写入命令
        execlp("ps", "ps", "-ef", NULL);
        close(fd[1]); //关闭写入文件描述符
        return 0; //signal  正常退出
    }
    if (i == 1)
    {
        printf("no child n = %d, cpid:%d\n", i, getpid());
        //=================写入数据 =====================
        close(fd[1]); //关闭 写描述符文件描述符
        //标准输出也是 文件描述符   中的1
        dup2(fd[0], STDIN_FILENO);
        // 执行  写入命令
        execlp("grep", "grep", "--color", "bash", NULL);
        close(fd[0]); //关闭写入文件描述符
        return 0; //signal  正常退出
    }

    //=================      主进程的操作 ========================
    if (i == n)
    {
        //pcb 进程块pid
        pid_t wpid;
        int status;

        while (1)
        {
            //等待线程
            wpid = waitpid(-1, &status, WNOHANG );
            if (wpid == 0) //有子进程程的操作
            {
                sleep(1);
                continue;
            }
            else if (wpid == -1) // 没有子进程的操作
            {
                printf("退出子进程的wpid:%d\n", wpid);
                exit(0);
            }
            else if (wpid > 0)  //什么原因退出
            {
                if (WIFEXITED(status)) //正常退出
                {
                    printf("子进程wpid:%d, status:%d\n", wpid, WEXITSTATUS(status));
                }
                if (WIFSIGNALED(status))
                {
                    printf("子进程wpid:%d, status:%d\n", wpid, WTERMSIG(status));
                    
                }
            }
        }

    }
	return 0;
}

效果图

1, 和fcntl函数一起使用可以数组read函数非阻塞

/*************************************************************************
	> File Name: pipe_fcntl.c
	> Author: songli
	> QQ: 2734030745
	> Mail: 15850774503@163.com
    > CSDN: http://my.csdn.net/Poisx
	> github: https://github.com/chensongpoixs
	> Created Time: Fri 20 Oct 2017 10:47:08 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>



int main(int argc, char *argv[])
{
    //数组文件描述符
    int fd[2];
    int ret;
    char buf[1024];

    //创建管道
    ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe error");
        return -1;

    }

    //pcb进程控制块pid
    pid_t pid;

    //创建子线程
    pid = fork();
    if (pid < 0) //异常处理
    {
        perror("fork error");
        return -1;
    }
    else if (pid > 0) //父进程的处理
    {
        printf("father fpid:%d, cpid:%d\n", getpid(), pid);

        //==============读取数据==================
        close(fd[1]); //关闭写文件操作
        //==================    在同一个进程中使用非阻塞使用fcntl函数 =========================================
        int flags = fcntl(fd[0], F_GETFL );
        flags |= O_NONBLOCK;
        fcntl(fd[0], F_SETFL, flags);
        //=============================================================
        while(1)
        {
            sleep(1);
            memset(buf, 0x00, sizeof(buf));
            ret = read(fd[0], buf, sizeof(buf));
            printf("读取数据的大小:%d buf = %s\n", ret, buf);
        }
        close(fd[0]);

    }
    else if (pid == 0) //子进程的操作
    {
       printf("child fpid:%d, cpid:%d\n", getppid(), getpid());
       //========== 写操作=================== 
        close(fd[0]); //关闭读取文件描述符
        int i = 0;
        while (1)
        {
            sleep(1);
            memset(buf, 0x00, sizeof(buf));
            sprintf(buf, "[%d]:[%s]", i++, "陈丽, 杨艳");
            write(fd[1], buf, strlen(buf));
        }
        close(fd[1]);
    }




	return 0;
}

** 效果图**

二,管道 mkfifio函数的使用

mkfifo命令生成管道文件

mkfifo函数 第一参数路径, 第二是权限

 int mkfifo(const char *pathname, mode_t mode);

1, 写入数据

/*************************************************************************
	> File Name: fifo_write.c
	> Author: songli
	> QQ: 2734030745
	> Mail: 15850774503@163.com
    > CSDN: http://my.csdn.net/Poisx
	> github: https://github.com/chensongpoixs
	> Created Time: Fri 20 Oct 2017 11:22:50 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>



int main(int argc, char *argv[])
{
    // 文件描述符
    int fd;
    
    int ret;
    //判断是否创建管道文件
    if ((ret = access("./myfifo", F_OK)) == -1)
    {
        //创建管道
        ret =  mkfifo("./myfifo", 0777);
        if (ret < 0)
        {
            perror("mkfifo error");
            return -1;
        }
    }

    //打开文件
    fd = open("./myfifo", O_RDWR);
    if (fd < 0)
    {
        perror("open error");
        return -1;
    }

    //=====================写数据到管道中============================

    while (1)
    {
        sleep(1);
        write(fd, "陈丽, 杨艳", strlen("杨艳"));
    }

    close(fd);


	return 0;
}

2, 读取数据

/*************************************************************************
> File Name: fifo_read.c
> Author: songli
> QQ: 2734030745
> Mail: 15850774503@163.com
> CSDN: http://my.csdn.net/Poisx
> github: https://github.com/chensongpoixs
> Created Time: Fri 20 Oct 2017 11:31:07 PM CST
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>




int main(int argc, char *argv[])
{
    //文件描述符
    int fd;


    int ret;

    char buf[1024];
    //检测是否有管道文件
    if ((ret = access("./myfifo", F_OK)) == -1)
    {
        ret = mkfifo("./myfifo", 0777);
        if (ret < 0)
        {
            perror("mkfifo error");
            return -1;
        }
    }

    //打开管道文件
    fd = open("./myfifo", O_RDWR);
    if (fd < 0)
    {
        perror("open error");
        return -1;
    }

    //==============读取管道中数据=========================

    while (1)
    {
        //=================   设置read函数非阻塞  ==================
/*        int flags = fcntl(fd, F_GETFL);
        flags |= O_NONBLOCK;
        fcntl(fd, F_SETFL, flags);*/
        sleep(1);
        memset(buf, 0x00, sizeof(buf));
        ret = read(fd, buf, sizeof(buf));
        printf("读取数据:%d, buf = %s\n", ret, buf);
    }
    //关闭文件
    close(fd);

    return 0;
}

效果图

三,mmap内存映射的机制

每个应用程序有虚的4个G的内存 由MMU把实际 要用的映射到内存中就是内核

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>



int main(int argc, char *argv[])
{
	//文件描述符
	int fd;
	int len;
	fd = open("./text.log", O_RDWR);
	if (fd < 0)
	{
		perror("open error");
		return -1;
	}
	//文件大小
	len = lseek(fd, 0, SEEK_END);

	//参数一:NULL
	//参数二:文件的大小,不能为0
	//参数三:权限
	//参数四:是否同步的硬盘中
	//参数五:文件指针
	//参数六:文件开始位置
	void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

	if (ptr == MAP_FAILED)
	{
		perror("mmap error");
		return -1;
	}
	char *p = (char*)ptr;
	strcpy(p, "陈丽");

	//参数一:文件指针
	//参数二:文件大小
	munmap(ptr, len);
	//关闭文件
	close(fd);


	return 0;
}

1, 没有告关系的进程之间通信

写数据的

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
	//文件描述符
	int fd;
	int len; //文件大小
	int ret; //状态码
	//打开文件
	fd = open("./text.log", O_RDWR);
	if (fd < 0)
	{
		perror("open error");
		return -1;
	}
	//文件大小
	len = lseek(fd, 0, SEEK_END);
	//映射到内核中
	void * ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

	if (ptr == MAP_FAILED)
	{
		perror("mmap error");
		return -1;
	}
	char * p = (char *) ptr;
	strcpy(p, "陈丽, 王蓉");


	//关闭
	munmap(ptr, len);
	close(fd);


	return 0;
}

读取数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

/**
读取数据mmap_read

**/

int main(int argc, char *argv[])
{
	//文件指针
	int fd;
	int len; //文件大小
	void * ptr; //映射内存地址
	char buf[1024];
	//打开文件
	fd = open("./text.log", O_RDWR);
	if (fd < 0)
	{
		perror("open error");
		return -1;
	}

	//文件大小
	len = lseek(fd, 0, SEEK_END);
	//创建映射
	ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED)
	{
		perror("mmap error");
		return -1;
	}

	//=============读取 map内存的数据===================
	//while (1)
	//{
		//memset(buf, 0x00, sizeof(buf));
	//	read(fd, buf, sizeof(buf));
	char *p = (char *) ptr;
		printf("读取数据:%s\n", p);
	//}
	munmap(ptr, len);

	//关闭文件操作
	close(fd);
	
	return 0;
}

四, sockepair 父子进程通信

socketpair 是父子进程通信的一种:nginx使用master与worker进程之间的通信就是基于socketpair通信的

  1. 其实socketpair是基于pipe的通信的全双工的
  2. 父子进程之间的 “描述符传递”
  3. 流程- open, pipe,mkfifio, socket, accept

1, msghdr结构体

不幸,对于4.3BSD以及在其基础上构造的SunOS和Ultrix,以及从4.3BSD Reno开始的后续版本我们必须提供不同的实现。
为了交换文件描述符,调用sendmsg(2)和recvmsg(2)函数。这两个函数的参数中都
有一个指向msghdr的指针,该结构包含了所有关于要发送和接收消息的信息。该结
构定义在〈sys/socket.h〉 头文件中,在BSD4.3之下,其样式是:

strcut msghdr {  
caddr_t  msg_name;    可选的地址  
int   msg_namelen;    地址长度  
struct iovec  msg_iov;     散布/聚集数组  
int    msg_iovlen;    在msg_iov数组中的元素数  
caddr_t  msg_accrights;    存取权发送/接收到  
int    msg-accrightslen;   存取权缓存的长度  
}  

从4.3BSD Reno开始,更改了msghdr结构的定义。在以前版本中被称之为”存取权”
的最后两个元素改称为”辅助数据”。另外,在该结构结束处增加了一个新成员msg
_flags。

strcut msghdr {  
caddr_t  msg_name;    可选的地址  
int   msg_namelen;    地址长度  
struct iovec  msg_iov;     散布/聚集数组  
int    msg_iovlen;    在msg_iov数组中的元素数  
caddr_t  msg_control;    辅助数据  
int    msg-controllen;   辅助数据的长度  
int    msg_flags;    接收到消息的标志  
}

我的内核查看

 killer@localhost:~/socket$ uname -r
4.4.0-131-generic

实例程序

// gcc -o socketpair socketpair.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include	<netinet/in.h>	/* sockaddr_in{} and other Internet defns */
#include	<arpa/inet.h>	/* inet(3) functions */
#include	<errno.h>
#include	<fcntl.h>		/* for nonblocking */
#include	<sys/un.h>		/* for Unix domain sockets */
#include <unistd.h>   /* unlink */
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#define	BUFFSIZE	8192	/* buffer size for reads and writes */
/* define if struct msghdr contains the msg_control member */

#define HAVE_MSGHDR_MSG_CONTROL 1



ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
	struct msghdr	msg;
	struct iovec	iov[1];
	ssize_t			n;

#ifdef	HAVE_MSGHDR_MSG_CONTROL
	union {
	  struct cmsghdr	cm;
	  char				control[CMSG_SPACE(sizeof(int))];
	} control_un;
	struct cmsghdr	*cmptr;

	msg.msg_control = control_un.control;
	msg.msg_controllen = sizeof(control_un.control);
#else
	int				newfd;

	msg.msg_accrights = (caddr_t) &newfd;
	msg.msg_accrightslen = sizeof(int);
#endif

	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	iov[0].iov_base = ptr;
	iov[0].iov_len = nbytes;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	if ( (n = recvmsg(fd, &msg, 0)) <= 0)
		return(n);

#ifdef	HAVE_MSGHDR_MSG_CONTROL
	if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
	    cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
		if (cmptr->cmsg_level != SOL_SOCKET)
			err_quit("control level != SOL_SOCKET");
		if (cmptr->cmsg_type != SCM_RIGHTS)
			err_quit("control type != SCM_RIGHTS");
		*recvfd = *((int *) CMSG_DATA(cmptr));
	} else
		*recvfd = -1;		/* descriptor was not passed */
#else
/* *INDENT-OFF* */
	if (msg.msg_accrightslen == sizeof(int))
		*recvfd = newfd;
	else
		*recvfd = -1;		/* descriptor was not passed */
/* *INDENT-ON* */
#endif

	return(n);
}
/* end read_fd */

ssize_t Read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
	ssize_t		n;

	if ( (n = read_fd(fd, ptr, nbytes, recvfd)) < 0)
		printf("read_fd error");

	return(n);
}


int my_open(const char *pathname, int mode)
{
	int			fd, sockfd[2], status;
	pid_t		childpid;
	char		c, argsockfd[10], argmode[10];

	socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);

	if ( (childpid = fork()) == 0) /* child process */
	{		
		close(sockfd[0]);
		snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
		snprintf(argmode, sizeof(argmode), "%d", mode);
		execl("./openfile", "openfile", argsockfd, pathname, argmode,
			  (char *) NULL);
		printf("execl error");
	}

	/* parent process - wait for the child to terminate */
	close(sockfd[1]);			/* close the end we don't use */

	waitpid(childpid, &status, 0);
	if (WIFEXITED(status) == 0)
	{
		printf("child did not terminate");
	}
	if ( (status = WEXITSTATUS(status)) == 0)
	{
		Read_fd(sockfd[0], &c, 1, &fd);
	}
	else 
	{
		errno = status;		/* set errno value from child's status */
		fd = -1;
	}

	close(sockfd[0]);
	return(fd);
}




int main(int argc, char **argv)
{
	int		fd, n;
	char	buff[BUFFSIZE];

	if (argc != 2)
	{
		printf("usage: mycat <pathname>");
	}
	//O_RDONLY 只读取
	if ( (fd = my_open(argv[1], O_RDONLY)) < 0)
	{
		printf("cannot open %s", argv[1]);
	}

	while ( (n = read(fd, buff, BUFFSIZE)) > 0)
	{
		write(STDOUT_FILENO, buff, n);
	}

	//exit(0);
	return 0;
}

五 , socket本地通信

本地套接字使用 struct sockaddr_un ;的数据结构

bind的函数时候仍然需要强转

创建套接字需要使用 AF_UNIX ,AF_UNIX, AF_LOCAL

创建套接字的type可以选择TCP,也可以选择UDP,但是如果选择TCP,那么必须按照TCP的流程进行通信,如果选择UDP,必须按照UDP的流程进行通信。

1, client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netinet/udp.h>



#define SERV_NAME "sock.c"
#define SERV_NAMES "sock.s"

int main(int argc, char *argv[]) {

	int listenfd;
	struct sockaddr_un servaddr, cliaddr;
	socklen_t len = sizeof(cliaddr);
	char buf[256] = { 0 };
	int ret, i = 0;

	//创建socket
	listenfd = socket(AF_UNIX, SOCK_DGRAM, 0);

	//绑定ip
	unlink(SERV_NAME);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path, SERV_NAME);
	if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
		perror("bind error");
		return -1;
	} 
	bzero(&cliaddr, sizeof(cliaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path, SERV_NAMES);

	while (1) {
	
		//发送数据
		sprintf(buf, "陈丽 %d\n", i++);
		sendto(listenfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));

		//接收数据
		memset(buf, 0x00, sizeof(buf));
		int ret = recvfrom(listenfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
		if (ret > 0) {
			printf("client 接收消息:%s\n", buf);
		}
		sleep(1);

	}


	close(listenfd);
	return 0;
}

2,server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>


#define SERV_PORT 8888
#define SERV_NAME "sock.s"

int main(int argc, char *argv[]) 
{
	int listenfd;
	struct sockaddr_un servaddr, cliaddr;
	socklen_t len = sizeof(cliaddr);
	char buf[256] = { 0 };
	int i, ret;

	//创建socket 
	listenfd = socket(AF_UNIX, SOCK_DGRAM, 0);

	//绑定本地套接字
	unlink(SERV_NAME); //删除绑定套接字
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path, SERV_NAME);
	if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
		perror("bind error");
		return -1;
	}

	while (1) {

		//接收数据
		ret = recvfrom(listenfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
		if (ret > 0) {
			printf("server :%s\n", cliaddr.sun_path);
			sendto(listenfd, buf, strlen(buf), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
		}

	}

	close(listenfd);
	return 0;
}

六 , getsockname 函数

man 查看

SYNOPSIS
       #include <sys/socket.h>

       int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

DESCRIPTION
       getsockname()  returns  the  current address to which the socket sockfd is bound, in the buffer pointed to by addr.  The addrlen argument should be ini‐
       tialized to indicate the amount of space (in bytes) pointed to by addr.  On return it contains the actual size of the socket address.

       The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

ERRORS
       EBADF  The argument sockfd is not a valid descriptor.

       EFAULT The addr argument points to memory not in a valid part of the process address space.

       EINVAL addrlen is invalid (e.g., is negative).

       ENOBUFS
              Insufficient resources were available in the system to perform the operation.

       ENOTSOCK
              The file descriptor sockfd does not refer to a socket.

得到已经连接sockIP长度

//gcc -o getsocket getsocketname.c
//得到socket连接 IP地址长度
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include	<netinet/in.h>	/* sockaddr_in{} and other Internet defns */
#include	<arpa/inet.h>	/* inet(3) functions */
#include	<errno.h>
#include	<fcntl.h>		/* for nonblocking */
#include	<sys/un.h>		/* for Unix domain sockets */
#include <unistd.h>   /* unlink */

// cmd -> ./getsocket chenli 
// screen -> bound name = chenli, returned len = 9
int main(int argc, char *argv[])
{
	int					sockfd;
	socklen_t			len;
	struct sockaddr_un	addr1, addr2;

	if (argc != 2)
	{
		printf("usage: unixbind <pathname>");
	}

	sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);

	unlink(argv[1]);		/* OK if this fails */

	bzero(&addr1, sizeof(addr1));
	addr1.sun_family = AF_LOCAL;
	strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
	bind(sockfd, (struct sockaddr *) &addr1, SUN_LEN(&addr1));

	len = sizeof(addr2);
	getsockname(sockfd, (struct sockaddr *) &addr2, &len);
	printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
	
	//exit(0);
	return 0;
}


结语