TCP/IP

Posted 鸟随二月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP/IP相关的知识,希望对你有一定的参考价值。

应用层

TCP/IP协议:
应用层:HTTP(80)、sSH(22)、FTP(21)、DNS(53)、Telnet(23)
现有的协议如(现有的代码,例如javaSocket)

重要应用层协议DNS(Domain NameSystem)域名解析协议

当在浏览器中输入url之后,会发生如下事情
1.浏览器会判断当前输入的url是否合规,也是判断当前url是正确。
2.浏览器会判断当前的url有没有缓存(消除缓存: CTRL+R 再者在之前的url加上不同的变量)。
3.DNS服务器(域名->IP)
4.TCP连接(3 次握手)
5.以HTTP协议的数据格式发送数据给服务端。
6.ACK返回给客户端,告诉客户端我已经收到消息了。
7.服务器业务代码处理并且把结果返回浏览器
8.浏览器拿到服务器返回的信息,执行渲染。
9.4 次挥手正常断开连接。

传输层

端口号

端口号(Port)标识了一个主机上进行通信的不同的应用程序;

划分

0- 1023:知名端口号,HTTP, FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的.(ssh服务器,使用22端口;ftp服务器,使用21端口;telnet服务器,使用23端口;http服务器,使用80端口;https服务器,使用443‘;)
1024-65535:操作系统动态分配的端口号.客户端程序的端口号,就是由操作系统从这个范围分配的.

udp


16位源端口号:源程序的端口号,根据这个端口号可以定义发送端程序。
16位目标端口号:目标端口号,定位接收端的应用程序。
16位长度= UDP头部长度+数据的长度。
16位效验和:效验数据正确性。

校验过程:

UDP计算检验和的方法和计算IP数据报首部检验和的方法相似。但不同的是: IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分起都检验。 在发送方,首先是先把全零放入检验和字段。再把伪首部以及UDP用户数据报看成是由许多
16位的字串接起来。若UDP用户数据报的数据部分不是偶数个字节,则要填入一个全零字节(但此字节不发送)。然后按二进制反码计算出这些16位字的和。将此和的二进制反码写入检验和字段后,就发送这样的UDP用户数据报。在接收方,把收到的UDP用户数据报连
同伪首部(以及可能的填充全零字节)一起,按二进制反码求这些16位字的和。当无差错时其结果应为全1。否则就表明有差错出现,接收方就应丢弃这个UDP用户数据报(也可交给应用层,附上警告)。

特点

UDP:无连接(Java编程中每次都要建立一个新的DatagramPacket)、不稳定、面向数据报(Java编程建立的DatagramPacket中都设定值个长度)。

UDP必须要有接收缓冲区,可以大大提高UDP的工作效率。UDP之所以不需要发送缓冲区,是因为UDP是不需要连接的,也就是不需要等待对方先连接的,所有最快的工作方式就是拿到消息就发。

全双工:发送端或者接收端既可以发送消息有可以接收消息。
半双工指的是发送端只能发送消息,不能接收消息。
TCP和 UDP都是全双工。

TCP是既有发送缓冲区又有接收缓冲区。
TCP:有连接、稳定、面向数据流。

注意事项

