C++面试总结之网络(二)

1.TCP中time_wait作用

主动关闭的Socket端会进入TIME_WAIT状态,并且持续2MSL时间长度,MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒,因而,TIME_WAIT状态一般维持在1-4分钟。

TIME_WAIT状态存在的理由:

(1)可靠地实现TCP全双工连接的终止

在进行关闭连接四路握手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状态信息进入TIME_WAIT状态。

(2)允许老的重复分节在网络中消逝

TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身 (incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

如果在大并发的短链接下,TIME_WAIT 太多会消耗很多系统资源。处理方式都是设置两个参数,tcp_tw_reuse,tcp_tw_recycle,这两个参数默认值都是被关闭的,如果使用tcp_tw_reuse,必需设置tcp_timestamps=1,否则无效。这里,你一定要注意,打开这两个参数可能会让TCP连接出一些诡异的问题(如果不等待超时重用连接的话,新的连接可能会建不上。)。

tcp_max_tw_buckets。这个是控制并发的TIME_WAIT的数量,默认值是180000,如果超限,那么,系统会把多的给destory掉,然后在日志里打一个警告(如:time wait bucket table overflow),官网文档说这个参数是用来对抗DDoS攻击的。

2.TCP与UDP编程区别

(1)TCP面向连接(三次握手机制),通信前需要先建立连接;UDP面向无连接,通信前不需要建立连接;

(2)TCP保障可靠传输(按序、无差错、不丢失、不重复);UDP不保障可靠传输,使用最大努力交付;

(3)TCP面向字节流的传输,UDP面向数据报的传输。

3.两个文件,每个文件中有50亿URL,找出两个文件中相同的URL

Hash+hash

可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。

遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999)中。这样每个小文件的大约为300M。

遍历文件b,采取和a相同的方式将url分别存储到1000小文件(记为b0,b1,...,b999)。这样处理后,所有可能相同的url都在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。

求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

4.tcp syn丢包,重连的时间

建立连接时SYN超时:如果server端接到了clien发的SYN后回了SYN-ACK后client掉线了,server端没有收到client回来的ACK,那么,这个连接处于一个中间状态,即没成功,也没失败。于是,server端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻售,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才会把断开这个连接。

SYN Flood攻击。一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后,就下线了,于是服务器需要默认等63s才会断开连接,这样,攻击者就可以把服务器的syn连接的队列耗尽,让正常的连接请求不能处理。于是,Linux下给了一个叫tcp_syncookies的参数来应对这个事——当SYN队列满了后,TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去(又叫cookie),如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie发回来,然后服务端可以通过cookie建连接(即使你不在SYN队列中)。请注意,请先千万别用tcp_syncookies来处理正常的大负载的连接的情况。因为,synccookies是妥协版的TCP协议,并不严谨。对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是:tcp_synack_retries 可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大SYN连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。

5.socket编程相关,如果服务器这边调用write写了100个字节的数据,客户端想要获得这个数据,是直接用read系统调用,参数也是100吗?

不是。对于read 调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会返回 20;对于 write调用,如果请求写 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会阻塞,直到把 100个字节全部交给发送缓冲区才返回。但如果 socket文件描述符有 O_NONBLOCK标志,则 write不阻塞,直接返回 20;此时非阻塞地read 也直接返回20。

6.TCP/IP协议栈各个层次及功能

层次名称

功     能

协     议

应用层

负责实现一切与应用程序相关的功能,对应 OSI参考模型的上三层

FTP(文件传输协议)HTTP(超文本传输协议)

DNS(域名服务器协议)SMTP(简单邮件传输协议)

NFS(网络文件系统协议)

传输层

负责提供可靠的传输服务,对应 OSI参考模型的第四层

TCP(控制传输协议)

UDP(用户数据报协议)

网际层

负责网络间的寻址、数据传输,对应 OSI参考模型的第三层

IP(网际协议)ICMP(网际控制消息协议)

ARP(地址解析协议)RARP(反向地址解析协议)

网络接口

负责实际数据的传输,对应 OSI参考模型的下两层

HDLC(高级链路控制协议)

PPP(点对点协议)SLIP(串行线路接口协议)

7. connect方法会阻塞,如何避免其长时间阻塞?

