Chapter 2. Socket API 概览

Chapter 2. Socket API 概览


早先的章节专注于网络应用通讯 设计领域的模式化,提供了很多解决内在固有的复杂度的指导。本章开始深入 解决方法领域,从最常见的进程间通讯 (IPC) 机制开始: socket。描述 Socket API 和它在 TCP/IP 中的使用,随后分析当开发者使用原生 OS APIs 进行网络应用编程时会发生的常见意外复杂度。

2.1 操作系统 IPC 机制的概览

网络应用需要进程间通讯机制进行客户端和服务器之间的信息交换。IPC 机制由操作系统提供,通常被分为两类:

  • Local IPC 某些 IPC 机制,例如共享内存,管道,UNIX-domain sockets,doors,或信号,仅用于在相同计算机上的实体间进行通讯。
  • Remote IPC 其他 IPC 机制,例如 Internet-domain sockets,X.25 circuits,和 Win32 命名管道,支持跨网络分布的实体间的通讯。

虽然网络应用主要关注远程 IPC 机制,我们所展现的模式和 ACE 封装外观也应用于大部分的本地 IPC 机制。

2.2 Socket API

BSD UNIX 开发的 Socket API 提供对 TCP/IP 协议套件的应用层接口。这一 API 已经被移植到了大部分的操作系统中。它现在是在 TCP/IP 上进行进程间通讯编程的事实上的标准。

应用可以使用 Socket API 中的 C 函数来创建或管理本地通讯 endpoints,被称为 sockets。每个 socket 通过一个 handle 访问,在 UNIX 文章中它也被引用为描述符。Socket handle 标识一个由 OS 管理的通讯 endpoint,同时保护应用远离底层 OS 内核实现细节的不同,以及对其的依赖,例如:

  • 在 UNIX 中,socket handles 和其他 I/O handles,例如文件,pipe,和终端设备 handles,在大多数操作中可以交换使用。
  • 在 Microsoft Windows 中,socket handles 与 I/O handles 对于大部分操作来说不能互换使用,虽然它们服务于类似的目的。

每个 socket 都能被绑定到本地和远程地址。这些地址定义两个和多个通过 socket 通讯的 peer 之间的关联。

Socket API 包含大约 24 个系统函数,能被分类为下列 5 类:

  1. Local context management. Socket API 提供函数以管理本地上下文信息,这些信息通常存储于 OS 内核或系统库中。
函数描述
Socket()分配 socket handle 并向调用者返回的工厂函数
bind()关联 socket handle 到本地或远程地址
getsockname()返回 socket 绑定的本地地址
getpeername()返回 socket 绑定的远程地址
close()解分配一个 socket handle,使其能够被重用
  1. Connection establishment and connection termination. Socket API 提供函数用于建立和终止连接:
函数描述
Connect()在 socket handle 上主动建立连接
listen()表示愿意被动地监听到来的客户端连接请求
Accept()为服务连接请求而创建一个新的通讯 endpoint 的工厂函数
Shutdown()选择性地终止双向连接的 read-side 流和/或 write-side 流
  1. Data transfer mechanisms. Socket API 提供通过 socket 发送和接收数据的功能:
函数描述符
send() recv()通过特定的 I/O handle 传输和接收数据缓冲
sendto() recvfrom()交换无连接的数据报,每个 sendto() 调用都提供接收方的网络地址
sendmsg() recvmsg()通用目的的函数,包含其他数据传输函数的行为
  1. Options management. Socket API 定义函数,允许程序员改变默认的 socket 行为以启用 multicasting,broadcasting,和修改/查询运输缓冲的大小。
函数描述
setsockopt()修改不同协议栈层的选项
getsockopt()查询不同协议栈层的选项
  1. Network addressing. 除了上述描述的函数,网络应用通常使用函数来解析人类可读的名字,例如,tango.ece.uci.edu,到底层的网络地址,例如 128.195.174.35:
函数描述
gethostbyname() gethostbyaddr()处理在主机名和 IPv4 地址之间的网络地址映射
getipnodebyname() getipnodebyaddr()处理主机名和 IPv4/IPv6 地址之间的网络地址映射
getservbyname()通过人类可读的名字标识服务