因为UDP头部中有一个16 位的长度(数据+头部),因此一个UDP的最大包必须是 16 位可以表示。
16位->2的16 次方= 65536
UDP理论可以传输的大小为64K。
如果UDP编程**,数据包大于64的解决方案**:
1从应用来分离和组装数据。(程序员
2.大包的方式去发,在数据链路层进行分包和组包。(交个协议自动处理)
实际工作当中,会采取第一种也就是应用层组包和分包来实现UDP大数据的传递,因为如果使用了第二种方式,那么任意一个包丢失之后,那么整个数据包也就丢失了,风险极大(网络环境非常复杂),所以不会使用第二种方式。

基于UDP的应用层协议

NFS: 网络文件系统
TFTP: 简单文件传输协议
DHCP: 动态主机配置协议
BOOTP: 启动协议(用于无盘设备启动)
DNS: 域名解析协议

TCP

TCP全称为 "传输控制协议

URG:表示紧急消息。
ACK:确认应答表示。
PSH:用来表示让接收端将消息从缓冲区取走。flash
RST:复位表示符。
SYC:同步标识符。TCP初次连接FIN:结束表示。TCP结束时使用
16位校验和:用来确认消息是否是正确的。

TCP特性

确认应答(ACK)机制

超时重传机制


TCP稳定性的核心:确认应答+超时重传。

超时重发策略

超时重发策略1:(使用递增的发送时间进行重试)
第一次发送的小丢失,假设汤老师认为消息丢失的时间为10分钟(时间是由内核确认的)。第一次失败之后,那么会在10 分钟之后再发送一次消息。
第二次再发送消息的时候就会210分钟进行发送。
第三次:2
2*10 …
TCP:设计思路,如果第一次发送失败了,那么大概率第二次发送也会失败,为了节省带宽和程序的开销,那么它会采取递增的方式发送消息。

超时重发策略2:最大尝试失败之后就会“停止”发送
当经过了一定次数的发送之后,还是没有结果,那么发送端就会任务接收端下线了,就会“停止”发送了。
即使已经确认对方下线了,还是会以固定的频率发送要给没有内容的检查包,米探测对方是否上线。

连接管理

连接:3次握手
断开连接:4次挥手

握手:

只有具备了以下4个能力之后,才能有效的进行TCP的连接。
1.发送的发送能力(话简)
2.发送的接收能力(耳机)
3.接收的发送能力(对方的话筒)
4.接收方的接收能力(对方的耳机)

挥手


关于CLOSE_WAIT状态的说明:
说明:你的应用程序有bug,你的程序没有调用close()方法。
TIME_ WAIT ->CLOSE要经历2MSL(最大生存时间)
TIME_WAIT=等ACK到对方1MSL+ FIN最大发送时间MSL

滑动窗口

滑动窗口的目的就是为了保障传输的性能。


流量控制

根据接收缓冲区的实际情况,控制发送的速度。

如果TCP协议头的_16位窗口大小为 0,表示接收缓冲区已经满了,不能在发了。此时发送端就不会进行消息发送了,发送端会定时发送一个探测包,用来检测接收缓冲区的大小,如果接收缓冲区有值了,那么消息就可以继续发送了。

拥塞控制

发包从1开始,以默认值16为临界值,当小于此值的时候,以指数增加的方式发包,当等于这个此值就有线性增长的方式发包,一直到有大量丢包的请求(发包已经到当前时间段的极致)﹔这个时候就会将发包值置位1,然后再讲临界值设置为最大发包值的一包,继续重复此过程。

延迟应答

延迟应答是在流量控制的基础上优化发送效率。
策略:
策略1:固定一定时间段,发送一个延迟应答包;
策略2:接收一定次数的包之后,来一个延迟应答。
注意事项:
延迟应答时间不能超过 MSL(最大生存时间)。如果超过MSL就会触发超时重传,它会以为消息丢失。

捎带应答(用来提高消息传输的性能)

TCP问题(沾包/半包)


代码例子:

//服务器端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 沾包和半包问题
 */
public class TCPServer2 {
    // 端口号
    private static final int port = 9005;

    // 数据传输的最大值
    private static final int leng = 1024;

    public static void main(String[] args) throws IOException {
        // 创建服务器
        ServerSocket serverSocket = new ServerSocket(port);

        // 得到客户端的连接
        Socket clientSocket = serverSocket.accept();

//        // 读取信息
//        try (InputStream inputStream = clientSocket.getInputStream()) {
//            while (true) {
//                byte[] bytes = new byte[leng];
//                int result = inputStream.read(bytes, 0, leng);
//                if (result > 0) {
//                    // 表示读取成功
//                    System.out.println("读取客户端消息:" + new String(bytes));
//                }
//            }
//        }

        // 得到读取对象(解决方案)
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()))) {
            while (true) {
                // 按行定义 TCP 的边界
                String msg = reader.readLine();
                if (msg != null && !msg.equals("")) {
                    System.out.println("接收到客户端消息:" + msg);
                }
            }
        }

    }
}

//客户端
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * 沾包和半包问题
 */
public class TCPClient2 {
    // IP
    private static final String ip = "127.0.0.1";
    // 端口号
    private static final int port = 9005;

