本文转载自:http://blog.csdn.net/roland_sun/article/details/50266565

版权声明:本文为博主原创文章,未经博主允许不得转载。

Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种。但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址。如果不同主机上的两个进程进行通信,当然这样做没什么问题。但是,如果只需要在一台机器上的两个不同进程间通信,还要用到IP地址就有点大材小用了。

其实很多人并不一定知道,对于套接字来说,还存在一种叫做Unix域套接字的类别,专门用来解决这个问题。其API的掉用方法基本上和普通TCP/IP的套接字一样,只是有些许差别。

因此,再正式介绍之前,先来复习一下套接字从创建,到传输数据,再到最后关闭的所有过程:

上边的图代表面向流的套接字,对于TCP/IP套接字来说,代表TCP协议;下边的图代表面向数据包的套接字,对于TCP/IP套接字来说,代表UDP协议。接下来,我们将特别针对普通TCP/IP套接字和Unix域套接字的不同,对这些API做一些解释。

1)socket()

最开始肯定还是要创建一个套接字:

  1. int socket (int domain, int type, int protocol);

API定义是一样的,不过这里的第一个参数,也就是域一定要设置成AF_UNIX或AF_LOCAL,而不是普通TCP/IP套接字的AF_INET。第二个参数表示套接字的类型,分为流套接字(SOCK_STREAM)和数据包套接字(SOCK_DGRAM)。不同于普通的AF_INET的Socket,由于都是在本机通过内核通信,所以SOCK_STREAM和SOCK_DGRAM都是可靠的,不会丢包也不会出现发送包的次序和接收包的次序不一致的问题。它们的区别仅仅是,SOCK_STREAM无论发送多大的数据都不会被截断,而对于SOCK_DGRAM来说,如果发送的数据超过了一个报文的最大长度,则数据会被截断。而最后一个参数,表示协议,对于Unix域套接字来说,其一定是被设置成0。因此,一般通过下面的方式创建一个Unix域套接字:

  1. int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  // 流式Unix域套接字
  2. int sockfd = socket(AF_UNIX,SOCK_DGRAM, 0);    // 数据包式套接字

2)bind()

对于流式套接字的服务器端来说,在用socket()函数获得了新创建套接字的文件描述符之后,还要将其绑定到一个地址上去:

  1. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

在Unix域套接字中,套接字的地址是以sockaddr_un结构体来表示的,其结构如下:

  1. struct sockaddr_un {
  2. sa_family_t sun_family;
  3. char sun_path[108];
  4. }

结构体中的第一个字段必须要设置成“AF_UNIX”。而第二个字段,表示的是一个路径名。因此,要将一个Unix域套接字绑定到一个本地地址上,需要创建并初始化一个sockaddr_un结构体,并将指向这个结构体的指针作为addr参数(需要类型转换)传入bind()函数,并将addrlen参数设置成这个结构体的实际大小。

这里还要特别提一下这个路径名,其实还要分为两种,一种是普通路径名,另一种是抽象路径名。

首先来说说普通路径名,这个很好理解,就是一个基本的linux文件路径,其必须要以NULL('\0')结尾。在绑定一个Unix域套接字时,会在文件系统中的相应位置上创建一个文件,且这个文件的类型被标记为“Socket”,因此这个文件无法用open()函数打开。当不再需要这个Unix域套接字时,可以使用remove()函数或者unlink()函数将这个对应的文件删除。如果在文件系统中,已经有了一个文件和指定的路径名相同,则绑定会失败(返回错误EADDRINUSE)。所以,一个套接字只能绑定到一个路径上,同样的,一个路径也只能被一个套接字绑定。

接下来看看什么叫抽象路径名,这其实是Linux特有的一个特性,它允许将一个Unix域套接字绑定到一个名字上,且不会在文件系统中创建这个名字的文件。如果要创建一个抽象名字空间的绑定,必须要将sun_path字段的第一个字节设置成NULL('\0'),而且和普通的文件系统名字空间不同的是,系统会用sun_path除第一个字节之后余下的所有字节当做抽象名字。也就是说在解析抽象路径名时需要用到sun_path字段当中所有的字节,而不是像解析普通路径名一样,解析到第一个NULL就可以停止了。因为不会再在文件系统中创建文件了,所以对于抽象路径名来说,就不需要担心与文件系统中已存在的文件产生名字冲突的问题了,也不需要在使用完套接字之后删除附带产生的这个文件了,当套接字被关闭之后会自动删除这个抽象名。

