UNP学习笔记(第十五章 UNIX域协议)

UNIX域协议是在单个主机上执行客户/服务器通信的一种方法

使用UNIX域套接字有以下3个理由:

1.UNIX域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍

2.UNIX域套接字可用于在同一个主机上的不同进程之间传递描述符

3.UNIX域套接字较新的实现把客户的凭证提供给服务器,从而能够提供额外的安全检查措施

UNIX域中用于标识客户和服务器的协议地址是普通文件系统的路径名。这些路径名不是普通的UNIX文件:

除非他们和UNIX域套接字关联起来,否则无法读写这些文件。

可以查看之前apue的学习笔记  http://www.cnblogs.com/runnyu/p/4650843.html

 

UNIX域套接字的地址结构

struct sockaddr_un 
{
    sa_family_t sun_family;   /* AF_LOCAL */
    char sun_path[104];   /* pathname */
};

实现提供了SUN_LEN宏以一个指向sockaddr_un结构的指针作为参数并返回该结构的长度

UNIX域套接字的bind调用

下面程序创建一个UNIX域套接字,往其上bind一个路径名,再调用getsockname输出这个绑定的路径名

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     socklen_t            len;
 8     struct sockaddr_un    addr1, addr2;
 9 
10     if (argc != 2)
11         err_quit("usage: unixbind <pathname>");
12 
13     sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
14 
15     unlink(argv[1]);        /* OK if this fails */
16 
17     bzero(&addr1, sizeof(addr1));
18     addr1.sun_family = AF_LOCAL;
19     strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
20     Bind(sockfd, (SA *) &addr1, SUN_LEN(&addr1));
21 
22     len = sizeof(addr2);
23     Getsockname(sockfd, (SA *) &addr2, &len);
24     printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
25     
26     exit(0);
27 }
View Code

先调用unlink删除这个路径名,以防bind失败

 

 

 

socketpair函数

socketpair函数创建两个随后连接起来的套接字。本函数仅适用于UNIX套接字

#include <sys/socket.h>
int socketpair(int domain,int type,int protocol,int sockfd[2]);

family参数必须为AF_LOCAL,protocol参数必须为0,type参数既可以是SOCK_STREAM,也可以是SOCK_DGRAM。新创建的两个套接字描述符作为sockfd[0]和sockfd[1]返回

 

 

 

UNIX域字节流客户/服务器程序

下面将第五章的TCP回射客户/服务器程序重新编写成使用UNIX域套接字

服务器程序。unp.h定义的UNIXSTR_PATH常值为/tmp/unix.str

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    listenfd, connfd;
 7     pid_t                childpid;
 8     socklen_t            clilen;
 9     struct sockaddr_un    cliaddr, servaddr;
10     void                sig_chld(int);
11 
12     listenfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
13 
14     unlink(UNIXSTR_PATH);
15     bzero(&servaddr, sizeof(servaddr));
16     servaddr.sun_family = AF_LOCAL;
17     strcpy(servaddr.sun_path, UNIXSTR_PATH);
18 
19     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
20 
21     Listen(listenfd, LISTENQ);
22 
23     Signal(SIGCHLD, sig_chld);
24 
25     for ( ; ; ) {
26         clilen = sizeof(cliaddr);
27         if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
28             if (errno == EINTR)
29                 continue;        /* back to for() */
30             else
31                 err_sys("accept error");
32         }
33 
34         if ( (childpid = Fork()) == 0) {    /* child process */
35             Close(listenfd);    /* close listening socket */
36             str_echo(connfd);    /* process request */
37             exit(0);
38         }
39         Close(connfd);            /* parent closes connected socket */
40     }
41 }
View Code

客户程序

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_un    servaddr;
 8 
 9     sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
10 
11     bzero(&servaddr, sizeof(servaddr));
12     servaddr.sun_family = AF_LOCAL;
13     strcpy(servaddr.sun_path, UNIXSTR_PATH);
14 
15     Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
16 
17     str_cli(stdin, sockfd);        /* do it all */
18 
19     exit(0);
20 }
View Code

 

 

 

UNIX域数据报客户/服务器程序

下面将第八章UDP回射客户/服务器程序重新编写成使用UNIX域数据报套接字。

服务器程序。unp.h定义的UNIXDG_PATH常值为/tmp/unix.dg

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_un    servaddr, cliaddr;
 8 
 9     sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