    public static void main(String[] args) throws IOException {
        // 创建客户端并连接服务器端
        Socket socket = new Socket(ip, port);

        //String msg = "Hi, Java.";
        //解决方案
        String msg = "Hi, Java.\\n";
        // 得到写入对象
        try (OutputStream outputStream = socket.getOutputStream()) {
            for (int i = 0; i < 10; i++) {
                // 发送数据请求
                outputStream.write(msg.getBytes(),
                        0, msg.getBytes().length);
                outputStream.flush();
            }
        }
    }
}   


使用以上的解决方案即可

进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接

TCP总结

TCP UDP 对比

1.UDP是无连接的;TCP是有连接的;
2.UDP是不稳定的;TCP是稳定的;
3.UDP是面向数据报;TCP是面向数据流;
4.UDP没有发送缓冲区;TCP有发送缓冲区;
5.TCP是以稳定性著称;而UDP是以高效性著称。

网络层


4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4.
4位头部长度(header length): IP头部的长度是多少个32bit, 也就是 length * 4 的字节数. 4bit表示最大的数字是15, 因此IP头部最大长度是60字节.
8位服务类型(Type Of Service): 3位优先权字段(已经弃用), 4位TOS字段, 和1位保留字段(必须置为0). 4位TOS分别表示: 最小延时, 最大吞吐量, 最高可靠性, 最小成本. 这四者相互冲突, 只能选择一个. 对于ssh/telnet这样的应用程序, 最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要.
16位总长度(total length): IP数据报整体占多少个字节.
16位标识(id): 唯一的标识主机发送的报文. 如果IP报文在数据链路层被分片了, 那么每一个片里面的这个id都是相同的.
3位标志字段: 第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到). 第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文. 第三位表示"更多分片", 如果分片了的话, 最后一个分片置为1, 其他是0. 类似于一个结束标记.
13位分片偏移(framegament offset): 是分片相对于原始IP报文开始处的偏移. 其实就是在表示当前分片在原报文中处在哪个位置. 实际偏移的字节数是这个值 * 8 得到的. 因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了).
8位生存时间(Time To Live, TTL): 数据报到达目的地的最大报文跳数. 一般是64. 每次经过一个路由, TTL -= 1, 一直减到0还没到达, 那么就丢弃了. 这个字段主要是用来防止出现路由循环
8位协议: 表示上层协议的类型
16位头部校验和: 使用CRC进行校验, 来鉴别头部是否损坏.
32位源地址和32位目标地址: 表示发送端和接收端(IPV4是32位,其他的不一定)

分类

IP地址:网络号+主机号
内网来说:网络号相同,主机号不同。

私有网段

10.,前8位是网络号,共16,777,216个地址一
172.16.到172.31.,前12位是网络号,共1,048,576个地址
192.168.*,前16位是网络号,共65,536个地址
包含在这个范围中的,都成为私有IP,其余的则称为全局IP(或公网IP);

缓解IPV4地址不够用的解决方案:

1.IPV6 ( 128位)「弊端:需要更换网络设备来支持IPV6].
2.NAT

NAT(网络地址转换)

优点:缓解IPV4 不够用的问题。
缺点:a)NAT服务是有开销。b)如果NAT服务器挂了,那么整个都会挂掉。

NAT 代理服务器 区别:

a)所在层级不一样:代理服务器一般运行在应用层; NAT是运行在数据链路层。
b)代理服务器通常是安装在电脑或者是手机上的,而NAT服务器通常会安装在防火墙上。
c)解决问题不同:NAT是解决IP不够用的问题,代理服务器是解决访问不了的问题。

数据链路层



ARP协议

ARP它是基于网络层数据链路层之间的一个协议,可以实现IP到MAC映射。

以上是关于TCP/IP的主要内容,如果未能解决你的问题,请参考以下文章

TCP/IP资料

TCP/IP协议---ICMP协议及pingtraceroute

TCP/IP 服务器代码在包含附加库后停止工作

漏洞预警 | VxWorks TCP/IP 协议栈多个远程代码执行漏洞

Java TCP/IP Socket基于NIO的TCP通信(含代码)

Java TCP/IP SocketUDP Socket(含代码)