Linux 网络编程_第1页
Linux 网络编程_第2页
Linux 网络编程_第3页
Linux 网络编程_第4页
Linux 网络编程_第5页
已阅读5页,还剩112页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux 网络编程网络编程 引言 通信协议 通信协议用于协调不同网络 不同系统之间的信息交换 80 年代 随着计算机从奢侈品到家用电器过渡 大家都意识到一台孤立的计算机构成 的 孤岛 没有太大意义 于是就想把这些孤立的系统组在一起形成网络 但是各个型号 的计算机中却是完全不同的设备与操作系统 这种天下大不统的局面成为这些设备与系统 交互的瓶颈 为了解决这样的问题 通信协议应运而生 它为建立设备之间互相识别提供 有价值的信息机制 当今通信界有许多可采用协议 如 XNS SNA TCP IP 等 TCP IP 协议族 它是非专有网际互联产品 起初 它仅仅是美国政府资助的一个分组 交换网络研究项目 但是随着技术的的不断进步 TCP IP 协议族的不断完善 越来越成熟 到今天 它已然发展成计算机之间最常用的组网形式 它成为被称作 全球互联网 或 因特网 的基础 同时 TCP IP 协议也是我们学习网络编程的基础 金字塔的最底层 习惯上 我们喜欢把 TCP IP 协议族简称之为 TCP IP 协议 但他并不是只提供两种协 议 实际上它是一个一起工作的通信家族 为网络数据通信提供通路 TCP IP 协议族大体 上可以分为三部分 1 Internet 协议 IP 2 传输控制协议 TCP 和用户数据报文协议 UDP 3 其它处于 TCP 和 UDP 之上的一组专门用于开发的用于程序的如 TELENET FTP DNS SMTP 等许多协议 TCP IP 协议 Transmission Control Protocol Internet Protocol 由 INTERNET 的广泛 使用 使得 TCP IP 成了事实上的工业标准 标准 OSI 模型与 TCP IP 参考模型对照 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 应用层 Telnet FTP HTTP SMTP等 传输层 TCP UDP等 网络层 IP ICMP IGMP等 网络接口层 本地网络协议 OSI参考模型 TCP IP参考模型 1 分层的结构 通信协议通常分不同层次进行开发 每一层分别负责不同的通信功能 如图 根据 TCP IP 协议参考模型 它通常被认为是一个四层协议的模型 其每一层负责不同的功 能 1 链路层 对应标准模型的数据链路层与物理层 有时也被称作是网络接口层 通 常包含操作系统中的设备驱动程序和计算机中对应的网络接口卡 他们一起处理电缆 的物理接口细节 2 网络层 对应标准模型的网络层 有时也称作互联网层 处理来自传输层的分组 发送请求 添加 IP 报头 封装数据包 处理数据报 拆开数据包 去掉 IP 报头 处理路由 流控 拥塞等 涉及到的协议有 IP ICMP RIP OSPF IGMP 3 传输层 对应标准模型的传输层 它负责网络主机与主机的通信 或者说是两台主 机的应用程序提供端到端的通信 这一层中 有两个传输协议 TCP 和 UTP 4 应用层 对用标准模型的会话层 表示层 应用层三层 提供用户的交互 处理 特定应用程序的细节 Internet 协议 IP IP 是 TCP IP 协议族中最为核心的协议 所有的 TCP UDP ICMP TGMP 数据都以 IP 数据报格式传输 IP 主要负责在源主机和目的主机之间传输来自较高层软件的数据 报文 数据块 它在源和目的地之间提供非连接的传递服务 简单归纳起来 IP 主要有以下四个功能 1 数据传送 2 寻址 3 路由选择 4 数据报文分段 1 数据传送的基本特点 尽最大努力的无连接传送服务 IP 的主要目的是为数据输入 输出网络提供基本办法 为高层协议提供无连接的传送 服务 这意味着在 IP 将数据递交给接受站点以前不在传输站点和接受站点之间建立对话 它只是分装和传递数据 但不向发送者或接受者报告包的状态 不处理所遇到的故障 假 如数据链路层遇到 可恢复 的错误时 IP 不予以通知和处理 它将报文和错误一起传出 去 由高层协议 TCP 负责执行和消除差错 IP 接受并格式化数据 以便传输到数据链路层 此外 IP 还检索来自数据链路的数据 并将它送给请求的高层 IP 传送的信息单位被称之为 数据报文 一些数据报文被组装 起来就称为包 IP 协议不注意包内的数据 它所要做的一切只是将自己的包头加到由上一层协议提供 过来的数据上封装数据 或者拆开包头 分析包头内容 并将它传递给网络或者某网际上 的某些节点 如图 分装在 Ethernet 帧中的 IP 头 我们来详细了解一下 IP 数据报的格式 4 比特字段的版本比特字段的版本 IP 协议的版本号 如 IPV4 IPV6 4 比特的字段的头长度比特的字段的头长度 即包头长度 在实际中 并是必须使用 IP 头的所有位 所以需要 该字段来指明 IP 头的长度 由于它是一个 4 比特字段 因此整个首部 IP 头 的最长为 60 字节 以 32 位表示字 每字 32 位 4 比特的字段从 0000 到 1111 其值最大是 15 15 个字一共 15 32 480 位 每字节 8 位 480 位则有 60 个字节 最短的 IP 头是 20 字节 不包括数据和选项 因此这个字段的值就是 5 也就是 0101 如果这个字段的值是 6 就此等于增加了 32 位 一个字 8 比特字段的服务类型 比特字段的服务类型 如图 优先权字段 3 位 000 111 也就是 0 7 的值 0 为正常 7 为网络控制 它允许传输站 点的应用程序向 IP 层发送数据报文的优先权 该字段与 D 延时 T 吞吐量 和 R 可靠 性 相结合 TOS 字段 这些位向路由器表明应采取哪个路由 D 位 设置为 1 时 请求低延时 T 位 设置为 1 时 请求高吞吐量 R 位 设置为 1 时 高可靠性 例如 如果去目的地有两个以上的路由 路由器将读这个字段 以选择一个正确的路由 由应用程序 协议 设置 TOS 字段 大多数实现不推荐应用程序自己设置 TOS 字段 路 由只负责读这种字段 不负责设置 在读信息的基础上 路由器将选择数据报文最佳路由 各种协议设置的 TOS 如下表 16 比特字段的总长度 比特字段的总长度 总长度指整个 IP 数据报的长度 以字节为单位 利用首部长度字 段和总长度就可以知道 IP 数据报中数据内容的起始位置和长度 这个字段 16 比特 所以 IP 数据报最长可达 65535 字节 总长度字段是 IP 首部中必要的内容 因为一些数据链路需 要填充一些数据 大分小补原则 分段包会讲到 以达到 Ethernet 最小帧长度 46 字节 但 是 IP 数据可能会更短 如果没有总长度字段那么 IP 层就不知道 46 字节中那些真正 IP 数 据报的内容 分段包分段包 有时会出现一个大的不能转入网络传出的包另外网络的情况 例如 TokenRing 最大 4472 字节的传输包 网络向 Ethernet LAN 最大 1518 字节 输出帧 TCP IP 路由器必 须能将较大的包破碎成较小的包 TCP 将建立适于连接的包大小 但是如果两个通信站点 被多种类型的媒体分开 那么将怎样支持不同的传输包大小呢 另外 尽管 IP 数据报的最长可达 65535 字节 但是主机一般要求不能接受超过 576 字 节的数据报 因此解决办法就是就数据报分段 16 比特字段的标识 比特字段的标识 3 比特字段的标志 比特字段的标志 13 比特字段的分段偏移比特字段的分段偏移 TCP IP 可以运行在任何数据链上 当向不同的网络发送数据时 发送数据的大小可以 根据网络的不同发生不同的变化 标识字段唯一的标识主机发送的每一份数据报 通常每 送一份报文他的值就加 1 使用识别将识别出那些数据报文属于一个小组 标志将标志出 是否将一份数据报文分段以及是否出现了较多的分段 它占 3 个位 分别为 0 DF MF DF 为 0 表示该包可拆分成多个碎片 1 为不能 MF 为 0 表示最后的碎片 为 1 表示还有碎片 总长度和分段偏移字段使 IP 能重新构造数据报文并将它传递到 高层 软件 总长度指出了原始包的总长度 偏移字段向正在组装包的节点指出该包偏移的开始 端 此时数据处于分段 以重新构造包 8 比特字段的生存时间 比特字段的生存时间 TTL 在包传递的过程中可能出现某种错误 致使包在网际的路由器之间不断循环 为防止此类 事件 因而引入了 TTL 由包的发源地设置的生存时间的起始值 生存时间是一个由路由 器使用的字段 确保包不会无限的循环 实际上这里记录的是一个允许经过路由器的数量 每经过一个路由器这个值变回减一 当这个字段减到 0 时 路由器将抛弃这个包 8 比特字段的协议字段比特字段的协议字段 该字段指出了哪个较高协议发送了帧 哪个接受协议应得到这个帧 当前对通用传输实现 是 TCP 和 UDP 16 比特字段的校验和比特字段的校验和 这是一个 16 位的循环冗余检验 目的是确保帧头的完整性 利用 IP 数据包数据段的数据 产生 CRC 循环冗余检验 数 由发送站点放入这个字段 当接受站点读数据时 它将计 算 CRC 如果两个 CRC 不匹配 则表示帧头有错误 将废弃包 随着数据报文被每个路 由器接收 每个路由器将重新计算校验和 这是因为数据报文由所穿过的每个路由器改变 TTL IP 选项字段 选项字段确定数据是正常数据还是用做网络控制的数据 在选项类别内包含了多种选项编 号 比如 常用的一种是 0 的选项类别代表数据报文或网络控制包 类别 0 的意味着 必须使用严格的路由选择 由源主机所规定 在那种情况下 包所经过的每个网关向包增 加它的 IP 地址 源 IP 地址 发送包的主机地址 目的 IP 地址 包发送目的地的主机地址 IP 地址的组成 每个 IP 地址都由两部门组成 1 网络号 2 主机号 其中网络号表示一个网络 主机号表示该主机在该网络的位置 网路号由子网掩码中全 1 的代码对应 主机号由子网掩码中全 0 的代码对应 例如 IP 地址 192 168 10 3 子网掩码 255 255 255 0 网络号 192 169 10 网络 主机号是 3 IP 地地址址分分类类 1 A 类 IP 地址 一个 A 类 IP 地址由 1 字节的网络地址和 3 字节主机地址组成 网络地址的最高位必 须是 0 地址范围 1 0 0 1 126 255 255 254 二进制表示为 00000001 00000000 00000000 00000001 01111110 11111111 11111111 11111110 可用的 A 类网络有 126 个 每个网络 能容纳 1600 多万个主机 2 B 类 IP 地址 一个 B 类 IP 地址由 2 个字节的网络地址和 2 个字节的主机地址组成 网络地址的最 高位必须是 10 地址范围 128 1 0 1 191 254 255 254 二进制表示为 10000000 00000001 00000000 00000001 10111111 11111110 11111111 11111110 可用的 B 类网络有 16382 个 每个网络能容纳 6 万多个主机 3 C 类 IP 地址 一个 C 类 IP 地址由 3 字节的网络地址和 1 字节的主机地址组成 网络地址的最高位 必须是 110 范围 192 0 1 1 223 255 254 254 二进制表示为 11000000 00000000 00000001 00000001 11011111 11111111 11111110 11111110 C 类网络可达 209 万余个 每个网络 能容纳 254 个主机 4 D 类地址用于多点广播 Multicast D 类 IP 地址第一个字节以 lll0 开始 它是一个专门保留的地址 它并不指向特定的 网络 目前这一类地址被用在多点广播 Multicast 中 多点广播地址用来一次寻址一组 计算机 它标识共享同一协议的一组计算机 地址范围 224 0 0 1 239 255 255 254 5 E 类 IP 地址 以 1111 开始 为将来使用保留 其他 全零 0 0 0 0 地址对应于当前主机 全 1 的 IP 地址 255 255 255 255 是当前子网的广播地址 一 传输控制协议 TCP 早先的网络使用电话线连接的 70 年代的通信工具和当今的通信设备无法比拟 线 的噪音 干扰 非常大 不能处理数据 因此 TCP 协议具有严格的内装差错检验算法确 保数据的完整性 TCP 用俗话说是一个可靠的传输协议 首先 它是一个传输层的协议 它能提供端 口编号的译码 以识别主机的应用程序 并完成数据的可靠传输 为了说明 TCP 的结构严格性 让我们来打个比方 假设你正在向某人讲述一个故事 如果那个人只是站在那里不应答你 你将不能辨明他是否了解了你所讲述的内容 如果那 个人用点头之类的方式应答你 那么你将知道你所将的话他是了解的 因此能继续同他交 谈 于此类似 TCP 协议使用顺序编号和确认信息通网络上另外的站点交谈 使用顺序编 号来确定包含数据的排序并发现故障的包 因为网际上不同的包不一定会以发送他们的顺 序老到达 所以要对包中数据进行排序 以确保与发送顺序相同 此外 接受站点还可能 接到两个同样的包 为了进行可靠类型的通信 使用带有确认信息的顺序编号 这种处理 被称为全双工 连接的每一端都必须要考虑到另一端的需要而维持它自己的顺序编号 TCP 是面向字节顺序的协议 这意味着包内的每一个字节被分配一个顺序编号 并分 配给每包一个顺序编号 分配给包那每个字节的顺序编号可以合理的重复 为了完成数据传输任务 TCP 将报文或数据分成可管理的长度并加上 TCP 头 下图表 示了一个 TCP 头 其详细 TCP 头如下图 端口说明 很多人都知道端口 但是并不了解为什么要有端口 那么端口有什么用 呢 实际上端口号就是网络服务的标识号 我们知道 一台拥有 IP 地址的主机可以提供许 多服务 比如 Web 服务 FTP 服务 SMTP 服务等 这些服务在一台主机上是通过 1 个 IP 地址来实现 那么 主机是怎样区分不同的网络服务呢 其实就是是通过 IP 地址 端口号 来区 分 16 比特字段的源端口号 比特字段的源端口号 源发站点的端口编号 说明那个服务发出的 TCP 请求 16 比特字段的目的端口 比特字段的目的端口 接受站点的端口编号 说明那个服务接受 TCP 请求 32 比特字段的顺序序号 比特字段的顺序序号 分配给 TCP 包的编号 32 比特字段的确认序号 比特字段的确认序号 目的地站点向源站点发送的编号 对以前所接受的包表明确 认 改序号指出目的站点希望接收下一个顺序编号 4 比特字段的首部长度 比特字段的首部长度 指出 TCP 头的长度 即 TCP 头中的 32 位字的数 一般的 这个值是 0101 代表 20 字节的 TCP头 6 比特字段的保留位比特字段的保留位 为未来使用而保留 必须设置为 0 6 位的控制位位的控制位 见表 URG紧急指示字段 详细解释见下 ACK如果设置 该包需要确认 PSH推入功能 当接收方收到数据后 立即交给上一层程 序 RST恢复连接 当一个功能是不接收连接请求时 SYN用于建立序号 同步序号 请求建立连接 FIN结束总报文 请求关闭来接 URG 当次比特字段为 1 时 那么后面 16 比特字段的紧急指字段有效 其作用是通 知接收方 由紧急指针指示的位置之前 数据流中有紧急数据传输 如果要传输需紧 急处理数据 应尽快传送和处理 意味着优先处理 例如 TCP 用于远程登录会话 服务 远端主机上的程序突然出现错误时 用户可能要从本地键盘上输入几个控制符 中断或退出远端运行的程序 这种信号应尽快处理 TCP 将停止发送缓存区积累的数 据 尽快将缓冲区中已有的数据发送出去 16 比特字段的窗口比特字段的窗口 窗口字段也叫接受窗口的大小 表明所能接受的最大字节数 16 比特字段的比特字段的 CRC 校验和校验和 用于确定被接收的数据报文在传输期间是否有错误 16 比特字段的紧急指示字段比特字段的紧急指示字段 它指出了紧急数据的字节的顺序编号 TCP 提供的主要服务有 1 建立 维持和终止两个进程之间的连接 2 可靠的包传递 经过确认过程 3 编序包 可靠的数据传输 4 控制差错的机制 5 通过使用端口 允许在主机内部实现不同进程多重连接的能力 6 是使用全双工操作的数据交换 关于 TCP IP 的三次握手 第一次握手 建立连接时 客户端发送 SYN 包到服务器 并进入 SYN SEND 状态 等待 服务器确认 第二次握手 服务器收到 SYN 包 必须确认客户的 SYN 同时自己也发送一个 SYN 包 即 SYN ACK 包 此时服务器进入 SYN RECV 状态 第三次握手 客户端收到服务器的 SYN ACK 包 向服务器发送确认包 ACK 此包发送 完毕 客户端和服务器进入 ESTABLISHED 状态 完成三次握手 带外数据 带外数据也称为 TCP 紧急数据 在套接字的抽象中包括了带外数据这一概念 带外数 据是每一对相连流套接字间逻辑上独立的传输通道 带外数据是独立于普通数据传递给用 户的 二 用户数据报文协议 UDP 也是 TCP IP 的传输层协议 他是无连接的 不可靠的传输服务 当接受数据时 它不向发送方提供确认信息 它不提供输入包的顺序 如果出现丢失包或重份包的情况 也不会想发送方发送错误消息 要求重传 这一点和 IP 协议很像 但 UDP 同样工作在传 输层 它的主要作用是分配和管理端口编号 由于它的执行具有很低的开销 所以执行速 度比 TCP 快 它多半用于不需要可靠的应程序 例如域名服务 UDP 头如下图 上图表示一个 UDP 头 应用程序被封装在 UDP 头那 然后到了下一层一起被封装在 UDP 头内 有 IP 协议将这个数据报文发送到数据链路层 数据链层又使用它的帧头来封 装这个报文 最后将数据传送到物理层传输 端口号分配 TCP 和 UDP 使用 16 个比特字段的端口号 端口可以看作是网络连接的接触点 如果 一个程序想要提供一个特定的服务 他就会将自己连接到一个端口并等待客户 端口号分类 为了防止端口号混乱 应对端口号进行统一的分配 可分为以下 3 个范围 0 1023 这些端口号由 IANA 统一控制和分配 1024 49151 这些端口号虽不由 IANA 控制 但 IANA 等级这些端口的使用 49152 65535 这些端口号通常只是展示使用 常用端口号 5 远程作业登录 21 文件传输控制协议 FTP 23 终端仿真协议 Telnet 25 简单邮件传输协议 SMTP 38 路由访问协议 RAP 53 域名服务器 DNS 69 小型文件传输协议 80 超文本传输协议 92 网络打印协议 93 设备控制协议 第一章 套接字 Socket 网络程序设计全靠套接字接收和发送信息 尽管套接字这个词表面上好像有些神秘 但是这个概念极易理解 1 1 Socket 的历史的历史 在80年代早期 远景研究规划局APRA Advanced Research Projects Agency 资助了加利 福尼亚大学伯克利分校的一个研究组 让他们将TCP IP 软件移植到UNIX 操作系统 中 并将结果提供给其他网点 作为项目的一部分 设计者们创建了一个接口 应用进程 使用这个接口可以方便的进行通信 他们决定 只要有可能就使用以有的系统调用 对那 些不能方便的容入已有的函数集的情况 就再增加新的系统调用以支持 TCP IP 功能 这样做的结果就出现了插口接口 Berkeley Socket 这个系统被称为Berkeley UNIX 或BSD UNIX TCP IP 首次出现在BSD 4 1 版本release 4 1 of Berkeley Software Distribution 由许多计算机厂商 都采用了Berkeley UNIX 于是许多机器上都可以使用Socket 了 这样 Socket 接口就被广泛使用 到现在已经成为事实上的标准 如下图 Socket 的英文原意就是 孔 或 插座 其在程序中的作用也近似是一个插座 我们将电话系统与面向连接的 Socket 机制相比 有着很相似的地方 以一个国家级的电话 网为例 电话的通话双方相当于相互通信的两个进程 通话双方所在的地区相当于一个网 络 区号是他们的网络号 网络地址 区内的一个单位的交换机相当于一台主机 主机 分配给每个用户的局内号码相当于 Socket 号 任何用户在通过之前 首先要有一部电话机 这相当于申请一个 Socket 号 同时要知道对方的电话号码 相当于对方的 Socket 然后 向对方拨号呼叫 相当于发出连接请求 假如对方在存在并空闲 拿起电话话筒 双方就 可以正式通话了 相当于连接成功 双方通话的过程 是向电话机发出信号和从电话机接 收信号的过程 相当于 Socket 发送数据和从 Socket 接收数据 通话结束后 一方挂上电 话 相当于关闭 Socket 撤销连接 在电话系统中 一般用户只能感受到本地电话机的和对方电话号码的存在 建立通话 的过程 话音传输的过程以及整个电话系统的技术细节对它都是透明的 这也与 Socket 机 制非常相似 Socket 利用网络之间的通信设施实现进程通信 但它对通信设施的细节毫不 关心 只要通信设施能提供足够的通信能力 它就满足了 一个完整的 Socket 描述 协议 本地地址 本地端口 远程地址 远程端口 同时每一个 Socket 有一个本地的唯一 Socket 号 由操作系统分配 Socket 描述符 Socket 是面向客户 服务器模型而设计的 1 2 套接字的类型 套接字有三种类型 流式套接字 SOCK STREAM 数据报套接字 SOCK DGRAM 及原 始套接字 SOCK RAW 1 流式套接字 SOCK STREAM 流式套接字可以提供可靠的 面向连接的通信流 如果你通过流式套接字发送了 顺序数据 1 2 3 那么数据到达远程时候的顺序也是 1 2 3 流式套接字使用了 TCP 协议 TCP 保证了数据传输的正确性和顺序性 2 数据报套接字 SOCK DGRAM 数据报套接字定义了一种无连接的服务 数据通过相互独立的报文进行传输 是 无序的 并且不保证可靠 无差错 数据报套接字定义了一种无连接的服务 数据通过相互独立的报文进行传输 使 用数据报协议 UDP 协议 数据是无序的 并且不保证可靠 无差错 这就意味着以下 一些事实 1 如果你发送了一个数据报 它可能不会到达 2 它可能会以不同的顺序到达 3 如果它到达了 它包含的数据中可能存在错误 由此 那问题也出现了 如果发送一个数据报 但是如果数据包丢失 怎么保证程序 的正常工作呢 事实上 每个使用 UDP 的程序都要有自己的对数据进行确认的协议 比如 TFTP 协议定义了对于每一个发送出去的数据包 远程在接受到之后都要回送一个数据包 告诉本地程序 我已经拿到了 一个 ACK 包 如果数据包发的送者在 5 秒内没有 的得到回应 它就会重新发送这个数据包直到数据包接受者回送了 ACK 信号 这些知识 对编写一个使用 UDP 协议的程序员来说是非常必要的 无连接服务器一般都是面向事务处理的 一个请求一个应答就完成了客户程序与服务 程序之间的相互作用 3 原始套接字 SOCK RAW 原始套接字主要用于一些协议的开发 可以进行比较底层的操作 它功能强大 但是没有上面的两种套接字使用方便 一般的程序也很少涉及到原始套接字 1 3 套接字的工作过程 套接字工作过程如下 服务器首先启动 通过调用 socket 建立一个套接字 然后调 用 bind 系统调用将该套接字和本地网络地址联系在一起 再调用 listen 使套接字做好 侦听的准备 并规定它的请求队列的长度 之后就调用 accept 来接收连接 客户在建立 套接字后就可调用 connect 和服务器建立连接 连接一旦建立 客户机和服务器之间就 可以通过调用 read send 和 write recv 来发送和接收数据 最后 待数据传 送结束后 双方调用 close 关闭套接字 Socket 描述符 我们经常反复强调 在 UNIX Linux 系统中 任何东西都是一个文件 这句话描述 一个事实 那就是在 UNIX Linux 中 任何关于 I O 的操作 都是通过读写文件描述符 来实现的 一个文件描述符只是一个简单整形数值 代表一个被打开的文件 如果我们把文件进 行广义上理解 这个文件并不只代表磁盘文件 它可以代表一个网络上的连接 一个先进 先出的队列 消息队列描述符 一个屏幕终端 以及其他的一切 在 UNIX Linux 系 统中 任何一个事物都是一个文件 所以如果你想通过 Internet 和另外一个程序通讯的话 你将会是通过一个文件描述符 广义 来实现 也就是传说中的 socket 文件描述符 那么怎么获得一个 socket 文件描述符呢 系统调用似乎是一个不错的选择 socket 系 统调用将返回一个套接字描述符 然后我们就可以对这个描述符进行一系列的操作 比如 send recv 事实上 Socket 套接字描述符也是一个文件描述符 read 和 write 系统调用似乎也是不错的操作选择 但是通过使用 send 和 recv 函数可 以对网络数据的传输进行更好的控制 一个套接字是怎样在网络上传输数据的 一个套接字是怎样在网络上传输数据的 数据被分成一个一个的包 Packet 包的数据头 或数据尾 在应用层由应用层协 议封装 比如TFTP协议 加上协议头 然后将整个包交给下一层 被下层协议再次包装 比如UDP 再这之后数据包会再次交给下一层并被被下层协议包装 比如IP 协议 最后交给最底层 物理层 包装上最后一层信息 Ethernet 信息头 通过物理层的通信 设备传输 当接受端的计算机接收到这个包后 依次自下而上首先剥去数据包中的Ethernet 信息头 然后内核在剥去IP 和UDP 信息头 最后把数据包提交给TFTP 应用程序 由 TFTP 剥去TFTP信息头 得到了原始数据 Socket 套接字的结构 sturct sockaddr 这个结构用来存储套接字的地址 数据定义 struct sockaddr unsigned short sa family char sa data 14 sa family Internet 地址簇 即特定的通信域 套接字必须存在于其特定的通信域 即地 址族 中 只有隶属于同一地址族的套接字才能建立对话 一般为网际域 AF INET 代表ipv4 版本 的网际域 所有使用网际协议簇的进程均适用于该域 另外ipv6 网际域为 AF INET6 协议域 PF INET 等 sa data 包含了一些远程电脑的地址 端口和套接字的数目 它里面的数 据是杂溶在一起的 为了能更清楚的处理 struct sockaddr 一般会建立一个相似结构的 struct sockaddr in struct sockaddr in in 代表 Internet struct sockaddr in short int sin family Internet 地址簇 unsigned short int sin port 端口号 struct in addr sin addr Internet 地址 unsigned char sin zero 8 添 0 这个结构提供了方便的手段来访问 socket address struct sockaddr 结构中的每一 个元素 其中 sin zero 8 是为了使两个结构在内存中具有相同的尺寸 使用 sockaddr in 的时候要把 sin zero 全部设成零值 bzero 或 memset 函数 而且有一点很重要 就是一个指向 struct sockaddr in 的指针可以声明指向一个 struct sockaddr 的结构 所以虽然 socket 系统调用需要一个 structaddr 但是你也可以给他一个 sockaddr in struct in addr 格式如下 struct in addr unsigned long s addr 注意 在 struct sockaddr in 中 sin family 相当于在 struct sockaddr 中的 sa family 通常设成 AF INET 后面的 sin port 和 sin addr 必须是网络字节顺序 1 4 基本转换函数 在前面提到了网络字节顺序 那么什么网络字节顺序呢 它有什么特殊性 又如何将 我们通常使用的数据转换成这种格式呢 1 网络字节顺序 因为每一个机器内部对变量的字节存储顺序不同 有的系统是高位在前 低位在后 而 有的系统是低位在前 高位在后 而网络传输的数据是一定要统一顺序的 所以对与内部 字节表示顺序和网络字节顺序不同的机器 就一定要对数据进行转换 比如IP 地址的表示 端口号的表示 但是内部字节顺序和网络字节顺序相同的机器该怎么办呢 是这样的 它们也要调用转换函数 但是真正转换还是不转换是由系统函数自己来决定的 2 有关的转化函数 我们通常使用的有两种数据类型 短型 两个字节 和长型 四个字节 下面介绍 的这些转换函数对于这两类的无符号整型变量都可以进行正确的转换 如果你想将一个短型数据从主机字节顺序转换到网络字节顺序的话 有这样一个函 数 它是以 h 开头的 代表 主机 紧跟着它的是 to 代表 转换到 然后 是 n 代表 网络 最后是 s 代表 短型数据 H to n s 就是htons 函数 下面给出套接字字节转换程序的列表 htons Host to Network Short 主机字节顺序转换为网络字节顺序 对无符号 短型进行操作4 bytes htonl Host to Network Long 主机字节顺序转换为网络字节顺序 对无符 号长型进行操作8 bytes ntohs Network to Host Short 网络字节顺序转换为主机字节顺序 对无符 号短型进行操作4 bytes ntohl Network to Host Long 网络字节顺序转换为主机字节顺序 对无符 号长型进行操作8 bytes 常用转换函数具体解释如下 htonshtons 将 将 1616 位主机字符顺序转换成网络字符顺序 位主机字符顺序转换成网络字符顺序 表头文件 include 定义函数 unsigned short int htons unsigned short int hostshort 函数说明 htons 用来将参数指定的 16 位 hostshort 转换成网络字符顺序 返回值 返回对应的网络字符顺序 htonlhtonl 将 将 3232 位主机字符顺序转换成网络字符顺序 位主机字符顺序转换成网络字符顺序 表头文件 include 定义函数 unsigned long int htonl unsigned long int hostlong 函数说明 htonl 用来将参数指定的 32 位 hostlong 转换成网络字符顺序 返回值 返回对应的网络字符顺序 32 位位 IPv4 地址结构地址结构 in addr struct in addr union struct uint8 t s b1 s b2 s b3 s b4 S un b struct uint16 t s w1 s w2 S un w in addr t S addr S un define s addr S un S addr define s host S un S un b s b2 define s net S un S un b s b1 define s imp S un S un w s w2 define s impno S un S un b s b4 define s lh S un S un b s b3 s addr 无符号的 32 位整数 对应 32 位的 IP 地址 s b1 s b2 s b3 s b4 4 个无符号 8 位整数 对应 4 个数字表示的 IP 地址 例 将 127 0 0 1 附给 addr in addr addr addr Sun un S un b s b1 127 addr Sun un S un b s b2 0 addr Sun un S un b s b3 0 addr Sun un S un b s b4 1 或者 in addr addr addr s addr 0 x0100007f IP 地址的转换 很幸运 Linux 系统提供很多用于转换 IP 地址的函数 使你不必自己在写一段费力不讨好 的子程序来变换 IP 首先 让我们假设你有一个 struct sockaddr in inaddr 并且你的 IP 是 166 111 69 52 你想把你的 IP 存储到 inaddr 中 你可以使用函数 inet addr 它能够 吧一个用数字和点表示的 IP 地址的字符串转换成一个无符号长整数 你可以像下面这样使 用它 inaddr sin addr s addr inet addr 166 111 69 52 注意 1 函数 inet addr 返回的 IP 地址已经是网络字节顺序了 所以不需要在调用 htonl 函数 2 上面的用法不是一个好习惯 因为没有进行错误检查 如果 inet addr 函数执行错 误 返回一个 1 那么二进制的无符号整数值 1 相当于什么 相当于 255 255 255 255 这是一个广播地址 那对程序来说简直是一个噩梦 好 现在我们可以把字符串的 IP 地址转换成长整型了 那么能不成转换回来呢 有没 有一个方法可以让我们吧 struct in addr 中的 IP 地址在转换成字符串打印出来 按照 数字 数字 数字 数字 的格式 这里 你可以使用函数 inet ntoa ntoa d 代表 Network to ASCII printf s inet ntoa inaddr sin addr 函数详细解析 inet ntoa 将网络二进制的数字转换成网络地址 将网络二进制的数字转换成网络地址 表头文件 include include include 定义函数 char inet ntoa struct in addr in 函数说明 inet ntoa 用来将参数 in 所指的网络二进制的数字转换成网络地址 然后将指向此 网络地址字符串的指针返回 返回值 成功则返回字符串指针 失败则返回 NULL 注意 1 inet ntoa 使用 struct in addr 作为一个参数 而是一个长整型 2 inet ntoa 返回一个字符指针 它指向一个定义在函数 inet ntoa 中的 staic 类型字符串 所以每次调用 inet ntoa 都会改变最后一次调用 inet ntoa 函数时得到 的结果 例如 char a1 a2 a1 inet ntoa ina1 sin addr this is 166 111 69 52 a2 inet ntoa ina2 sin addr this is 166 111 69 53 printf address 1 s n a1 printf address 2 s n a2 将会显示出 address 1 166 111 69 53 address 2 166 111 69 53 如果我们想把结果保存下来 那么你可以在每次调用 inet ntoa 后调用 strcpy 将 结果保存到另外一个你自己的字符串中 补 inet aton const char cp struct in addr addr 转换网络主机地址 cp 为二进制数值 并存储在 struct in addr 结构中 即第二个参数 addr 函数返回非 0 表示 cp 主机有地有效 返回 0 表示主 机地址无效 1 5 基本套接字调用 Linux 支持伯克利 BSD 风格的套接字编程 它同时支持面向连接和不连接类型的套 接字 在面向连接的通讯中服务器和客户机在交换数据前要先建立一个连接 在不连接通信 中数据被作为信息的一部分交换 无论哪一种形式 服务器中是最先启动的 把自己绑定 在一个套接字上 然后侦听信息 服务器究竟怎样试图去侦听就得依靠你编程所设定的连 接类型了 在这之前 我们要先了解一些系统调用 socket 函数 函数目标 取得套接字描述符 所需文件头 include include 函数原型 int socket int domain int type int protocol 参数说明 domain 需要设置成 AF INET Internet 地址族 即 Internet 通信域 type 参数告诉内核这个新 socket 是什么类型 SOCK STREAM 流式套接 字 或者是 SOCK DGRAM 数据报套接字 protocol 协议 设为 0 表示协议有系统自己匹配 另外 IPPROTO TCP 表示 TCP 协议 IPPROTO UDP 表示 UDP 协议 Socket 函数只是简单的返回一个以后可以使用的套接字描述符 如果发生错误 将返 回 1 错误将包含在 ERRNO 中 bind 函数 函数目标 建立套接字描述符与相应的套接字地址对应起来 所需文件头 include include 函数原型 int bind int sockfd struct sockaddr my addr int addrlen 参数说明 sockfd 是由 socket 函数返回的套接字描述符 my addr 本地套接字的地址 是一个指向 struct sockaddr 的指针 地址 包含有关你的地址信息 名称 端口和 IP 地址 addrlen 是 struct sockaddr 的大小 通常为 sizeof struct sockaddr 当我们使用 socket 函数得到一个套接字描述符 我们需要将这个 socket 绑定到一 个你的机器上的端口 例如下面一段代码 include include include define MYPORT 4000 main int sockfd struct sockaddr in my addr sockdf socket AF INET SOCK STREAM 0 if sockfd 1 return my addr sin family AF INET my addr sin port htons MYPORT my addr sin addr s addr inet addr 166 111 69 52 bzero bind sockfd struct sockaddr 这里有一些值得注意的代码 my addr sin port 是网络字节顺序 my addr sin addr s addr 也是网络字节顺序 最后 还有一种方法 可以让 bind 函数自己取得你自己的 IP 地址和端口 代码如下 my addr sin port 0 my addr sin addr s addr INADDR ANY 其中宏 INADDR ANY 代表 0 为了保险起见 通常我们如下代码 my addr sin port htons 0 my addr sin addr s addr htonl INADDR ANY 当 bind 函数调用错误时 它返回 1 错误代码会包含在 error 中 说明 绑定意味者该服务器只接受来自绑定的协议端口 IP 地址和 TCP 端口号 的数 据 connect 函数 函数目标 使用套接字进行网络连接 所需文件头 include include 函数原型 int connect int sockfd struct sockaddr serv addr int addrlen 参数说明 sockfd 套接字文件描述符 由 socket 函数返回的 serv addr 是一个存储远程计算机的 IP 地址和端口信息的结构 Addrlen 同样是 struct sockaddr 的大小 通常是 sizeof struct sockaddr 下面我们来看一个程序片段 include include include define DEST IP 166 111 69 52 DEFINE DEST POST 23 Main int sockfd struct sockaddr in dest addr sockfd socket AS INET SOCK STREAM 0 if sockfd 1 return des addr sin family AF INET dest addr sin port htons DEST PORT dest addr sin addr s addr inet addr DEST IP bzero connect sockfd struct sockaddr 注意 一定检查 connect 函数的返回值 如果发生了错误 它将返回 1 全局变量 errno 将会存储错误代码 另外 我们注意到 在上面的代码片段中 我们没有调用 bind 来绑定端口 为什 么 假设我们是客户 在有些时候 作为客户的我们并不在乎本地用什么端口来通信 我 们在乎的是我们连到哪台主机上的哪个个端口上 Linux 内核自动为我俄们选择了一个没 有被使用的端口 listen 函数 函数目标 等待连接 进行系统侦听请求 所需文件头 include 函数原型 int listen int sockfd int backlog 参数说明 sockfd 是一个套接字描述符 由 socket 系统调用获得 backlog 是一个未经过处理的连接请求队列可以容纳的最大数目 Backlog 具体是一些什么意思呢 每一个连接请求都要进行一个连接请求队列 等待 listen 的程序调用 accept 函数来接收这个请求 当系统还有调用 accept 函数的 时候吗如果有很多的连接 那么本地能等待的最大数目就是 backlog 的数值 我们可以将 其设成 5 到 10 之间的数值 推荐 如果一个服务请求到来时 输入队列已满 该套接字 将拒绝连接请求 客户将收到一个出错信息 与其他函数一样 listen 失败将返回 1 全局变量 errno 中存储了错误的代码 说明 listen 函数为申请进入的连接建立输入数据的队列 将到达本地的客 户服务连接请求存在此队列上 知道程序处理他们为止 Listen 函数只应用在 TCP 服务器 通常 一个服务器只能产生一个 监听套接字 并 且这个服务器的生命周期内一直保留 accept 函数 函数目标 接受一个连接请求 所需文件头 include 函数原型 int accept int sockfd void addr int addrlen 参数说明 sockfd 是正在 listen 的一个套接字描述符 addr 一般是指向一个 struct socket in 结

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论