(1)考虑采用异步传输机制,同步传输与异步传输的主要区别在于同步传输中,如果调用recvfrom(使用该函数接收数据)后会一致阻塞运行,从而导致调用线程暂停运行;异步传输机制则不然,会立即返回。

(2)当在一个非阻塞的TCP套接字上调用connect时,connect将立即返回一个EINPROGRESS错误,不过已经发起的TCP三路握手继续进行。然后用select检测连接是否成功。给select设置超时值,就可以避免长时间阻塞。

非阻塞connect有三个用途:

(1)在三路握手期间,可以做其他操作。

(2)同时建立多个连接。这个用途已随着Web浏览器流行起来。

(3)缩短connect的超时,避免长时间阻塞。

select+非阻塞connect:两个规则:

(1)当连接成功建立时,描述符可写

(2)当连接建立遇到错误时,描述符变为既可读又可写。  

所以可以用getsockopt查看是否connect出错。

8. 网络中客户端突然掉线或者重启,服务器端怎么样才能立刻知道?

如果server用select,客户重启,进程结束时发送FIN,server可读,读取长度为0 。

如果客户端突然掉线,不会发送FIN,服务器需要主动发送数据才能知道。

9心跳

(1)当TCP两端A、B建立了连接后,如果一端拔掉网线或者拔掉电源,那么另一端能够收到通知吗? 答案是不会。

原因:

TCP是一种有连接的协议,但是这个连接并不是指有一条实际的电路,而是一种虚拟的电路。TCP的建立连接和断开连接都是通过发送数据实现的,也就是我们常说的三次握手、四次挥手。TCP两端保存了一种数据的状态,就代表这种连接,TCP两端之间的路由设备只是将数据转发到目的地,并不知道这些数据实际代表了什么含义,也并没有在其中保存任何的状态信息,也就是说中间的路由设备没有什么连接的概念,只是将数据转发到目的地,只有数据的发送者和接受者两端真正的知道传输的数据代表着一条连接。

但是这就说明了一点,如果不发送数据那么是无法断开连接的。正常情况下当TCP的一端A调用了SOCKET的close或者进程结束,操作系统就会按照TCP协议发送FIN数据报文。B端收到后就会断开连接。但是当出现了上文所说的异常情况时:被拔掉网线或者断掉电源,总结起来就是没有机会发出断开的FIN数据报文。那么和A直连的路由设备虽然知道A设备已经断开了,但是路由设备并没有保存连接的状态信息,所以路由设备也就不可能去通知B端A端的断开。而B端没有收到断开的数据报文就会依然保持连接。所以A端拔掉网线或者断掉电源后B端是没办法收到断开连接的通知的。

解决方案:

保持连接并不是毫无代价的,如果这种异常断开的连接有很多,那么势必会耗费大量的资源,必须要想办法检测出这种异常连接。 

检测的方法很简单,只要让B端主动通过这个连接向A端继续发送数据即可。上文说过,A端异常断开后,和A端直接连接的路由器是知道的。当B端发送的数据经过转发后到达这个路由器后,必然最终会返回B端一个目的不可达。此时B端立刻就会知道这条连接其实已经异常断开了。 

但是B端不可能知道什么时候会出现这种异常,所以B端必须定时发送数据来检测连接是否异常断开。数据的内容无关紧要,任何数据都能达到这个效果。这个数据就是我们经常在TCP编程中所说的心跳。

(2) KEEP_ALIVE(TCP本身提供的保活机制)

TCP协议本身就提供了一种这样的机制来探测对端的存活。TCP协议有一个KEEP_LIVE开关,只要打开这个开关就会定时发送一些数据长度为零的探测心跳包,发送的频率和次数都可以设置,具体的方法在网上搜索tcp keepalive即可,网上有很多文章,这里不再赘述。

(3)应用层心跳(主动发送心跳数据包)

区别:

A. 应用层的心跳数据包会耗费更多的带宽,因为TCP协议的保活机制发送的是数据长度为零心跳包,而应用层的心跳数据包长度则必然会大于0。因此,由TCP协议提供的检活, 其发的探测包, 理论上实现的会更精妙(用更少的字节完成更多的目标), 耗费更少的流量.