10 
11     unlink(UNIXDG_PATH);
12     bzero(&servaddr, sizeof(servaddr));
13     servaddr.sun_family = AF_LOCAL;
14     strcpy(servaddr.sun_path, UNIXDG_PATH);
15 
16     Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
17 
18     dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
19 }
View Code

客户程序

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_un    cliaddr, servaddr;
 8 
 9     sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
10 
11     bzero(&cliaddr, sizeof(cliaddr));        /* bind an address for us */
12     cliaddr.sun_family = AF_LOCAL;
13     strcpy(cliaddr.sun_path, tmpnam(NULL));
14 
15     Bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
16 
17     bzero(&servaddr, sizeof(servaddr));    /* fill in server's address */
18     servaddr.sun_family = AF_LOCAL;
19     strcpy(servaddr.sun_path, UNIXDG_PATH);
20 
21     dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
22 
23     exit(0);
24 }
View Code

与UDP客户不同的是:使用UNIX域数据报协议时,我们必须显式bind一个路径名到我们的套接字,这样服务器才会有能回射应答的路径名。

 

 

 

描述符传递

下面两种方法可以把一个描述符从一个进程传递到另一个进程

1.fork调用返回之后,子进程共享父进程的所有打开的描述符

2.exec调用执行之后,所有描述符通常保持打开状态不变

第一种方法中:进程先打开一个描述符,再调用fork,然后父进程关闭这个描述符,子进程则处理这个描述符。

                    这样一个打开的描述符就从父进程传递到子进程

当前UNIX系统提供了用于从一个进程向任一其他进程传递任一打开的描述符的方法,这两个进程无需存在亲缘关系

这种技术要求首先在这两个进程之间创建一个UNIX域套接字,然后使用sendmsg跨这个套接字发送一个特殊消息。

这个消息由内核来专门处理,会把打开的描述符从发送进程传递到接收进程。

在两个进程之间传递描述符涉及的步骤如下:

1.创建一个字节流或数据报流的UNIX域套接字

2.发送进程通过调用返回描述符的任一UNIX函数打开一个描述符,这些函数的例子有:open、pipe、mkfifo、socket和accept

3.发送进程创建一个msghdr结构,其中含有待传递的描述符

4.接收进程调用recvmsg在来自步骤1的UNIX域套接字上接收这个描述符

描述符传递的例子

mycat程序:通过命令行参数取得一个路径名,打开这个文件,再把文件的内容复制到标准输出。

该程序调用我们名为my_open的函数:my_open创建一个流管道,并调用fork和exec启动执行一个程序,期待输出的文件由这个程序打开。该程序随后必须把打开的描述符通过流管道传递给父进程。

下面展示上述步骤:

1.通过调用socketpair创建一个流管道后的mycat进程。

2.mycat进程接着调用fork,子进程再调用exec执行openfile程序。父进程关闭[1]描述符,子进程关闭[0]描述符

 

3.父进程给openfile程序传递三条信息(调用exec时进行传递):待打开文件的路径名、打开方式、流管道本进程端对应的描述符号

通过执行另一个程序打开文件的优势在于:另一个程序可以是一个setuid到root的程序,能够打开我们通常没有打开权限的文件。

下面是mycat程序(使用别的程序打开,然后把描述符传递过来进行读取该文件)

 1 #include    "unp.h"
 2 
 3 int        my_open(const char *, int);
 4 
 5 int
 6 main(int argc, char **argv)
 7 {
 8     int        fd, n;
 9     char    buff[BUFFSIZE];
10 
11     if (argc != 2)
12         err_quit("usage: mycat <pathname>");
13 
14     if ( (fd = my_open(argv[1], O_RDONLY)) < 0)
15         err_sys("cannot open %s", argv[1]);
16 
17     while ( (n = Read(fd, buff, BUFFSIZE)) > 0)
18         Write(STDOUT_FILENO, buff, n);
19 
20     exit(0);
21 }
View Code

