当前位置: 首页 > java >正文

java网络原理4

一、TCP连接异常处理

 

1.1四次挥手异常:

正常的四次挥手完成后,双方可确认连接关闭并删除保存的信息。若挥手过程中,一方发送的FIN未收到ack,会尝试重传几次FIN。若最终仍未成功挥手,至少自己可删除保存的信息,而对端若关机,其内存数据会丢失。

1.2掉电情况处理

- 接收方掉电:若接收方掉电,发送方继续发送数据却收不到ack,超时重传后仍无ack,达到一定程度,发送方会发送复位报文,放弃当前连接并重新连接。

- 发送方掉电:接收方会感知到发送方突然停止发送数据,会继续阻塞等待新数据。此时引入心跳包机制,接收方和发送方周期性交换“心跳包” (A发无业务数据报文,B返回ack ) ,若对方无应答,接收方可以单方面释放连接。此机制在分布式系统中尤为重要,用于判断节点存活。

 

二、IP协议基础

 

2.1 IP报头:

IP报头最大长度60字节,其中0 - 15位占4字节,且只有4位有效。8位服务类型(ToS )字段中,4位优先权字段已弃用,剩下4位分别表示最小延时、最大吞吐量、最高可靠性、最小成本(四者互斥,只能选其一 ) 。例如,对于ssh/telnet这类应用,最小延时较重要;对于文件传输等,最大吞吐量更关键。

2.2 IP数据包长度与拆包:

IP数据包由报头和载荷组成,单个IP数据报有长度限制,但当携带超过64KB载荷数据时,IP协议支持拆包/组包。传输层数据包太长时,IP自动拆成多个数据报,每个携带部分数据,接收方依据16位标识、3位标志、13位片偏移三个属性进行组包。

2.3生存时间(TTL ):

TTL表示当前IP数据报在网络上的存活次数,每经路由器转发一次,TTL减1 ,为0时若未到达对方便丢弃。可通过 traceroute (Linux )或 tracert (Windows )命令追踪数据报转发路径及中间节点。

 

三、TCP传输效率优化机制

 

3.1延时应答:

为提高传输效率,TCP不立即返回ack,而是稍作等待。这样给接收方时间处理更多数据,使接收缓冲区剩余空间更大,让窗口尽量大些(在保证可靠性前提下 ) 。

3.2 捎带应答:

基于延时应答,进一步优化传输效率。在网络通信“一问一答”模型中,服务器收到客户端请求后,不是立即返回ack,而是等计算出响应后,将ack和响应一起返回,减少报文数量。但捎带应答不是100%触发,取决于延时应答和应用程序处理逻辑。

 

四、TCP面向字节流及粘包问题

 

4.1 面向字节流特性:

TCP读取/写入操作灵活,如读100字节数据,可一次读10字节,分10次完成;或一次读20字节,分5次完成等。

4.2粘包问题:

接收方接收多个TCP数据报时,去除报头后将载荷放接收缓冲区,由于TCP字节流特性,应用层数据包边界模糊,出现粘包现象。例如发送方发送aaa、bbb、ccc三个应用层载荷,接收方读取时可能有多种组合方式。

- 粘包问题解决:从应用层入手,合理设计协议。如使用特殊分隔符(如 \n  )区分包边界;或在应用层数据包开头约定固定长度,接收方先读固定长度确定后续读取字节数,确保读到完整应用层数据包 。UDP不存在粘包问题,因其接收缓冲区机制与TCP不同。

 

五、TCP协议总结及应用场景

 

5.1 核心机制总结:

TCP协议包括确认应答、超时重传、连接管理(三次握手、四次挥手 ) 、滑动窗口、流量控制、拥塞控制、延时应答、捎带应答、面向字节流(粘包问题 ) 、异常情况(心跳包 )等核心机制。

5.2 协议对比与应用选择:

TCP有连接、可靠传输、面向字节流,适用于大部分需可靠传输场景;UDP无连接、不可靠传输、面向数据报,传输效率高,适用于机房内部等对丢包不敏感、效率要求高场景。还有如KCP等协议,适用于既需一定可靠性又要求高效率的场景,如竞技类网游。

 

六、代码示例(以Java为例解决粘包问题 )

 

6.1.使用分隔符解决粘包问题

 

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

 

public class DelimiterBasedServer {