B. 由应用自己实现的应用层的心跳, 为心跳消息额外定义一个消息类型就可以了. 就是应用正常的消息包, 只是这个包特殊点, 专门用来检活而已, 通常比较小。应用层的心跳数据包可以带一些应用所需要的数据,随应用自己控制,而TCP协议的保活机制则是对于应用层透明的,无法利用心跳携带数据。

应用层心跳的好处有两点: 

一是比较灵活, 因为协议层的心跳只能提供最纯粹的检活功能, 但是应用层自己可以随意控制, 包括协议可能提供的是秒级的, 但是你想做成毫秒级的都任意(虽然实际几乎不会有这种时间级别的心跳), 包里还甚至可以携带额外的信息, 这些都是灵活之处.

二是通用, 应用层的心跳不依赖协议. 如果有一天不用TCP要改为UDP了, 协议层不提供心跳机制了, 但是你应用层的心跳依旧是通用的, 可能只需要做少许改动就可以继续使用.

应用层心跳的不好的地方也很显而易见, 增加开发工作量, 由于应用特定的网络框架, 还可能很增加代码结构的复杂度.

(4)双向心跳

那么是否只是一端向另一端发送心跳就行了呢?显然不行。因为两端都有可能发生异常断开的情况。所以TCP连接的两端必须都向对端发送心跳。

10.TTL是什么?有什么用处,通常那些工具会用到它?

(ping? traceroute? ifconfig? netstat?)

TTL(time to live)生存时间字段(8位,在IP首部)设置了ip数据报可以经过的最多路由器数。TTL的初始值由主机设置,一旦经过一个处理它的路由器,它的值减去1。当该字段的值为0时,数据报就被丢弃,并发送ICMP报文通知源主机。

TTL字段的用处:目的是防止数据报在选路时无休止地在网络中流动。(例如,当路由器瘫痪或者两个路由器之间连接丢失,选路协议有时会检测丢失的路由并一直进行下去。)

Traceroute会用到TTL,当路由器接收IP数据报中的TTL是1或者0,不转发,丢弃后给信源发ICMP“超时”信息。包含这份ICMP信息的IP报文的信源地址是该路由器的IP地址。当Traceroute发送TTL为1的IP数据报,得到第一个路由器的地址,发送TTL为2则得到第二个路由器地址,最后IP数据报到达目的主机时,由于Tracerout发送的UDP数据报中的端口选择了一个不可能的值,使得目的主机的UDP模块产生一份“端口不可达”错误的ICMP报文,Tracerout收到后结束。

Ping: TCP/IP协议,发送ICMP报文

Traceroute:查看路由路径

Ifconfig:用于配置和显示Linux内核中网络接口的网络参数。用ifconfig命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。

Netstat:netstat命令用来打印Linux中网络系统的状态信息,可让你得知整个Linux系统的网络情况。

Arp:查看或修改IP到MAC转换表

11.路由表的作用?linux中怎么来配置一条默认路由?

路由表是用来决定如何将包从一个子网传送到另一个子网的。在Linux上可以用 “route add default gw <默认路由器IP>”来配置一条默认路由。

route命令用来显示并设置Linux内核中的网络路由表,route命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。

在Linux系统中设置路由通常是为了解决以下问题:该Linux系统在一个局域网中,局域网中有一个网关,能够让机器访问Internet,那么就需要将这台机器的ip地址设置为Linux机器的默认路由。要注意的是,直接在命令行下执行route命令来添加路由,不会永久保存,当网卡重启或者机器重启之后,该路由就失效了;可以在/etc/rc.local中添加route命令来保证该路由设置永久有效。

显示当前路由:route 

添加网关/设置网关:
route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0    //增加一条到达244.0.0.0的路由。

屏蔽一条路由:
route add -net 224.0.0.0 netmask 240.0.0.0 reject     //增加一条屏蔽的路由,目的地址为224.x.x.x将被拒绝。

删除路由记录:
route del -net 224.0.0.0 netmask 240.0.0.0
route del -net 224.0.0.0 netmask 240.0.0.0 reject

删除和添加设置默认网关:
route del default gw 192.168.120.240
route add default gw 192.168.120.240

12.在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排除故障?

从一台机器ping另一台机器,测试是否连通。

