1 - 协议栈概述


2 - TCP协议

TCP: 传输控制协议. 是一种面向连接的可靠的流协议.

image.png

2.1 - TCP握手过程

TCP第一个特性就是面向连接. 顾名思义, 两端在通信之前必须建立一条通信通道.

2.1.1 - 三次握手

image.png
SYN段: TCP协议中的标志位. 表示主机A发送的是要建立连接的请求.
1和2: 称为一个RTT(Round-Trip Time).

2.1.2 - 四次挥手

image.png

  • FIN: TCP协议中的标志位. 表示要断开TCP连接
  • 一般情况下2和3会同时发送到主机A

2.2 - TCP如何可靠传输

2.2.1 - 序列号和确认应答

2.2.2 - 超时重传

2.2.3 - 连接管理

2.2.4 - 窗口控制

2.3 - TCP粘包断包

2.3.1 - 什么是粘包断包

接收方收到的数据包, 并不是发送方的完整数据. 不足一条完整数据包时叫做断包, 超过完整数据包长度的叫做粘包

2.3.2 - 产生原因

  • MTU/MSS
  • TCP拥塞算法
  • 发送缓冲区

2.3.3 - 如何解决

TCP/IP协议本身无法理解上层数据语义, 那么上层应用在设计数据协议的时候, 就需要保证数据的有界. 一般有两种解决方案.

  • 定长Header + 不定长Body
  • 无意义分隔符

    image.png

image.png

  1. public boolean doDecode(IoSession iosession, IoBuffer iobuffer, ProtocolDecoderOutput out) throws Exception {
  2. boolean complete = false;
  3. IoBuffer buff = IoBuffer.allocate(320).setAutoExpand(true);
  4. while (iobuffer.hasRemaining()) {
  5. byte b = iobuffer.get();
  6. if (b == CIMConstant.MESSAGE_SEPARATE) {
  7. complete = true;
  8. break;
  9. }
  10. buff.put(b);
  11. }
  12. buff.flip();
  13. byte[] bytes = new byte[buff.limit()];
  14. buff.get(bytes);
  15. String message = new String(bytes, charset);
  16. if (!complete) {
  17. int position = iobuffer.position();
  18. int limit = iobuffer.limit();
  19. logger.warn("消息不够消费, {} => :: 当前位置 => {} :: 长度 => {} ", message, position, limit);
  20. iobuffer.flip();
  21. return false;
  22. }
  23. buff.clear();
  24. // message解析成高级对象
  25. }
  1. IoBuffer resultBuffer = null;
  2. do {
  3. byte frameInfo = in.get();
  4. // 第一个byte抹掉高4位 取opcode
  5. byte opCode = (byte) (frameInfo & 0x0f);
  6. if (opCode == 8) {
  7. // opCode = 8 关闭连接
  8. session.closeNow();
  9. return resultBuffer;
  10. }
  11. // 第二个byte抹掉第1位 取有效数据长度 PlayLoad length
  12. // PlayLoad length < 126: 真实数据长度
  13. // PlayLoad length = 126: 紧跟着后2个字节代表数据长度
  14. // PlayLoad length = 127: 紧跟着后8个字节代表数据长度
  15. int frameLen = (in.get() & (byte) 0x7F);
  16. if (frameLen == 126) {
  17. frameLen = in.getShort();
  18. }
  19. // 缓冲区的数据不足
  20. if (frameLen + 4 > in.remaining()) {
  21. return null;
  22. }
  23. // PlayLoad length后面的是4byte的masking-key
  24. byte mask[] = new byte[4];
  25. for (int i = 0; i < 4; i++) {
  26. mask[i] = in.get();
  27. }
  28. byte[] unMaskedPayLoad = new byte[frameLen];
  29. for (int i = 0; i < frameLen; i++) {
  30. byte maskedByte = in.get();
  31. unMaskedPayLoad[i] = (byte) (maskedByte ^ mask[i % 4]);
  32. }
  33. if (resultBuffer == null) {
  34. resultBuffer = IoBuffer.wrap(unMaskedPayLoad);
  35. resultBuffer.position(resultBuffer.limit());
  36. resultBuffer.setAutoExpand(true);
  37. } else {
  38. resultBuffer.put(unMaskedPayLoad);
  39. }
  40. }
  41. while (in.hasRemaining());
  42. resultBuffer.flip();

3 - TCP状态转换

image.png

image.png
如果服务端存在大量出于TIME_WAIT状态的连接, 可能会带来:

  1. 服务端进程关闭后, 可能会在一段时间内无法重启
  2. 大量TIME_WAIT堆积, 如果超出了服务器可分配的端口范围, 会导致无法创建新的连接

4 - Linux内核TCP调优参数