网络编程方法

网络编程概念

所谓网络编程,是指通过编程的手段,将一台主机上的信息通过网络发送到另一台主机上。而我们把这些能使得网络中的两台主机通信的程序称之为网络程序/网络应用/网络服务,如DNS,FTP,Telnet,HTTP等等。

网络编程通常采用的是经典的客户端-服务器模型,如面向连接的客户端服务器通信过程(以四层的TCP/IP模型说明):

1)同一个局域网:LAN

                图1 局域网客户端-服务器通信流程图  

2)不同局域网:WAN

                图2 广域网客户端\-服务器通信流程图

从上图中可以看到,客户端与服务器想要通信则必须调用操作系统提供的方法,完成协议栈的转换与数据交换。而网络环境错综复杂、网络介质种类不一,这时亟需要一种两个端点在网络中通信(交换数据)的抽象,使得上层的应用程序可以屏蔽掉下层这些网络细节,从而专注于实现具体的网络应用。

伯克利套接字的诞生

1983年,加州大学伯克利分校发布4.2BSD Unix操作系统,包含其中的有一套网络应用程序编程接口库,简称伯克利套接字(全称Internet Berkeley Sockets),由于其抽象的优雅性以及使用的便捷性,如今伯克利套接字已经是事实上的网络套接字标准。所有的现代操作系统Socket实现均源于伯克利套接字,包括微软Windows的Winsock。

网络套接字设计

可以说,自上个世纪八十年代伯克利套接字诞生之日起,套接字基本上就是今天的样子(虽然中间有过几次小修补),足见其本身设计的优秀之处。所有的网络编程无一例外地使用套接字实现,套接字编程已然等同于网络编程。

1)理解Socket概念

Socket是操作系统内核提供的一套TCP/IP协议栈的抽象,用于网络中两个端点(endpoint)的通信。从字面意思上理解,Socket直译是插座的意思,可类比为物理上的插孔连接器,两个端点之间要想通过可视化的电缆通道(即网络)通信,则只需将端点上的插头(即应用程序)插入到插孔连接器(即插座)中即可(如封面图所示),而这其中插入的过程就是网络编程。

上面是站在应用层的角度看Socket,如果站在操作系统内核的角度,Socket到底是什么呢?

在Unix操作系统『一切皆文件』的设计哲学指导下,Socket也被抽象为一种特殊类型的文件,可以通过统一的文件方法调用等来操作它,即Socket本质是一种字节流(a stream of bytes)。

:在《UNIX网络编程卷1:套接字网络API》一书中,译者认为Socket一次翻译为『套接口』更为准确,而创建套接字返回的文件描述符则称为『套接字』。因为Socket实际上就是一套内核提供的API接口,无奈『套接字』的译法已被广泛接受。

不同类型的Socket有不同的属性,下面从创建Socket方法调用着手,看下操作系统具体支持哪些socket类型,以Unix操作系统的POSIX.1规范为例:

# include <sys/socket.h>
// 创建Socket:若成功则返回文件(套接字)描述符,否则返回-1
int socket(int domain, int type, int protocol);

参数含义

  • domain:套接字通信域,决定Socket地址格式与寻址方式

    • AF_UNIX:UNIX域,用于同一主机上的程序之间的通信。
    • AF_INET:IPv4因特网域,用于互联网上不同主机上程序之间的通信。
    • AF_INET6:IPv4因特网域。
    • AF_UNSPEC:未指定
  • type:所在通信域采用的套接字类型,决定Socket通信格式

    • SOCK_STREAM:有序、可靠、双向的面向连接的字节流,默认协议TCP。
    • SOCK_DGRAM:长度固定、无链接的不可靠报文传递,默认协议为UDP。
    • SOCK_RAW:IP协议层的数据报接口
    • SOCK_SEQPACKET:长度固定、有序、可靠的面向连接报文传递,与SOCK_STREAM类似,但从该类型套接字是基于报文而不是基于字节流。默认协议SCTP(该协议起步较晚,未广泛集成于TCP/IP协议栈中,目前主要用于电信领域)。
  • protocol:套接字类型支持的协议,决定Socket基于的通信协议

2)Socket所在协议层位置

从根据上面图1与图2的客户端服务器模型可以看到套接字接口位于传输层与应用层之间,这样设计的好处是:

  • 应用层与传输层之间的地方是网络应用与通信细节的分界点:应用层主要处理具体网络应用服务的所有细节,对通信细节了解甚少;传输层及以下对网络应用了解不多,但是要处理所有通信细节,如发送数据、等待确认,给无需到达的数据排序,计算并验证校验和等等。

  • 所有现代操作系统均区分用户态与内核态:应用层构建的是用户态进程;传输层及以下通常作为操作系统内核实现的一部分。

凡事皆有例外。

在广域网(或互联网)中的路由器或三层交换机等网络设备(如上图2)通常不要求实现传输层的协议,因为它们的所有操作均在IP层即可完成。然而,某些场景下网络设备也需要使用到socket相关信息,如NAT,Qos支持等,这就需要内核提供套接字接口直接访问下层的IP层(即此时需要自己构建协议首部)。为此,套接字标准的套接字类型定义了SOCK_RAW类型,专用于这些网络设备中,配合路由协议如IGRP和OSPF以及ICMP。所以,套接字所在的准确网络层级位置如下图3所示

注:Linux系统还增加了SOCK_PACKET类型,直接从网络驱动层获取报文,tcpdump工具就是基于此实现的。

套接字编程方法

Socket通常用于客户端与服务器之间通信(即交换信息)。典型的系统设计是服务器在一台机器上,客户端在另外一台机器上,然后客户端连接到服务器,交换信息,最后断开连接。所有现代操作系统均提供了一套SocketAPI用于编写网络应用,下面是基于TCP协议的SocketAPI流程

关于具体API细节,请自行RTFM。

总结

本文介绍了网络编程中的概念与原理,以及从设计者的角度分析Socket实现的『知其所以然』部分。本文力求做到简单易懂,这并不意味着网络编程很简单(可不只是调用几个系统API...)。实现一个网络应用程序(如TCP/HTTP/WebSocket服务器)不难,难的是基于当前业务场景针对具体操作系统与网络协议不断调优,提供一个高性能/高并发且稳定可靠的网络服务。

本文首发微信公众号:yablog

欢迎扫码关注

Webp.net-compress-image.jpg

阅读 161

推荐阅读