若ping不通,用traceroute可以确定是哪个路由器不能连通,然后再找问题是在交换设备、hup、cable等。

13.deamon进程如何实现?

关于deamon进程:deamon进程,也就是守护进程,是在UNIX后台运行不受终端控制的进程。守护进程常常在系统引导装入时启动,系统关闭时终止。由于没有控制终端,syslog函数是输出消息的标准方式。守护进程通过读取环境变量而获得配置信息。

守护进程的创建,首先调用fork,为避免挂起控制终端,将守护进程放入后台执行。调用setsid函数脱离控制终端,登录会话和进程组,使该进程成为无终端的会话组长。通过不让进程成为会话组长来禁止进程重新打开控制终端,这就需要第二次调用fork,父进程(会话组长)退出,子进程继续执行,并不再拥有打开控制终端的能力。

Fork()函数返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程标记;否则,出错返回-1。

// unistd.h  syslog.h  signal.h  fcntl.h
int deamon_init(const char *pname,int facility){
    int i;   
    int deamon_proc;   
    pid_t pid;
    if((pid = fork())<0)//出错
        return -1;
    else if(pid)//父进程,结束父进程,子进程继续
        exit(0);
    if(setsid()<0) 
        return -1;
    signal(SIGHUP,SIG_IGN);
    if((pid = fork())<0) 
        return -1;
    else if(pid) //结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
        exit(0);
    daemon_proc = 1;
    chdir("/"); //改变工作路径
    //进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
    for(i=0;i<64;i++)
        close(i);
    open("/dev/null",O_RDONLY);
    open("/dev/null",O_RDWR);
    open("/dev/null",O_RDWR);
    openlog(pname,LOG_PID,facility);
    return 0;
}

if(setsid()<0)  return -1;  当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结 束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。

signal(SIGCHLD,SIG_IGN);

这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

14.用UDP协议通讯时怎样得知目标机是否获得了数据包

可以在每个数据包中插入一个唯一的ID,比如timestamp或者递增的int。

发送方在发送数据时将此ID和发送时间记录在本地。

接收方在收到数据后将ID再发给发送方作为回应。

发送方如果收到回应,则知道接收方已经收到相应的数据包;如果在指定时间内没有收到回应,则数据包可能丢失,需要重复上面的过程重新发送一次,直到确定对方收到。

15.TCP为什么不是两次连接?而是三次握手?

如果A与B两个进程通信,如果仅是两次连接。可能出现的一种情况就是:A发送完请报文以后,由于网络情况不好,出现了网络拥塞,即B延时很长时间后收到报文,即此时A将此报文认定为失效的报文。B收到报文后,会向A发起连接。此时两次握手完毕,B会认为已经建立了连接可以通信,B会一直等到A发送的连接请求,而A对失效的报文回复自然不会处理。依次会陷入B忙等的僵局,造成资源的浪费。

16. https的理解,http的各个方法的使用场景

http的特点:

·  支持客户端/服务器模式 

·  简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快 

·  灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记 

·  无连接:无连接的含义是限制每次链接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开链接,采用这种方式可以节省传输时间 

·  无状态:HTTP 协议是无状态协议。无状态是指协议对于事物处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能会导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就比较快

方法名

功能

GET

向指定的资源发出“显示”请求,使用 GET 方法应该只用在读取数据上,而不应该用于产生“副作用”的操作中

POST

指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求文本中。这个请求可能会创建新的资源或者修改现有资源,或两者皆有。

PUT

向指定资源位置上传其最新内容

DELETE

请求服务器删除 Request-URI 所标识的资源

OPTIONS

使服务器传回该资源所支持的所有HTTP请求方法。用*来代替资源名称,向 Web 服务器发送 OPTIONS 请求,可以测试服务器功能是否正常运作

HEAD

与 GET 方法一样,都是向服务器发出指定资源的请求,只不过服务器将不传回资源的本文部分,它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中关于该资源的信息(原信息或称元数据)

TRACE

显示服务器收到的请求,主要用于测试或诊断

CONNECT

HTTP/1.1 中预留给能够将连接改为通道方式的代理服务器。通常用于 SSL 加密服务器的链接(经由非加密的 HTTP 代理服务器)

猜你喜欢

转载自blog.csdn.net/lxin_liu/article/details/89313615