最后再提一下权限的问题,因为要在文件系统中创建相应的文件,对于普通路径名来说,掉用bind()函数的进程必须要有路径名中目录部分的可写和可访问权限。还有,在默认情况下,在调用bind()函数时,会给所有者、组和其他用户赋予所有的权限(即777),如果想改变这个行为,可以在bind()之后再修改创建的文件的权限和属性。

3)listen()

对于流式套接字的服务器端来说,listen()函数在TCP/IP套接字和Unix域套接字中调用方式是一样的,没有区别:

  1. int listen(int sockfd, int backlog);

4)accept()

对于流式套接字的服务器端来说,在调用bind()绑定完本地路径之后,还需要接收客户端的请求,这是通过调用accept()函数来实现的:

  1. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

与普通的TCP/IP套接字不同,Unix域套接字不存在客户端地址的问题(都在一台机器上),因此这里的addr和addrlen参数都要设置成NULL。在这里,不同进程像这个服务器端进程发送的流数据是在内核里面区分的,并绑定到了accept()创建的套接字中了。而数据包套接字就没有这种对应关系,所以还是要在代码中区分出来,后面会介绍。

5)connect()

对于流式套接字的客户端来说,在用socket()函数获得了新创建套接字的文件描述符之后,就可以调用connect()函数连接服务器端了:

  1. int connect(int sockfd, struct sockaddr *addr,int addrlen);

这个函数在TCP/IP套接字中和在Unix域套接字中调用方式基本相同,只不过和bind()函数一样,地址addr必须是以sockaddr_un结构体来表示。

6)read()和write()

对于流式套接字的服务器端来说,read()和write函数在TCP/IP套接字和Unix域套接字中调用方式是一样的,没有区别:

  1. ssize_t read(int sockfd, void *buf, size_t length);
  2. ssize_t write(int sockfd, const void *buf, size_t length);

7)recvfrom()和sendto()

对于数据包事套接字来说,在服务器端recvfrom()用来接收客户端发送的请求,而在客户端这个函数用来接收服务器端发送过来的响应:

  1. int recvfrom(int sockfd, void *buf, int length, unsigned int flags, struct sockaddr *addr, int *addrlen);

同时,在客户端sendto()用来向服务器端发送请求数据,而服务器端用这个函数来向客户端发送响应数据:

  1. int sendto (int sockfd, const void *buf, int length, unsigned int flags, const struct sockaddr *addr, int addrlen);

前面也提到了,对于数据包套接字来说,服务器端在发送响应数据时是需要知道客户端到底是哪个的,从而后面可以将相应的响应数据发送给正确的客户端。而客户端也需要知道到底是向哪个服务器端发送数据,或者说接收到的响应数据到底来自哪个服务器端(当然,如果只保证和一个服务器端通信就没有这个问题)。

但是,按照普通的包套接字创建和连接的流程,只是在服务器端掉用bind()函数绑定了一个地址,而客户端并没有地址。这在流式套接字中没有问题,内核已经在服务器端调用accept()函数接收一个客户端连接时创建了一个新的套接字,从而将一一对应关系绑定到了这个新的套接字上了。所以,对于包套接字来说,在客户端还需要再掉用bind()函数绑定一次,人为的创建一个客户端地址,且这个客户端路径名地址显然不能和服务器端的路径名相同。

剩下的就都和普通的TCP/IP套接字相同了,只不过地址addr必须是以sockaddr_un结构体来表示罢了。

