1 网络基础概念
1.1 知识点
网络适配器 :
- 作用 收发数据mac地址
- 作用: 用来标识一块网卡 6个字节 物理地址()
1.1.2 ip
- ip用来标识一台主机 逻辑地址
- iPv4 : ip地址是4字节 32位
- ipv6: 128位 16字节
- 子网id ip中被子网掩码中1连续覆盖的位
- 主机id ip中被子网掩码中0连续覆盖的位
- 192.168.1.2/24 192.168.1.2/255.255.255.0()
- 网段地址: 192.168.1.0广播地址: 192.168.1.255子网掩码
- netmask: 用来区分子网id和主机id()
1.1.3 端口
- 作用: 用来标识应用程序(进程)
- port: 2个字节 0-65535
- 0-1023 知名端口(已被占用)
- 自定义端口 1024 - 65535
- netstat
- 一个应用程序可以有多个端口,一个端口只能用于一个应用程序
1.1.4分层模型
OSI 七层模型
-
物理层:主要定义物理设备标准,如网线(双绞线)的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流这一层的数据叫做比特。
-
数据链路层:定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。(MAC负责收发数据)
-
网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择。
-
传输层:定义了一些传输数据的协议和端口号(port区分数据传递到哪一个应用程序),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。
-
会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。
-
表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取(解码)。
-
应用层:是最靠近用户的OSI层。这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务。
TCP/IP四层模型
TCP/IP网络协议栈分为
- 应用层(Application)
- 传输层(Transport)
- 网络层(Network)
- 链路层(Link)
1.1.5 协议的概念
规定了数据传输的方式和格式
典型协议:
- 应用层协议: FTP: 文件传输协议 HTTP: 超文本传输协议 NFS: 网络文件系统
- 传输层协议: TCP: 传输控制协议 UDP: 用户数据报协议
- 网络层: IP:英特网互联协议 ICMP: 英特网控制报文协议ping IGMP: 英特网组管理协议
- 链路层协议: ARP: 地址解析协议 (通过ip找mac地址) RARP: 反向地址解析协议 (通过mac找ip)
- 地址解析协议: 通过ip找mac地址arp请求包
1.1.6 网络应用程序设计模式
网络设计模式
- B/S模式 browser/ server
- 浏览器()/服务器(server)模式。只需在一端部署服务器,而另外一端使用每台PC都默认配置的浏览器即可完成数据的传输。
- 优点:c/s 性能较好 客户端容易篡改数据 开发周期较长
- C/S cilent/server
- 传统的网络应用设计模式,客户机(client)/服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。
- 优点:b/s 性能低 客户端安全 开发周期短
1.2网络名词术语解析
1.2.1路由(route)
- 路由(名词):数据包从源地址到目的地址所经过的路径,由一系列路由节点组成。
- 路由(动词):某个路由节点为数据包选择投递方向的选路过程。
1.2.2路由器工作原理
路由器(Router)是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号的设备。传统地,路由器工作于OSI七层协议中的第三层,其主要任务是接收来自一个网络接口的数据包,根据其中所含的目的地址,决定转发到下一个目的地址。
1.2.3路由表(Routing Table)
在计算机网络中,路由表或称路由择域信息库(RIB)是一个存储在路由器或者联网计算机中的电子表格(文件)或类数据库。路由表存储着指向特定网络地址的路径。
1.2.4路由条目
路由表中的一行,每个条目主要由目的网络地址、子网掩码、下一跳地址、发送接口四部分组成,如果要发送的数据包的目的网络地址匹配路由表中的某一行,就按规定的接口发送到下一跳地址。
1.2.5缺省路由条目 路由表中的最后一行,主要由下一跳地址和发送接口两部分组成,当目的地址与路由表中其它行都不匹配时,就按缺省路由条目规定的接口发送到下一跳地址。
1.2.6路由节点
一个具有路由能力的主机或路由器,它维护一张路由表,通过查询路由表来决定向哪个接口发送数据包。
1.2.7以太网交换机工作原理
以太网交换机是基于以太网传输数据的交换机,以太网采用共享总线型传输媒体方式的局域网。以太网交换机的结构是每个端口都直接与主机相连,并且一般都工作在全双工方式。交换机能同时连通许多对端口,使每一对相互通信的主机都能像独占通信媒体那样,进行无冲突地传输数据。以太网交换机工作于OSI网络参考模型的第二层(即数据链路层),是一种基于MAC(Media Access Control,介质访问控制)地址识别、完成以太网数据帧转发的网络设备。
1.2.8hub工作原理
集线器实际上就是中继器的一种,其区别仅在于集线器能够提供更多的端口服务,所以集线器又叫多口中继器。集线器功能是随机选出某一端口的设备,并让它独占全部带宽,与集线器的上联设备(交换机、路由器或服务器等)进行通信。从Hub的工作方式可以看出,它在网络中只起到信号放大和重发作用,其目的是扩大网络的传输范围,而不具备信号的定向传送能力,是—个标准的共享式设备。其次是Hub只与它的上联设备(如上层Hub、交换机或服务器)进行通信,同层的各端口之间不会直接进行通信,而是通过上联设备再将信息广播到所有端口上。由此可见,即使是在同一Hub的不同两个端口之间进行通信,都必须要经过两步操作:
- 第一步是将信息上传到上联设备;
- 第二步是上联设备再将该信息广播到所有端口上。
1.2.9半双工/全双工
- Full-duplex(全双工)全双工是在通道中同时双向数据传输的能力。
- Half-duplex(半双工)在通道中同时只能沿着一个方向传输数据。
1.2.10DNS服务器
DNS 是域名系统 (Domain Name System) 的缩写,是因特网的一项核心服务,它作为可以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的IP地址串。它是由解析器以及域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器。
1.2.11局域网(LAN)
localarea network,一种覆盖一座或几座大楼、一个校园或者一个厂区等地理区域的小范围的计算机网。
- 覆盖的地理范围较小,只在一个相对独立的局部范围内联,如一座或集中的建筑群内。
- 使用专门铺设的传输介质进行联网,数据传输速率高(10Mb/s~10Gb/s)
- 通信延迟时间短,可靠性较高
- 局域网可以支持多种传输介质
1.2.12广域网(WAN)
wide areanetwork,一种用来实现不同地区的局域网或城域网的互连,可提供不同地区、城市和国家之间的计算机通信的远程计算机网。覆盖的范围比局域网(LAN)和城域网(MAN)都广。广域网的通信子网主要使用分组交换技术。广域网的通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网,它将分布在不同地区的局域网或计算机系统互连起来,达到资源共享的目的。如互联网是世界范围内最大的广域网。
- 适应大容量与突发性通信的要求;
- 适应综合业务服务的要求;
- 开放的设备接口与规范化的协议;
- 完善的通信服务与网络管理。
1.2.13端口
逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。
- 端口号小于256的定义为常用端口,服务器一般都是通过常用端口号来识别的。
- 客户端只需保证该端口号在本机上是惟一的就可以了。客户端口号因存在时间很短暂又称临时端口号;
- 大多数TCP/IP实现给临时端口号分配1024—5000之间的端口号。大于5000的端口号是为其他服务器预留的。 我们应该在自定义端口时,避免使用well-known的端口。如:80、21等等。
1.2.14MTU
MTU:通信术语最大传输单元(Maximum Transmission Unit,MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传输单元这个参数通常与通信接口有关(网络接口卡、串口等)。
以下是一些协议的MTU:
- FDDI协议:4352字节
- 以太网(Ethernet)协议:1500字节
- PPPoE(ADSL)协议:1492字节
- X.25协议(Dial Up/Modem):576字节
- Point-to-Point:4470字节
1.3 TCP 协议
1.3.1TCP通讯时序
(三次握手四次挥手)
TCP通信时序
下图是一次TCP通讯的时序图。TCP连接建立断开。包含大家熟知的三次握手和四次握手。mss(Maximum Segment Size,最大报文长度)
建立连接(三次握手)的过程:
- 客户端发送一个带SYN标志的TCP报文到服务器, 这是三次握手过程中的段1
- 服务器端回应客户端,是三次握手中的第2个报文段,同时带ACK标志和SYN标志。ACK它表示对刚才客户端SYN的回应;同时又发送SYN给客户端,询问客户端是否准备好进行数据通讯。
- 客户必须再次回应服务器端一个ACK报文,这是报文段3。
seq---序列号
- 报文的序号是多少,随机的,
ack---确认序列号
- 确认序列号的含义:1.确认收到对方的报文 2.期待下一次对方的序列号为我的确认序列号
- 确认序列号 = 对方发送过来的序列号+标志位长度SYN(1)+数据长度
数据传输过程:
- 客户端发出段4,包含从序号1001开始的20个字节数据。
- 服务器发出段5,确认序号为1021,对序号为1001-1020的数据表示确认收到,同时请求发送序号1021开始的数据,服务器在应答的同时也向客户端发送从序号8001开始的10个字节数据,这称为piggyback。
- 客户端发出段6,对服务器发来的序号为8001-8010的数据表示确认收到,请求发送序号8011开始的数据。
关闭连接(四次握手)的过程:
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
-
客户端发出段7,FIN位表示关闭连接的请求。
-
服务器发出段8,应答客户端的关闭连接请求。
-
服务器发出段9,其中也包含FIN位,向客户端发送关闭连接请求。
-
客户端发出段10,应答服务器的关闭连接请求。
建立连接的过程是三方握手,而关闭连接通常需要4个段,服务器的应答和关闭连接请求通常不合并在一个段中,因为有连接半关闭的情况,这种情况下客户端关闭连接之后就不能再发送数据给服务器了,但是服务器还可以发送数据给客户端,直到服务器也关闭连接为止。
1.3.2滑动窗口 (TCP流量控制)
介绍UDP时我们描述了这样的问题:如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据。TCP协议通过“滑动窗口(Sliding Window)”机制解决这一问题。
每一次读取数据之后,回ack报文,报文中会携带当前缓存区大小,用来告知对方
1.3.3TCP状态转换
CLOSED:表示初始状态。
LISTEN:该状态表示服务器端的某个SOCKET处于监听状态,可以接受连接。
SYN_SENT:这个状态与SYN_RCVD遥相呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,随即进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
SYN_RCVD: 该状态表示接收到SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂。此种状态时,当收到客户端的ACK报文后,会进入到ESTABLISHED状态。
ESTABLISHED:表示连接已经建立。
FIN_WAIT_1:
FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。
区别是:
FIN_WAIT_1状态是当socket在ESTABLISHED状态时,想主动关闭连接,向对方发送了FIN报文,此时该socket进入到FIN_WAIT_1状态。
FIN_WAIT_2状态是当对方回应ACK后,该socket进入到FIN_WAIT_2状态,正常情况下,对方应马上回应ACK报文,所以FIN_WAIT_1状态一般较难见到,而FIN_WAIT_2状态可用netstat看到。
FIN_WAIT_2:主动关闭链接的一方,发出FIN收到ACK以后进入该状态。称之为半连接或半关闭状态。该状态下的socket只能接收数据,不能发。
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,等2MSL后即可回到CLOSED可用状态。如果FIN_WAIT_1状态下,收到对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态较特殊,属于一种较罕见的状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
CLOSE_WAIT: 此种状态表示在等待关闭。当对方关闭一个SOCKET后发送FIN报文给自己,系统会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,察看是否还有数据发送给对方,如果没有可以 close这个SOCKET,发送FIN报文给对方,即关闭连接。所以在CLOSE_WAIT状态下,需要关闭连接。
LAST_ACK: 该状态是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,即可以进入到CLOSED可用状态。
1.3.4 半关闭 当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。 (主动方发生FIN_WAIT_2状态时,主动方不可以在应用层发送数据了,但是应用层还可以接收数据)
从程序的角度,可以使用API来控制实现半连接状态。
#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how:允许为shutdown操作选择以下几种方式:
SHUT_RD(0):关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
SHUT_WR(1): 关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。
SHUT_RDWR(2): 关闭sockfd的读写功能。相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR。使用close中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用计数为0时才关闭连接。
shutdown不考虑描述符的引用计数,直接关闭描述符。也可选择中止一个方向的连接,只中止读或只中止写。
注意:
- 如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。
- 在多进程中如果一个进程调用了shutdown(sfd,SHUT_RDWR)后,其它的进程将无法进行通信。但,如果一个进程close(sfd)将不会影响到其它进程。
1.3.5 2MSL 2MSL (MaximumSegment Lifetime) TIME_WAIT状态的存在有两个理由:
-
让4次握手关闭流程更加可靠;4次握手的最后一个ACK是是由主动关闭方发送出去的,若这个ACK丢失,被动关闭方会再次发一个FIN过来。若主动关闭方能够保持一个2MSL的TIME_WAIT状态,则有更大的机会让丢失的ACK被再次发送出去。
-
防止lost duplicate对后续新建正常链接的传输造成破坏。
RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等待
端口复用 在server的TCP连接没有完全断开之前不允许重新监听是不合理的。因为,TCP连接没有完全断开指的是connfd(127.0.0.1:6666)没有完全断开,而我们重新监听的是lis-tenfd(0.0.0.0:6666),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。
在server代码的socket()和bind()调用之间插入如下代码:
int opt = 1;
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
有关setsockopt可以设置的其它选项请参考UNP第7章。
1.3.6TCP异常断开
- 心跳包:最小粒度
- 乒乓包: 携带比较多的数据的心跳包
心跳检测机制 在TCP网络通信中,经常会出现客户端和服务器之间的非正常断开,需要实时检测查询链接状态。常用的解决方法就是在程序中加入心跳机制。
Heart-Beat线程 这个是最常用的简单方法。在接收和发送数据时个人设计一个守护进程(线程),定时发送Heart-Beat包,客户端/服务器收到该小包后,立刻返回相应的包即可检测对方是否实时在线。该方法的好处是通用,但缺点就是会改变现有的通讯协议!大家一般都是使用业务层心跳来处理,主要是灵活可控。UNIX网络编程不推荐使用SO_KEEPALIVE来做心跳检测,还是在业务层以心跳包做检测比较好,也方便控制。
设置TCP属性 SO_KEEPALIVE保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况:对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接口本身则被关闭。对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟 15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置EHOSTUNREACH。
keepAlive =1;
setsockopt(listenfd,SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));