下面是my_open函数

 1 #include    "unp.h"
 2 
 3 int
 4 my_open(const char *pathname, int mode)
 5 {
 6     int            fd, sockfd[2], status;
 7     pid_t        childpid;
 8     char        c, argsockfd[10], argmode[10];
 9 
10     Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
11 
12     if ( (childpid = Fork()) == 0) {        /* child process */
13         Close(sockfd[0]);
14         snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
15         snprintf(argmode, sizeof(argmode), "%d", mode);
16         execl("./openfile", "openfile", argsockfd, pathname, argmode,
17               (char *) NULL);
18         err_sys("execl error");
19     }
20 
21     /* parent process - wait for the child to terminate */
22     Close(sockfd[1]);            /* close the end we don't use */
23 
24     Waitpid(childpid, &status, 0);
25     if (WIFEXITED(status) == 0)
26         err_quit("child did not terminate");
27     if ( (status = WEXITSTATUS(status)) == 0)
28         Read_fd(sockfd[0], &c, 1, &fd);
29     else {
30         errno = status;        /* set errno value from child's status */
31         fd = -1;
32     }
33 
34     Close(sockfd[0]);
35     return(fd);
36 }
View Code

my_open函数返回的描述符是调用read_fd返回的描述符(调用recvmsg接收一个描述符)

 1 /* include read_fd */
 2 #include    "unp.h"
 3 
 4 ssize_t
 5 read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
 6 {
 7     struct msghdr    msg;
 8     struct iovec    iov[1];
 9     ssize_t            n;
10 
11 #ifdef    HAVE_MSGHDR_MSG_CONTROL
12     union {
13       struct cmsghdr    cm;
14       char                control[CMSG_SPACE(sizeof(int))];
15     } control_un;
16     struct cmsghdr    *cmptr;
17 
18     msg.msg_control = control_un.control;
19     msg.msg_controllen = sizeof(control_un.control);
20 #else
21     int                newfd;
22 
23     msg.msg_accrights = (caddr_t) &newfd;
24     msg.msg_accrightslen = sizeof(int);
25 #endif
26 
27     msg.msg_name = NULL;
28     msg.msg_namelen = 0;
29 
30     iov[0].iov_base = ptr;
31     iov[0].iov_len = nbytes;
32     msg.msg_iov = iov;
33     msg.msg_iovlen = 1;
34 
35     if ( (n = recvmsg(fd, &msg, 0)) <= 0)
36         return(n);
37 
38 #ifdef    HAVE_MSGHDR_MSG_CONTROL
39     if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
40         cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
41         if (cmptr->cmsg_level != SOL_SOCKET)
42             err_quit("control level != SOL_SOCKET");
43         if (cmptr->cmsg_type != SCM_RIGHTS)
44             err_quit("control type != SCM_RIGHTS");
45         *recvfd = *((int *) CMSG_DATA(cmptr));
46     } else
47         *recvfd = -1;        /* descriptor was not passed */
48 #else
49 /* *INDENT-OFF* */
50     if (msg.msg_accrightslen == sizeof(int))
51         *recvfd = newfd;
52     else
53         *recvfd = -1;        /* descriptor was not passed */
54 /* *INDENT-ON* */
55 #endif
56 
57     return(n);
58 }
59 /* end read_fd */
60 
61 ssize_t
62 Read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
63 {
64     ssize_t        n;
65 
66     if ( (n = read_fd(fd, ptr, nbytes, recvfd)) < 0)
67         err_sys("read_fd error");
68 
69     return(n);
70 }
View Code

下面给出openfile程序:打开一个文件并传递回其描述符

 1 #include    "unp.h"
 2 
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int        fd;
 7 
 8     if (argc != 4)
 9         err_quit("openfile <sockfd#> <filename> <mode>");
10 
11     if ( (fd = open(argv[2], atoi(argv[3]))) < 0)
12         exit( (errno > 0) ? errno : 255 );
13 
14     if (write_fd(atoi(argv[1]), "", 1, fd) < 0)
15         exit( (errno > 0) ? errno : 255 );
16 
17     exit(0);
18 }
View Code

