Linux 网络编程修炼指南-内功心法
文章平均质量分 78
Linux 环境编程的延伸,主要对 tcp/ip 详解和 unix 网络编程这两本书进行解读,非常适合初学者。从理论到实践,让你搞清楚网络编程的每一个细节!
--Allen--
我知道我不知道。
展开
-
0-Linux 网络编程修炼指南——内功心法
学习交流群: Linux 学习交流群 610441700说明:本系列文章并不能取代 《UNP》这本旷世之作,文章中难免有错误与不足之处,希望读者们遇到有疑问的地方可以加群互相交流,共同进步。写这一系列文章的目的有三个:一是为了提升自己对 linux 的理解,二是锻炼自己能够把知识点讲清楚,三是希望能更好的帮助基础薄弱的同学能够学习 UNP。在学习 unix 网络编程前,请...原创 2017-04-04 17:09:48 · 19810 阅读 · 51 评论 -
113-给 udp 增加可靠性(一)
先说一些题外话。第 112 篇到这一篇,跨越了半年多时间。并是说真的忙到了没时间写,中间因为毕业,找工作忙活了一阵子,导致写作被中断,后来就不了了之了~重新拾起笔需要很大有勇气。(因为我自己也快忘记了^_^)。不过这半年多时间,博客的阅读量涨到了 20W+ 也算是对我的鼓励,多次想要回归继续写作,却都放弃了,因为网络编程实在是太难了。说起来挺有意思,实际上给 udp 增加可靠性早在 1原创 2018-01-27 23:10:25 · 1803 阅读 · 10 评论 -
114-给 udp 增加可靠性(二)
这一节的目标是完成一个最简单的尽量可靠 UDP 程序,实现的功能如下:客户端给服务端发送数据,服务端收到数据后立即将数据原样发送回去,进行确认。客户端收到服务端的确认后,检查是不是自己刚刚发送出去的数据,如果是表明服务端收到,继续发送一下个。如果客户端在约定的时间(比如 1s 内)没有收到服务端的确认,则重传一次。如果客户端重传 3 次服务端还没有确认,放弃重传,发送下一个数据。原创 2018-01-28 17:54:10 · 1052 阅读 · 3 评论 -
115-并发的 UDP 服务器
TCP 服务并发对我们来说已经不陌生了,你有各种手段处理,比如多进程,多线程,IO 复用 + 单/多线程。但是 UDP 处理并发,如果不仔细思考一下,可能你会觉得这没什么嘛,还不是和 TCP 差不多。1. TCP & UDP 多进程并发这里以多进程 TCP 服务器并发作为参照,看看和 UDP 的区别在哪里。tcp如果你不记得多进程并发模型,还请回去再复习一下《并发服务器(多原创 2018-02-04 13:11:45 · 2312 阅读 · 5 评论 -
112-封装 recvFromFlags
在上一篇博文中,我们使用了 recvmsg 函数来获取标志位,但是每次填充 struct msg 结构体都相当费事,因此我们希望将这个过程封装成一个函数 recvFromFlags,一劳永逸。除了获取标志位之外,我们还希望得到数据包是从哪个接口进来的,以及数据包的目的地址。这个任务需要使用辅助数据结构来完成,如果你不记得辅助数据结构,请参考《辅助数据》。本文所用到的代码托管在 gitos 上:htt原创 2017-06-19 11:39:24 · 1301 阅读 · 1 评论 -
110-获取接口信息(二)
1. 获取接口其它信息前面我们已经了解了 ioctl 的使用方法,现在我们希望获取更多的接口信息,最后将其封装成一个独立函数,名为 getIfiInfo.我们先来看看这个函数的演示示例,后面再给出详细过程。 图1 getIfiInfo 函数使用示例 上面的程序只是获取了接口信息,并将所有接口信息输出到了屏幕。经过编译运行后,结果如图 2 所示。 图2 getIfiInfo 获取到的接口原创 2017-06-09 09:34:55 · 1182 阅读 · 2 评论 -
111-UDP 数据报被截断
1. 数据报被截断有时候我们有这样的需求,在 udp 数据报传输的过程中,如果对方发过来的数据报很大,而我们的接收缓冲区不足以容纳这么大的数据报,怎么办?此时 udp 数据报就会被截断,有没有一种办法能判断数据报是否被截断呢?先来看看 UDP 数据报被截断的行为,可能有下面三种:丢弃超出部分,并向上层返回 MSG_TRUNC 标志。需要使用 recvmsg 来接收这个标志。直接丢弃,不通知保留原创 2017-06-09 11:09:35 · 3909 阅读 · 1 评论 -
108-网络接口
1. 接口网卡,是让计算机通向外部世界的一扇大门。但是在计算机的世界里,一般这扇门称这“接口(Interface)”。接口是物理意义上的设备。在 Linux/Unix 中,我们使用 ifconfig 命令来查看接口信息: 图1 接口信息 接口通常都有自己的属性,比如在图1 中我们可以看到:有自己的名字,比如 ens33, lo, virbr0.有一些标志位,比如 UP, BROADCAS原创 2017-06-08 10:52:10 · 2267 阅读 · 1 评论 -
109-获取接口信息(一)
1. 接口的其他信息上一篇文章简要的介绍了接口的名字和索引号的概念,我们也可以通过一些函数去获取、转换它们。可是,接口除了这些信息外,还有很多其它信息,比如接口上配置的 ip 地址啊,子网掩码啦,MTU 等等。说了这么多,那要怎么才能获取到这些信息呢?有一个类似 fcntl 的函数,叫 ioctl,也是一个垃圾桶函数,通过不同命令来完成不同的功能。它的函数原型如下:#include原创 2017-06-08 21:13:54 · 2141 阅读 · 1 评论 -
106-多播地址
1. 多播地址多播地址,类似“QQ群号”,它是 D 类 IP 地址(224.0.0.0~239.255.255.255)。D 类 IP 地址的首 4 位总是 2 进制 1111 开头,因此,可用的范围就只剩下后 28 位。这 28 位称为 group id——多播组 ID,完整的 32 位称为 group address——组地址。2. 多播地址转换成 mac 地址2.1 思考在单播中,我们知道可以原创 2017-06-04 17:35:46 · 6115 阅读 · 3 评论 -
107-使用多播的 UDP C/S 程序
在掌握了多播的相关基础后,本文我们通过实例来演示如何让你的进程加入多播组。1. 程序路径本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp本文使用的程序路径为 unp/program/multicast/basic.2. 客户端udp 客户端部分无需做任何更改,在指定目标 ip 地址的时候,直接使用多播地址。意思是说,向这个“QQ群”发信原创 2017-06-06 19:34:18 · 2263 阅读 · 1 评论 -
105-多播(基础)
1. 引言多播(或叫组播,Multicast)的出现,正是为了解决广播的不足。我们已经学会用 udp 发广播数据包,然而,对于不想接收广播数据包的主机来说,这就是一种骚扰。比如某主机接收到以太网帧(目的 MAC 地址为 ff:ff:ff:ff:ff:ff),一路沿着协议栈往上解包,到达 UDP 层,根据端口进行分发,但是并没有任何进程在这个端口监听,从而不得不丢弃这个数据包,这无疑是一种对资源的浪费原创 2017-06-03 17:45:46 · 1647 阅读 · 1 评论 -
104-信号引起的竞争错误
1. 引言之前我们学习过使用 alarm 信号这种奇技淫巧来实现带超时的 IO 函数,一直以来,我们写的这种程序都带有一个隐含的 bug.举例来说,我们可能经常会写下面这样的代码:alarm(2);for(;;) { addrlen = sizeof(cliaddr); // 1. 如果信号在 recvfrom 执行前产生 nr = recvfrom(sockfd, buf, 409原创 2017-06-01 20:47:09 · 1450 阅读 · 2 评论 -
103-使用广播的 UDP 回射客户端
讲了好几篇有关广播的理论,是时候实践一下了。这一次,需要将之前写的 udp 回射服务器和客户端拿过来,稍作修改。你可以直接去 unp/program/template 这个模板文件夹下面把 udp 的代码拿过来改。本文使用的程序工具托管在 gitos 上:http://git.oschina.net/ivan_allen/unp模板路径:unp/program/template本文程序路径:un原创 2017-05-31 16:10:05 · 1335 阅读 · 1 评论 -
102-受限广播地址
这种也称之为本地广播地址,它的目标地址为 255.255.255.255. 意思是只在本网络进行广播,绝对不会被路由器转发。1. 规则如果一台主机发送的 IP 数据报目标地址为 255.255.255.255,则相当于向发送者所在的网络发送子网定向广播。路由器从不转发目的地址为 255.255.255.255 的 IP 数据报。这比子网定向广播要容易的多,我们来看下实验。2. 实验2.1 网络拓扑原创 2017-05-26 13:30:10 · 11573 阅读 · 4 评论 -
100-广播
1. 引言在学习前面的 IPv4 地址的时候,你就已经注意到,有些地址被称之为广播地址(主机号全1),还有一类地址称为组播地址(D类地址)。从这一篇开始,我们需要做大量实验来学习广播。听起来很简单,但实际上没那么容易。2. 广播的基本概念按照协议层次来分第 2 层广播,即链路层广播,这种广播不会穿越路由器。因此你会经常听到,路由器隔离广播域。(交换机隔离冲突域。)第 3 层广播,也就是我们要讲的原创 2017-05-25 19:52:36 · 1086 阅读 · 2 评论 -
101-指向子网的广播
我们将用三个实验来观察现象。1. 实验一1.1 网络拓扑图 图1 网络拓扑 1.2 实验步骤在主机 PC1 上 ping 广播地址 192.168.1.255,并在主机 PC1 上抓包。 图2 ping 命令结果 1.2 结果分析 图3 抓包结果 我们希望回答这样一个问题:主机 PC1 自己知道这是一个广播地址吗?答:很明显,PC1 自己知道它是一个广播地址,并且还和自己在同原创 2017-05-25 21:06:13 · 3743 阅读 · 2 评论 -
98-traceroute 程序
traceroute 程序可以让我们看到 IP 数据报从一台主机传到另一台主机所经过的路由,该程序最早由 Van Jacobson 实现。当然我们不可能原汁原味的模仿一遍 traceroute 程序,在这里,我们只需要把最关键的功能实现出来就算完成任务。1. 牛刀小试本文使用的程序工具托管在 gitos 上:http://git.oschina.net/ivan_allen/unp1.1 仿真环境测原创 2017-05-23 12:12:13 · 1643 阅读 · 2 评论 -
99-IPv4 地址
很久以前我们非常简单的讨论过 《IPv4 地址》,那时候是出于网络编程基础的需要,介绍了和 IP 地址相关的函数。这一次,我们来详细的讨论下 IP 地址的结构。1. IPv4 地址分类1981 年,标准就制定了一种基本的编址方法。32 位的 IP 地址由 {netid, hostid} 两部分构成,根据这个规则,依据 netid 将 IP 地址分成 5 大类: 图1 五类 IP 地址 比如原创 2017-05-23 17:10:20 · 2876 阅读 · 5 评论 -
96-ICMP 协议(时间戳请求与应答)
ICMP 时间戳请求允许系统向另一个系统查询当前的时间。1. ICMP 时间戳1.1 首部格式 图1 ICMP 时间戳请求与应答报文 它的 type 字段为 17(请求)或 18(应答),code 字段为 0.发起时间戳:发送者的发送时间,由发送者填写接收时间戳:接收者的接收时间,由接收者填写发送时间戳:接收者的发送时间,由接收者填写通常,接收时间戳等于发送时间戳。可以理解为接收者接原创 2017-05-21 18:10:19 · 13097 阅读 · 7 评论 -
97-ICMP 协议(端口不可达)
ICMP 端口不可达是差错报文中的一种,它的类型(type)是 3,代码(code)也是 3.1. 差错报文首部格式 图1 差错报文首部 2. 实验打开我们之前写的 ICMP 报文接收程序程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp如果你已经 clone 过这个代码了,请使用 git pull 更...原创 2017-05-23 10:38:29 · 14031 阅读 · 2 评论 -
94-ICMP 协议(回显请求与应答)
当 ICMP 首部 type = 8, code = 0,该 ICMP 是回显请求报文。当 type = 0, code = 0 时,是回显应答报文。1. 回显请求与应答报文1.1 首部格式 图1 ICMP 回显请求与应答报文首部 当 ICMP 报文是回显请求与应答报文时,我们可以看到首部的第 4、5 两个字节是标识符字段,第 6、7 两个字节是序号字段。1.2 结构体该结构体定义在 unp原创 2017-05-18 15:01:26 · 15197 阅读 · 6 评论 -
95-PING 命令实现
学完了 ICMP 回显请求与应答报文,就可以用它来实现我们平时使用的 PING 命令了。先来看看效果: 图1 自带的 ping 命令与我们自己实现的 1. 程序路径本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/prog原创 2017-05-19 14:34:14 · 1563 阅读 · 2 评论 -
92-ICMP 协议(基础)
ICMP 协议(Internet Control Messages Protocol,网际控制报文协议)是网络层最重要的协议之一。前面的实验我们也看到了,它由 IP 协议承载,封装在 IP 协议的数据部分。 图1 ICMP 被封装在 IP 数据报的数据部分 1. ICMP 报文的作用网络上经常会出现一些错误,比如目标主机不存在,目标端口不存在什么的,那么你要怎么知道呢?ICMP 的功能之一就原创 2017-05-17 17:35:40 · 1585 阅读 · 2 评论 -
93-接收 ICMP 报文
如果你练习过前面的接收 IP 数据报的程序,相信写出这个不会很难。1. 程序路径本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/icmp/basic.2. 程序编写思路因为这一次我们只想要 ICMP 协议,因此可原创 2017-05-17 20:29:56 · 1683 阅读 · 5 评论 -
90-IP 协议(基础)
IP 协议是 TCP/IP 协议族中最核心的协议,TCP、UDP、ICMP 等众多协议需要依赖它工作。1. IP 首部1.1 IP 首部格式 图1 IP 首部 IP 首部相比 TCP 首部要简单的多。因为 TCP 是可靠协议,所以需要更多的字段。1.2 IP 首部结构体该结构体定义在 netinet/ip.h 中。不过为了方便学习,我将该结构体定义在了 unp/include/ip.h,写程原创 2017-05-16 16:04:20 · 1483 阅读 · 2 评论 -
91-接收 IP 数据报
Raw Sockets,原始套接字。它出现的理由很简单,我们可以自己构造一个完整的 IP 数据报,通过原始套接字发送出去。也可以从原始套接字中读取一个完整的 IP 数据报。1. 创建原始套接字// 创建一个 IPv4 原始套接字sockfd = socket(AF_INET, SOCK_RAW, protocol); 注意:只有 root 权限的用户才能创建原始套接字第三个参数 protoc原创 2017-05-16 20:13:00 · 1586 阅读 · 2 评论 -
88-非阻塞 connect 版本的 web 客户程序
1. web 客户程序目标:1) web 客户程序建立与某个 web 服务器的 HTTP 连接,然后获取主页(这只是一个开胃菜,测试我们的 web 程序是否正常工作)。2) 同时请求多个网络资源,并下载。(看起来很像多线程爬虫,但是我们不使用多线程,而是非阻塞 connect)我们的 web 客户程序命名为就命名为 web,使用方法如下:$ ./web <同时允许最大连接数> <主页> <主页原创 2017-05-14 15:34:38 · 1199 阅读 · 3 评论 -
89-非阻塞 accept
不知道你是否还记得异常连接—— accept 返回前连接终止。这篇文章探讨的是在 accept 函数调用前,连接被异常终止的情况。不过,很遗憾我们并未观察到 accept 产生异常。man 手册中解释,linux 会把错误转移到 accept 返回的新套接字(已连接套接字)中,因此在后续的 read 已连接套接字的时候,会产生错误。假设我考虑最坏的情况,即 accept 会产生错误,会怎样呢?1.原创 2017-05-14 18:17:15 · 1385 阅读 · 2 评论 -
86-时间获取客户端
本文来一点比较轻松的话题,来写一个客户端,从时间服务器(daytime server) 上取得时间并打印。1. daytime 服务器daytime 服务器我们不用自己写了,这个网站列举了很多现成的 daytime 服务器地址:http://tf.nist.gov/tf-cgi/servers.cgi , 剩下的事情我们就只管写客户端。daytime 服务器使用的是 TCP 协议,默认端口是 13。原创 2017-05-10 12:40:36 · 1322 阅读 · 2 评论 -
87-非阻塞 connect
非阻塞i/o 上调用 connect 比非阻塞 i/o 上调用 read/write 要麻烦一点,一方面 connect 函数不能像 read/write 那样反复调用,它只能调用一次;另一方面,connect 函数返回错误,并不代表连接建立不成功。1. 非阻塞 connect对于 TCP 协议,在非阻塞 i/o 上调用 connect,意味着 connect 会发送 SYN 段给服务...原创 2017-05-12 10:33:18 · 1195 阅读 · 5 评论 -
84-使用非阻塞 I/O 改写回射客户端
这个程序,应该是相当复杂的。读完它需要一些耐心,不过我会力求突显程序的结构,删除无关的代码。1. 回顾旧程序旧版本程序的结构如下:while(1) { rfds = {stdin, sockfd}; select(rfds); if (stdin in rfds) { read(stdin); // 风险代码,可能产生阻塞 writen(sockfd); }原创 2017-05-08 19:58:07 · 1434 阅读 · 2 评论 -
85-使用多线程改写回射客户端
上一篇文章中,我们已经见识了非阻塞I/O + 缓冲区改写回射客户端的复杂性。本文我们使用多线程来重写客户端,后面可以看到,这个版本要简洁的多。1. 程序路径本文使用的程序托管在 gitos 上,你可以使用下面的命令 clone 到你的电脑上:git clone https://git.oschina.net/ivan_allen/unp.git如果你已经 clone 过这个代码了,请使用 git p原创 2017-05-09 10:58:32 · 930 阅读 · 2 评论 -
82-再议 select 版回射客户端
第一次,我们写的服务器客户端是停等版本,像下面这样:while(1) { read(stdin); writen(sockfd); read(sockfd); writen(stdout);}后来,我们用 select 改进了它:while(1) { rfds = {stdin, sockfd}; select(rfds); if (stdin in rfds) {原创 2017-05-08 13:46:11 · 1036 阅读 · 1 评论 -
83-非阻塞 I/O
回顾一下上一篇文章,阻塞版本的 writen 固然好用,但是也有风险,我们不能把希望寄托在增加客户端 TCP 发送缓冲区和接收缓冲区的大小上,而是应该采取更加有保障的措施。前面说过,可以使用非阻塞 IO,也可以使用多进程或多线程。本文,我们先来讨论一下非阻塞 I/O 的特性。1. 概述可能产生阻塞的套接字调用分成四类:1) 输入操作,包括 read, readv, recv, recvfrom,原创 2017-05-08 16:11:35 · 1060 阅读 · 3 评论 -
80-进程间传递描述符(策略)
万事俱备,只欠东风。现在就差如何进行程序结构设计了。我们已经知道,可以通过辅助数据传递描述符了,那么接下来怎么做?1. 目标我们的目标是让进程 fork 一个子进程,子进程继承 unix 域套接字。子进程打开某个文件,然后将该文件描述符通过 sendmsg 发送给父进程。很好,这没什么难度。程序结构大概就如下:int main() { /****************** 封装成 myOpen原创 2017-05-05 15:50:25 · 1446 阅读 · 2 评论 -
81-凭证的发送与接收
使用 Unix 域套接字作为辅助数据传递的另一种数据是用户凭证(user credential)。FreeBSD 使用 struct cmsgcred 结构来传递凭证,此时辅助数据的 type 类型是 SCM_CREDS.不过,我们讨论的是 Linux,不同于 FreeBSD,在 Linux 中使用 struct ucred 结构传递凭证,辅助数据的 type 类型是 SCM_CREDENTIALS原创 2017-05-06 15:45:22 · 1446 阅读 · 1 评论 -
78-socketpair 函数
如果涉及到父子进程间的通信,我们就没有必要弄的那么麻烦,不需要再创建套接字地址,不需要绑定。函数 socketpair 会创建两个连接好的套接字。1. socketpair 函数int socketpair(int family, int protocol, int sockfd[2]);family 参数只能是 AF_LOCAL 或 AF_UNIX,protocol 只能是 0. type 参数原创 2017-05-05 10:59:14 · 1425 阅读 · 2 评论 -
79-辅助数据
Unix 域协议这一章的第一篇文章就是讨论如何在进程间传递描述符,可是后面似乎我们把这件事忘了。其实不然,我们一直在为这件事做铺垫,本文将进一步逼近“真相”。辅助数据 (Ancillary) ,也叫控制数据,实际上在前面讲解 recvmsg 和 sendmsg 时提过一嘴,当时只是说先放一放。1. 回忆 msghdr{}还记得吧,这个结构体是 recvmsg 和 sendmsg 函数的第二个参数类型原创 2017-05-05 13:45:15 · 2096 阅读 · 1 评论 -
76-抽象 unix 域套接字地址
1. 普通 VS 抽象前面我们已经学习过了 sockaddr_un 结构,它有一个成员 sun_path,通常它保存的是一个以'\0'为结尾的绝对路径。一旦绑定了一个绝对路径,就会生成一个文件。这种 unix 域套接字地址我们称为普通 unix 域套接字地址。还有一种 unix 域套接字地址,它的特征是 sun_path[0] 是 null,即 sun_path[0] == '\0'. 这种 uni原创 2017-05-04 19:46:35 · 2218 阅读 · 6 评论