    public static void main(String[] args) {

        try (ServerSocket serverSocket = new ServerSocket(8080)) {

            System.out.println("Server started. Waiting for client...");

            try (Socket clientSocket = serverSocket.accept()) {

                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

 

                String request;

                while ((request = in.readLine()) != null) {

                    // 处理请求逻辑

                    System.out.println("Received: " + request);

                    // 构造响应并发送

                    out.println("Response to: " + request);

                }

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

 

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.Socket;

 

public class DelimiterBasedClient {

    public static void main(String[] args) {

        try (Socket socket = new Socket("localhost", 8080)) {

            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

 

            String request = "Hello, Server";

            // 发送请求

            out.println(request);

            // 接收响应

            String response = in.readLine();

            System.out.println("Received response: " + response);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

 

 

上述代码中,服务器端用 BufferedReader 的 readLine 方法读取数据,以 \n 作为分隔符,客户端通过 println 方法发送数据,保证数据按行分隔,解决粘包问题。

 

6.2.使用固定长度解决粘包问题

 

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

 

public class FixedLengthBasedServer {

    public static void main(String[] args) {

        try (ServerSocket serverSocket = new ServerSocket(8080)) {

            System.out.println("Server started. Waiting for client...");

            try (Socket clientSocket = serverSocket.accept()) {

                DataInputStream in = new DataInputStream(clientSocket.getInputStream());

                DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());

 

                // 假设约定数据长度为10字节

                byte[] buffer = new byte[10];

                in.readFully(buffer);

                String request = new String(buffer).trim();

                // 处理请求逻辑

                System.out.println("Received: " + request);

                // 构造响应并发送

                String response = "Response to: " + request;

                byte[] responseBytes = response.getBytes();

                out.write(responseBytes);

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

 

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.Socket;

 

public class FixedLengthBasedClient {

    public static void main(String[] args) {

        try (Socket socket = new Socket("localhost", 8080)) {

            DataInputStream in = new DataInputStream(socket.getInputStream());

            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

 

            String request = "Hello, Server";

            // 补全到固定长度10字节

            request = String.format("%10s", request);

            byte[] requestBytes = request.getBytes();

            out.write(requestBytes);

            // 接收响应

            byte[] responseBuffer = new byte[10];

            in.readFully(responseBuffer);

            String response = new String(responseBuffer).trim();

            System.out.println("Received response: " + response);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

在该代码中,服务器端和客户端约定数据长度(这里假设为10字节 )  DataInputStream 和 DataOutputStream 按固定长度读写数据,避免粘包问题。

 

 

http://www.xdnf.cn/news/2015.html

相关文章:

  • 配合图解 SEG-SAM: Semantic-Guided SAM for Unified Medical Image Segmentation
  • 三格电子——如何解决工业场景中以太网设备布线不方便的问题
  • 海外红人营销+用户反馈闭环:2025跨境电商品牌持续优化策略
  • 【前缀和计算和+哈希表查找次数】Leetcode 560. 和为 K 的子数组
  • 特斯拉宣布启动自动驾驶网约车测试,无人出租车服务进入最后准备阶段
  • SIEMENS PLC程序解读 -Serialize(序列化)SCATTER_BLK(数据分散)
  • sherpa-ncnn:Linux(x86/ARM32/ARM64)构建sherpa-ncnn --语音转文本大模型
  • BIOS主板(非UEFI)安装fedora42的方法
  • ClickHouse 中`MergeTree` 和 `ReplicatedMergeTree`表引擎区别
  • 谈谈接口和抽象类有什么区别?
  • 从“干瞪眼“到精准唤醒:Java线程通信的打怪升级之路
  • Unity3D Lua集成技术指南
  • kubesphere 单节点启动 etcd 报错
  • 3、LangChain基础:LangChain Chat Model
  • 从FP32到BF16,再到混合精度的全景解析
  • 高等数学第二章---导数与微分(2.1~2.3)
  • 多模态大语言模型arxiv论文略读(四十)
  • 语音合成之五语音合成中的“一对多”问题主流模型解决方案分析
  • Synopsys 逻辑综合的整体架构概览
  • vscode 打开csv乱码
  • 4.5/Q1,GBD数据库最新文章解读
  • Dubbo负载均衡策略深度解析
  • 洛谷 B3647:【模板】Floyd 算法
  • 筑牢数字防线:商城系统安全的多维守护策略
  • 《解锁LLMs from scratch:开启大语言模型的探索之旅》
  • Electron Forge【实战】阿里百炼大模型 —— AI 聊天
  • BGP网络协议
  • 数据可视化平台产品介绍及功能特色
  • .NET 10 中的新增功能
  • 力扣347:前K个高频元素