虽然 Socket API 总被用于写 TCP/IP 应用,但它足够广泛,能支持多种通讯域。通讯域由一个协议族和一个地址族定义,如下:

  • Protocol family. 如今的网络环境包含大量的协议,能提供多种通讯服务,例如面向连接的可靠的递交,不可靠的多播,等。协议族是协议的集合,提供一组不同的相关服务。当使用 Socket API 创建 socket 时,协议由下述两个参数结合指定:
    1. Protocol family 例如,UNIX-domain(PF_UNIX),Internet-domain IPv4(PF_INET),ATM(PF_ATMSVC),X.25(PF_x25),Appletalk(PF_APPLETALK), 等。
    2. Service type 例如,序列化的可靠字节流 (SOCK_STREAM),不可靠的 datagram (SOCK_DGRAM),等。

例如,TCP/IP 协议通过传入 PF_INET (或 PF_INET6) 和 SOCK_STREAM 标识到 socket() 函数中指定。

  • Address family. 地址族定义地址的格式,以字节为单位描述地址的大小,以及地址的字段的数字,类型和顺序。另外,地址族定义了一组函数来解释地址格式,例如,为了确定 IP datagram 的目的子网。地址族和协议族密切对应,例如,IPv4 地址族 AF_INET 仅和 IPv4 协议族 PF_INET 一起工作。

2.3 Socket API 的限制

原生 Socket API 有几个限制:容易出错,极度复杂,不可移植/不统一。虽然下述的讨论专注于 Socket API,评论同样也应用于其他原生的 OS IPC APIs。

2.3.1 Error-Prone APIs

正如 Section 2.2 描述的那样,Socket API 使用 handles 标识 socket endpoint。通常,操作系统也使用 handle 标识其他的 I/O 设备,比如文件,pipes,和 terminal。这些 handle 使用 weakly typed 的整型或指针类型实现,这会导致隐晦的错误发生在运行期间。为了解释可能发生的这些或其他问题,考虑下述 echo_server() 函数:

// This example contains bugs! Do not copy this example!
#include <syc/types.h>
#include <sys/socket.h>

const int PORT_NUM = 10000;

int echo_server() {
	struct sockaddr_in addr;
	int addr_len;
	cahr buf[BUFSIZ];
	int n_handle;
	// Create local endpoint
	int s_handle = socket(PF_UNIX, SOCK_DGRAM, 0);
	if(s_handle == -1) return -1;
	
	// Set up the address information where the server listens.
	addr.sin_family = AF_INET;
	addr.sin_port = PORT_NUM;
	addr.sin_addr.addr = INADDR_ANY;
	
	if(bind(s_handle, (struct sockaddr *) &addr, &addr_len) == -1)
		return -1;
	
	// Create a new communication endpoint.
	if (n_handle = accept(s_handle, (struct sockaddr *) & addr, &addr_len) != -1) {
		int n;
		while ((n== read(s_handle, buf, sizeof buf)) > 0)
			write (n_handle, buf, n);
		
		close(n_handle);
	}
	return 0;
}

2.3.2 极度复杂的 API

Socket API 提供支持多个功能的单个接口:

  • Protocol families, 例如 TCP/IP,IPX/SPX,ISO OSI,ATM,和 UNIX-domian sockets
  • Communication/connection 角色,例如 active connection establishment vs passive connection extablishment vs 数据传输
  • Communication optimizations, 例如集中写入函数,writev(),在单个系统函数中发送多个缓冲,以及
  • Options 对于不太常见的功能,比如 broadcasting,multicasting,异步 I/O,和紧急数据递交。

Socket 将所有的这些功能组合成一个 API,在 Section 2.2 中的表中被列出来。结果很复杂难以掌握。如果你对 socket API 进行小心的分析,你会看到它的接口可以被解耦成下列维度:

  1. Type of communication service,例如流 vs 数据报 vs 连接的数据报。
  2. Communication/connection role; 例如,客户端总是主动初始化连接,服务器总是被动的接受它们。
  3. Communication domain, 例如仅本地主机 vs 本地和远程主机。

图 2.1 依据这三个维度集群了相关的 Socket 函数。然而,这种自然的聚类在 Socket API 中是模糊的,因为所有的这些功能都被塞到了一组函数中。进一步来说,Socket API 不能在编译期保证其对不同通讯和连接的功能的正确使用,例如主动 vs 被动连接的建立,或数据报 vs 流式通讯。

在这里插入图片描述

2.3.3 不可移植和不统一的 APIs

2.4 Summary

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值