Unix域套接字(Unix Domain Socket)介绍【转】的更多相关文章

  1. UNIX 域套接字——UNIX domain socket

    /*********************程序相关信息********************* * 程序编号:015 * 程序编写起始日期:2013.11.30 * 程序编写完成日期:2013.1 ...

  2. UNIX域套接字——UNIX domain socket(DGRAM)

    #define UNIX_PATH_MAX 108 #include <sys/types.h> #include <sys/socket.h> #include <sy ...

  3. Unix域套接字简介

    在Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种.但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址.如果不同主机上的两个进程进行通信,当然这样做没什么问 ...

  4. 《网络编程》Unix 域套接字

    概述 Unix 域套接字是一种client和server在单主机上的 IPC 方法.Unix 域套接字不运行协议处理,不须要加入或删除网络报头,无需验证和,不产生顺序号,无需发送确认报文,比因特网域套 ...

  5. UNIX域套接字(unix domain)

    UNIX域套接字用于在同一台机器上运行的进程之间的通信. UNIX域套接字提供流和数据报两种接口. 说明:UNIX域套接字比因特网套接字效率更高.它仅赋值数据:不进行协议处理,如添加或删除网络报头.计 ...

  6. 高级进程间通信之UNIX域套接字

    UNIX域套接字用于在同一台机器上运行的进程之间的通信.虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高.UNIX域套接字仅仅复制数据:它们并不执行协议处理,不需要添加或删除网络报头,无 ...

  7. UNIX网络编程——通过UNIX域套接字传递描述符和 sendmsg/recvmsg 函数

    在前面我们介绍了UNIX域套接字编程,更重要的一点是UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #in ...

  8. UNIX网络编程——UNIX域套接字编程和socketpair 函数

    一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...

  9. 通过UNIX域套接字传递描述符和 sendmsg/recvmsg 函数

    在前面我们介绍了UNIX域套接字编程,更重要的一点是UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h>  #i ...

随机推荐

  1. dutacm.club_1089_A Water Problem_(dp)

    题意:要获得刚好后n个'k'的字符串,有两种操作,1.花费x秒,增加或删除1个'k'; 2.花费y秒,使个数翻倍.问最少需要多少时间获得这个字符串. 思路:i为偶数个'k',dp[i]=min(dp[ ...

  2. MySQL的基本概念与操作

    数据库的基本概念什么是数据库?用于存储和管理数据的仓库.数据库的特点:持久化存储数据的.其实数据库就是一个文件系统方便存储和管理数据使用了统一的方式操作数据库 – SQL数据库的分类:数据库根据存储采 ...

  3. 安卓app测试之Monkey测试

    一.Monkey特点 1.运行时机:一般是产品稳定后 首轮功能测试完成的夜间进行 2.需要知道packageName 3.目的:主要测试产品是否存在崩溃问题和ANR问题. 二.获取包名的两个方法 首先 ...

  4. C++ 11常见功能介绍:auto,decltype,nullptr,for,lambda

    什么是C++11 C++11是曾经被叫做C++0x,是对目前C++语言的扩展和修正,C++11不仅包含核心语言的新机能,而且扩展了C++的标准程序库(STL),并入了大部分的C++ Technical ...

  5. Java排序算法全

    目录 Java排序算法代码 零. 排序基类 一. 选择排序 二. 插入排序 三. 希尔排序 四. 归并排序 1. 自顶向下 2. 自底向上 五. 快速排序 1. 基本版 2. 双路切分版 3. 三路切 ...

  6. TWaver3D特效系列之环境映射

    随着TWaver3D的快速发展,越来越多的各种功能都在不断加强,包括性能的极大提升(可以参考这里),3D编辑器的易用性和功能持续增强(欢迎大家申请试用),各种特效的增加,特效是本文的主角. 对于UI技 ...

  7. dock helloworld

    Docker Hello World Docker 允许你在容器内运行应用程序, 使用 docker run 命令来在容器内运行一个应用程序. 输出Hello world runoob@runoob: ...

  8. Turtle-可视化界面画圣诞树

    圣诞节(Christmas)又称耶诞节.耶稣诞辰,译名为“基督弥撒”,是西方传统节日,起源于基督教,在每年公历12月25日.弥撒是教会的一种礼拜仪式.圣诞节是一个宗教节,因为把它当作耶稣的诞辰来庆祝, ...

  9. 分金币 (UVA 11300)

    http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=33899 思路:推公式,发现可以转化为求给定n个数,求到所有点距离之和最小的点 ...

  10. 吧,其实spring自带的BeanUtils就有这样的功能,引入spring-beans和spring-core之后,就有BeanUtils.copyProperties(a, b);可以实现两个javabean之间的相互拷贝,自己写的就当是研究咯---https://www.cnblogs.com/NieXiaoHui/p/7150928.html

    吧,其实spring自带的BeanUtils就有这样的功能,引入spring-beans和spring-core之后,就有BeanUtils.copyProperties(a, b);可以实现两个ja ...