它调用write_fd调用sendmsg跨一个UNIX域套接字发送一个描述符

 1 /* include write_fd */
 2 #include    "unp.h"
 3 
 4 ssize_t
 5 write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
 6 {
 7     struct msghdr    msg;
 8     struct iovec    iov[1];
 9 
10 #ifdef    HAVE_MSGHDR_MSG_CONTROL
11     union {
12       struct cmsghdr    cm;
13       char                control[CMSG_SPACE(sizeof(int))];
14     } control_un;
15     struct cmsghdr    *cmptr;
16 
17     msg.msg_control = control_un.control;
18     msg.msg_controllen = sizeof(control_un.control);
19 
20     cmptr = CMSG_FIRSTHDR(&msg);
21     cmptr->cmsg_len = CMSG_LEN(sizeof(int));
22     cmptr->cmsg_level = SOL_SOCKET;
23     cmptr->cmsg_type = SCM_RIGHTS;
24     *((int *) CMSG_DATA(cmptr)) = sendfd;
25 #else
26     msg.msg_accrights = (caddr_t) &sendfd;
27     msg.msg_accrightslen = sizeof(int);
28 #endif
29 
30     msg.msg_name = NULL;
31     msg.msg_namelen = 0;
32 
33     iov[0].iov_base = ptr;
34     iov[0].iov_len = nbytes;
35     msg.msg_iov = iov;
36     msg.msg_iovlen = 1;
37 
38     return(sendmsg(fd, &msg, 0));
39 }
40 /* end write_fd */
View Code

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在现有省、市港口信息化系统进行有效整合基础上,借鉴新 一代的感知-传输-应用技术体系,实现对码头、船舶、货物、重 大危险源、危险货物装卸过程、航管航运等管理要素的全面感知、 有效传输和按需定制服务,为行政管理人员和相关单位及人员提 供高效的管理辅助,并为公众提供便捷、实时的水运信息服务。 建立信息整合、交换和共享机制,建立健全信息化管理支撑 体系,以及相关标准规范和安全保障体系;按照“绿色循环低碳” 交通的要求,搭建高效、弹性、高可扩展性的基于虚拟技术的信 息基础设施,支撑信息平台低成本运行,实现电子政务建设和服务模式的转变。 实现以感知港口、感知船舶、感知货物为手段,以港航智能 分析、科学决策、高效服务为目的和核心理念,构建“智慧港口”的发展体系。 结合“智慧港口”相关业务工作特点及信息化现状的实际情况,本项目具体建设目标为: 一张图(即GIS 地理信息服务平台) 在建设岸线、港口、港区、码头、泊位等港口主要基础资源图层上,建设GIS 地理信息服务平台,在此基础上依次接入和叠加规划建设、经营、安全、航管等相关业务应用专题数据,并叠 加动态数据,如 AIS/GPS/移动平台数据,逐步建成航运管理处 "一张图"。系统支持扩展框架,方便未来更多应用资源的逐步整合。 现场执法监管系统 基于港口(航管)执法基地建设规划,依托统一的执法区 管理和数字化监控平台,通过加强对辖区内的监控,结合移动平 台,形成完整的多维路径和信息追踪,真正做到问题能发现、事态能控制、突发问题能解决。 运行监测和辅助决策系统 对区港口与航运业务日常所需填报及监测的数据经过科 学归纳及分析,采用统一平台,消除重复的填报数据,进行企业 输入和自动录入,并进行系统智能判断,避免填入错误的数据, 输入的数据经过智能组合,自动生成各业务部门所需的数据报 表,包括字段、格式,都可以根据需要进行定制,同时满足扩展 性需要,当有新的业务监测数据表需要产生时,系统将分析新的 需求,将所需字段融合进入日常监测和决策辅助平台的统一平台中,并生成新的所需业务数据监测及决策表。 综合指挥调度系统 建设以港航应急指挥中心为枢纽,以各级管理部门和经营港 口企业为节点,快速调度、信息共享的通信网络,满足应急处置中所需要的信息采集、指挥调度和过程监控等通信保障任务。 设计思路 根据项目的建设目标和“智慧港口”信息化平台的总体框架、 设计思路、建设内容及保障措施,围绕业务协同、信息共享,充 分考虑各航运(港政)管理处内部管理的需求,平台采用“全面 整合、重点补充、突出共享、逐步完善”策略,加强重点区或 运输通道交通基础设施、运载装备、运行环境的监测监控,完善 运行协调、应急处置通信手段,促进跨区、跨部门信息共享和业务协同。 以“统筹协调、综合监管”为目标,以提供综合、动态、实 时、准确、实用的安全畅通和应急数据共享为核心,围绕“保畅通、抓安全、促应急"等实际需求来建设智慧港口信息化平台。 系统充分整合和利用航运管理处现有相关信息资源,以地理 信息技术、网络视频技术、互联网技术、移动通信技术、云计算 技术为支撑,结合航运管理处专网与行业数据交换平台,构建航 运管理处与各部门之间智慧、畅通、安全、高效、绿色低碳的智 慧港口信息化平台。 系统充分考虑航运管理处安全法规及安全职责今后的变化 与发展趋势,应用目前主流的、成熟的应用技术,内联外引,优势互补,使系统建设具备良好的开放性、扩展性、可维护性。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领,每个领都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值