linux网络编程(十一)UNIX域套接字与数据链路层
- UNIX 域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。
- UNIX域套接字可以在同一台主机上各进程之间传递描述符。
- UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述。

- UNIX域地址结构成员变量sun_family的值是AF_UNIX或者AF_LOCAL。
- sun_path是一个路径名,此路径名的属性为0777,可以进行读写等操作。
- 使用函数bind()进行套接字和地址的绑定的时候,地址结构中的路径名和路径名所表示的文件的默认访问权限为 0777,即用户、 用户所属的组和其他组的用户都能读、 写和执行。
- 结构sum_path中的路径名必须是一个绝对路径,不能是相对路径。
- 函数 connect()使用的路径名必须是一个绑定在某个己打开的UNIX域套接字上的路径名,而且套接字的类型也必须一致。下列情况将出错:
- 该路径名存在但不是一个套接字;
- 路径名存在且是一个套接口,但没有与该路径名相关联的打开的描述字;
- 路径名存在且是一个打开的套接字,但类型不符。
- 用函数 connect() 连接UNIX域套接字时的权限检查和用函数open()以只写方式访问路径名完全相同。
- UNIX域字节流套接字和TCP套接字类似:它们都为进程提供一个没有记录边界的字节流接口。
- 如果UNIX域字节流套接字的 connect() 函数发现监听套接字的队列己满,会立刻返回一个ECONNREFUSED错误。这和TCP有所不同:如果监听套接字的队列己满,它将忽略到来的SYN,TCP连接的发起方会接着发送几次SYN重试。
- UNIX域数据报套接字和UDP套接字类似: 它们都提供一个保留记录边界的不可靠的数据服务。
- 与UDP套接字不同的是,在未绑定的UNIX域套接字上发送数据报不会给它捆绑一个路径名。这意味着,数据报发送者除非绑定一个路径名,否则接收者无法发
- 回应答数据报。 同样,与TCP和UDP不同的是,给UNIX域数据报套接字调用 connect() 不会捆绑一个路径名。
- 创建一个字节流或者数据报的UNIX域套接字。
-
- 如果目标是fork()一个子进程,让子进程打开描述符并将它返回给父进程,那么父进程可以用socketpair()创建一个流管道,用它来传递描述字。
- 如果进程之间没有亲缘关系,那么服务器必须创建一个UNIX域字节流套接字,绑定一个路径名,让客户连接到这个套接字。 然后客户端可以向服务器发送一个请求以打开某个描述字,服务器将描述符通过UNIX域套接字传回。在客户端和服务器之间也可以使用UNIX数据报套接字,但这样做没有什么好处,而且数据 报存在丢失的可能性。
- 进程可以用任何返回描述符的UNIX函数打开,例如函数open()、pipe()、mkfifo()、 socket()或者accept()。可以在进程间传递任何类型的描述符。
- 发送进程建立一个 msghdr结构,其中包含要传递的描述符。在POSIX中说明该描述符作为辅助数据发送,但老的实现使用msg_accright成员。发送进程调用sendmsg()通过第一步得到的UNIX域套接字发出套接字。这时这个描述符是在飞行中的。即在发送进程调用sendmsg()之后、 在接受进程调用recvmsg()之前将描述符关闭,它仍会为接收进程保持打开状态。描述符的发送导致它的访问统计数加1。
- 接收进程调用recvmsg()在UNIX域套接字上接收套接字。通常接收进程收到的描述符的编号和发送进程中的描述符的编号不同,但这没有问题。传递描述符不是传递描述符的编号,而是在接收进程中建立一个新的描述符,指向内核的文件表中与发送进程发送的描述符相同的项。
socketpair() 函数的返回值为0时表示调用成功,为-1时表示发生了错误,错误值在变量errno中,errno的含义如 表11.3 所示。

- 取出标志位。
- 目标标志位=取出的标志位|设置的标志位。
- 写入目标标志位。
以太网头部结构定义为如下形式:

套接字文件描述符建立后,就可以从此描述符中读取数据,数据的格式为上述的以太网数据,即以太网帧。套接口建立以后,就可以从中循环读取捕获的链路层以太网帧。 要建立一个大小为 ETH_FRAME_LEN 的缓冲区,并将以太网的头部指向此缓冲区,接收数据以后,缓冲区 ef 与以太网头部的对应关系如图 11.11 所示。

因此,要获得以太网帧的目的 MAC 地址、 源 MAC 地址和协议的类型, 可以通过p_ethhdr->h_dest、p_ethhdr->h_source 和p_ethhdr->h_proto 获得。
IP头部的数据结构定义在头文件<netinet/ip.h>中,代码如下:

若捕获的以太帧中h_proto的取值为0x0800,将类型为iphdr的结构指针指向帧头后面载荷数据的起始位置,则可以得到IP数据包的报头部分。通过saddr和daddr可以得到 IP 报文的源IP地址和目的IP地址。
对应的数据结构在头文件<netinet/tcp.h>中定义,代码如下:


对于 TCP 协议,其 IP 头部的 protocol 的值应该为 6,通过计算 IP 头部的长度可以得到 TCP 头部的地址,即 TCP 的头部为 IP 头部偏移 ihl*4。 TCP 的源端口和目的端口可以通过成员 source 和 dest 来获得。
UDP 的头部数据结构在文件<netinet/udp.h>中定义,代码如下:

对于UDP协议,其IP头部的protocol的值为17,通过计算IP头部的长度可以得到 UDP头部的地址,即UDP的头部为IP头部偏移ihl*4。UDP的源端口和目的端口可以通过成员 source 和 dest 来获得。头部数据结构的布局如图 11.15 所示:

## 协议名称数据表

浙公网安备 33010602011771号