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

Android TCP封装工具类

TCP通信的封装,我们可以从以下几个方面进行改进:

线程池优化:使用更高效的线程池配置,避免频繁创建和销毁线程。

连接重试机制:在网络不稳定时,自动重试连接。

心跳机制:保持长连接,避免因超时断开。

数据缓冲区优化:动态调整缓冲区大小,适应不同数据量。

异常处理增强:区分不同类型的异常,提供更详细的错误信息。

代码简洁性:减少冗余代码,提高可读性和可维护性。

TCP客户端封装(Java)

import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class TcpClient {private static final String TAG = "TcpClient";private static final int HEARTBEAT_INTERVAL = 10; // 心跳间隔(秒)private static final int CONNECT_TIMEOUT = 5000; // 连接超时时间(毫秒)private static final int RECONNECT_DELAY = 3000; // 重连延迟时间(毫秒)private Socket socket;private InputStream inputStream;private OutputStream outputStream;private ExecutorService executorService;private ScheduledExecutorService heartbeatExecutor;private boolean isConnected = false;private TcpListener listener;private String serverIp;private int serverPort;public TcpClient(TcpListener listener) {this.listener = listener;executorService = Executors.newCachedThreadPool();heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();}/*** 连接到服务器** @param ip   服务器IP地址* @param port 服务器端口*/public void connect(String ip, int port) {this.serverIp = ip;this.serverPort = port;executorService.execute(this::connectInternal);}private void connectInternal() {try {// 创建Socket并连接服务器socket = new Socket();socket.connect(new InetSocketAddress(serverIp, serverPort), CONNECT_TIMEOUT);inputStream = socket.getInputStream();outputStream = socket.getOutputStream();isConnected = true;// 通知连接成功if (listener != null) {listener.onConnected();}// 开始接收数据receiveData();// 启动心跳机制startHeartbeat();} catch (IOException e) {Log.e(TAG, "Connection failed: " + e.getMessage());if (listener != null) {listener.onError("Connection failed: " + e.getMessage());}scheduleReconnect();}}/*** 断开连接*/public void disconnect() {executorService.execute(() -> {try {if (socket != null) {socket.close();}if (inputStream != null) {inputStream.close();}if (outputStream != null) {outputStream.close();}isConnected = false;// 通知断开连接if (listener != null) {listener.onDisconnected();}} catch (IOException e) {Log.e(TAG, "Disconnect error: " + e.getMessage());} finally {stopHeartbeat();}});}/*** 发送数据** @param data 要发送的数据*/public void sendData(byte[] data) {if (!isConnected || outputStream == null) {Log.e(TAG, "Not connected to server");return;}executorService.execute(() -> {try {outputStream.write(data);outputStream.flush();Log.d(TAG, "Data sent successfully");} catch (IOException e) {Log.e(TAG, "Failed to send data: " + e.getMessage());if (listener != null) {listener.onError("Failed to send data: " + e.getMessage());}disconnect();}});}/*** 接收数据*/private void receiveData() {executorService.execute(() -> {byte[] buffer = new byte[1024];int bytesRead;while (isConnected) {try {bytesRead = inputStream.read(buffer);if (bytesRead == -1) {// 服务器关闭连接disconnect();break;}// 处理接收到的数据byte[] receivedData = new byte[bytesRead];System.arraycopy(buffer, 0, receivedData, 0, bytesRead);// 通知数据接收if (listener != null) {listener.onDataReceived(receivedData);}} catch (IOException e) {Log.e(TAG, "Failed to receive data: " + e.getMessage());if (listener != null) {listener.onError("Failed to receive data: " + e.getMessage());}disconnect();break;}}});}/*** 启动心跳机制*/private void startHeartbeat() {heartbeatExecutor.scheduleAtFixedRate(() -> {if (isConnected) {sendData("HEARTBEAT".getBytes()); // 发送心跳包}}, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);}/*** 停止心跳机制*/private void stopHeartbeat() {heartbeatExecutor.shutdown();}/*** 安排重连*/private void scheduleReconnect() {executorService.schedule(this::connectInternal, RECONNECT_DELAY, TimeUnit.MILLISECONDS);}/*** 是否已连接*/public boolean isConnected() {return isConnected;}/*** 关闭线程池*/public void shutdown() {executorService.shutdown();heartbeatExecutor.shutdown();}/*** TCP事件监听器*/public interface TcpListener {void onConnected(); // 连接成功void onDisconnected(); // 断开连接void onDataReceived(byte[] data); // 接收到数据void onError(String error); // 发生错误}
}

2. 在Activity中使用

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity implements TcpClient.TcpListener {private static final String SERVER_IP = "192.168.1.100"; // 服务器IPprivate static final int SERVER_PORT = 8080; // 服务器端口private TcpClient tcpClient;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化TCP客户端tcpClient = new TcpClient(this);// 连接到服务器tcpClient.connect(SERVER_IP, SERVER_PORT);// 发送数据String message = "Hello, Server!";tcpClient.sendData(message.getBytes());}@Overridepublic void onConnected() {Log.d("TcpClient", "Connected to server");}@Overridepublic void onDisconnected() {Log.d("TcpClient", "Disconnected from server");}@Overridepublic void onDataReceived(byte[] data) {String message = new String(data);Log.d("TcpClient", "Received data: " + message);}@Overridepublic void onError(String error) {Log.e("TcpClient", "Error: " + error);}@Overrideprotected void onDestroy() {super.onDestroy();// 断开连接并释放资源if (tcpClient != null) {tcpClient.disconnect();tcpClient.shutdown();}}
}

进一步优化(Kotlin版本)

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.net.InetSocketAddress
import java.net.Socketclass MainActivity : AppCompatActivity(), TcpClient.TcpListener {private val serverIp = "192.168.1.100" // 服务器IPprivate val serverPort = 8080 // 服务器端口private lateinit var tcpClient: TcpClientoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 初始化TCP客户端tcpClient = TcpClient(this)// 连接到服务器tcpClient.connect(serverIp, serverPort)// 发送数据val message = "Hello, Server!"tcpClient.sendData(message.toByteArray())}override fun onConnected() {Log.d("TcpClient", "Connected to server")}override fun onDisconnected() {Log.d("TcpClient", "Disconnected from server")}override fun onDataReceived(data: ByteArray) {val message = String(data)Log.d("TcpClient", "Received data: $message")}override fun onError(error: String) {Log.e("TcpClient", "Error: $error")}override fun onDestroy() {super.onDestroy()// 断开连接并释放资源tcpClient.disconnect()tcpClient.shutdown()}
}class TcpClient(private val listener: TcpListener) {private var socket: Socket? = nullprivate var inputStream: InputStream? = nullprivate var outputStream: OutputStream? = nullprivate var isConnected = falseprivate val scope = CoroutineScope(Dispatchers.IO)private var heartbeatJob: Job? = nullfun connect(ip: String, port: Int) {scope.launch {try {socket = Socket().apply {connect(InetSocketAddress(ip, port), 5000) // 5秒超时}inputStream = socket?.getInputStream()outputStream = socket?.getOutputStream()isConnected = truewithContext(Dispatchers.Main) {listener.onConnected()}receiveData()startHeartbeat()} catch (e: IOException) {withContext(Dispatchers.Main) {listener.onError("Connection failed: ${e.message}")}scheduleReconnect()}}}fun disconnect() {scope.launch {try {socket?.close()inputStream?.close()outputStream?.close()isConnected = falsewithContext(Dispatchers.Main) {listener.onDisconnected()}} catch (e: IOException) {withContext(Dispatchers.Main) {listener.onError("Disconnect error: ${e.message}")}} finally {stopHeartbeat()}}}fun sendData(data: ByteArray) {if (!isConnected || outputStream == null) {Log.e("TcpClient", "Not connected to server")return}scope.launch {try {outputStream?.write(data)outputStream?.flush()Log.d("TcpClient", "Data sent successfully")} catch (e: IOException) {withContext(Dispatchers.Main) {listener.onError("Failed to send data: ${e.message}")}disconnect()}}}private fun receiveData() {scope.launch {val buffer = ByteArray(1024)var bytesRead: Intwhile (isConnected) {try {bytesRead = inputStream?.read(buffer) ?: -1if (bytesRead == -1) {disconnect()break}val receivedData = buffer.copyOf(bytesRead)withContext(Dispatchers.Main) {listener.onDataReceived(receivedData)}} catch (e: IOException) {withContext(Dispatchers.Main) {listener.onError("Failed to receive data: ${e.message}")}disconnect()break}}}}private fun startHeartbeat() {heartbeatJob = scope.launch {while (isConnected) {sendData("HEARTBEAT".toByteArray())delay(10000) // 10秒间隔}}}private fun stopHeartbeat() {heartbeatJob?.cancel()}private fun scheduleReconnect() {scope.launch {delay(3000) // 3秒后重连connect(socket?.inetAddress?.hostAddress ?: "", socket?.port ?: 0)}}fun shutdown() {scope.cancel()}interface TcpListener {fun onConnected()fun onDisconnected()fun onDataReceived(data: ByteArray)fun onError(error: String)}
}

相关文章:

Android TCP封装工具类

TCP通信的封装,我们可以从以下几个方面进行改进: 线程池优化:使用更高效的线程池配置,避免频繁创建和销毁线程。 连接重试机制:在网络不稳定时,自动重试连接。 心跳机制:保持长连接&#xff…...

Python基于Django的医用耗材网上申领系统【附源码、文档说明】

博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...

Java虚拟机之垃圾收集(一)

目录 一、如何判定对象“生死”? 1. 引用计数算法(理论参考) 2. 可达性分析算法(JVM 实际使用) 3. 对象的“缓刑”机制 二、引用类型与回收策略 三、何时触发垃圾回收? 1. 分代回收策略 2. 手动触发…...

【mysql】mysql数据库,权限授予,账号设置

创建一个可用的数据库,名字为gitea,支持中文utf8mb4 CREATE DATABASE gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 设置东八区时区 SET GLOBAL time_zone 08:00; FLUSH PRIVILEGES; # 查询当前时间 SELECT NOW();给【普通用户】授予查…...

Html5学习教程,从入门到精通, HTML5超链接应用的详细语法知识点和案例代码(18)

HTML5超链接应用的详细语法知识点和案例代码 超链接(Hyperlink),也称为跃点链接,是互联网和文档编辑中的一种重要概念。 超链接的定义 超链接是指从一个网页指向一个目标的连接关系,这个目标可以是另一个网页&#…...

⭐LeetCode(数学分类) 48. 旋转图像——优美的数学法转圈(原地修改)⭐

⭐LeetCode(数学分类) 48. 旋转图像——优美的数学法转圈(原地修改)⭐ 示例 1: 输入:root [5,3,6,2,4,null,8,1,null,null,null,7,9] 输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9] 示例 2: 输入&#xff1…...

深度学习PyTorch之13种模型精度评估公式及调用方法

深度学习pytorch之22种损失函数数学公式和代码定义 深度学习pytorch之19种优化算法(optimizer)解析 深度学习pytorch之4种归一化方法(Normalization)原理公式解析和参数使用 深度学习pytorch之简单方法自定义9类卷积即插即用 实时…...

tomcat单机多实例部署

一、部署方法 多实例可以运行多个不同的应用,也可以运行相同的应用,类似于虚拟主机,但是他可以做负载均衡。 方式一: 把tomcat的主目录挨个复制,然后把每台主机的端口给改掉就行了。 优点是最简单最直接,…...

Java开发者如何接入并使用DeepSeek

目录 一、准备工作 二、添加DeepSeek SDK依赖 三、初始化DeepSeek客户端 四、数据上传与查询 五、数据处理与分析 六、实际应用案例 七、总结 【博主推荐】:最近发现了一个超棒的人工智能学习网站,内容通俗易懂,风格风趣幽默&#xff…...

win10电脑鼠标速度突然变的很慢?

电脑鼠标突然变很慢,杀毒检测后没问题,鼠标设置也没变,最后发现可能是误触鼠标的“DPI”调节键。 DPI调节键在鼠标滚轮下方,再次点击即可恢复正常鼠标速度。 如果有和-的按键,速度变快,-速度变慢。 图源&…...

第四次CCF-CSP认证(含C++源码)

第四次CCF-CSP认证 第一道(easy)思路及AC代码 第二道(easy)思路及AC代码遇到的问题 第三道(mid)思路及AC代码 第一道(easy) 题目链接 思路及AC代码 这题就是将这个矩阵旋转之后输出…...

Netty基础—1.网络编程基础一

大纲 1.什么是OSI开放系统互连 2.OSI七层模型各层的作用 3.TCP/IP协议的简介 4.TCP和UDP的简介 5.TCP连接的三次握手 6.TCP连接的四次挥手 7.TCP/IP中的数据包 8.TCP通过确认应答与序列号提高可靠性 9.HTTP请求的传输过程 10.HTTP协议报文结构 11.Socket、短连接、长…...

【理想解法学习笔记】

目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是:首先建立初始化决策矩阵,而后基于规范化后的初…...

98.在 Vue3 中使用 OpenLayers 根据 Resolution 的不同显示不同的地图

在 Vue3 中使用 OpenLayers 根据 Resolution 的不同显示不同的地图 前言 在 Web GIS(地理信息系统)应用开发中,地图的 Resolution(分辨率)是一个重要的概念。不同的 Resolution 适用于不同的地图层级,有时…...

Docker 部署 Vaultwarden

一、前言 1. 官网 1.1 Vaultwarden https://github.com/dani-garcia/vaultwarden https://github.com/wcjxixi/Vaultwarden-Wiki-Chn https://hub.docker.com/r/vaultwarden/server https://rs.ppgg.in/ # Vaultwarden Wiki 中文版 https://geekdaxue.co/read/Vaultward…...

Smart contract -- 自毁合约

在区块链开发中,Solidity 语言提供了强大的功能,其中自毁合约是一个独特且重要的特性。今天,就让我们深入探讨一下 Solidity 中的自毁合约,以及如何使用 selfdestruct 函数。 注意:使用继承时请确保代码的正确性&#…...

unity学习64,第3个小游戏:一个2D跑酷游戏

目录 学习参考 素材资源导入 1 创建项目 1.1 创建1个2D项目 1.2 导入素材 2 背景图bg 2.0 bg素材 2.1 创建背景 2.2 修改素材,且修改摄像机等 2.2.1 修改导入的原始prefab素材 2.2.2 对应调整摄像机 2.2.3 弄好背景 2.3 背景相关脚本实现 2.3.1 错误…...

Python Flask 在网页应用程序中处理错误和异常

Python Flask 在网页应用程序中处理错误和异常 Python Flask 在网页应用程序中处理错误和异常 Python Flask 在网页应用程序中处理错误和异常 在我们所有的代码示例中,我们没有注意如何处理用户在浏览器中输入错误的URL或向我们的应用程序发送错误的参数集的情况。…...

模板方法模式的C++实现示例

核心思想 模板方法设计模式是一种行为设计模式,它定义了一个算法的框架,并将某些步骤的具体实现延迟到子类中。通过这种方式,模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法模式的核心在于: ​…...

水下机器人推进器PID参数整定与MATLAB仿真

水下机器人推进器PID参数整定与MATLAB仿真 1. PID控制原理 目标:通过调节比例(P)、积分(I)、微分(D)参数,使推进器输出力快速稳定跟踪期望值。传递函数(示例):推进器动力学模型可简化为: [ G(s) = \frac{K}{\tau s + 1} \cdot e^{-Ts} ] 其中:K为增益,τ为时间常…...

在本地部署DeepSeek等大模型时,需警惕的潜在安全风险

在本地部署DeepSeek等大模型时,尽管数据存储在本地环境(而非云端),但仍需警惕以下潜在安全风险: 1. 模型与数据存储风险 未加密的存储介质:若训练数据、模型权重或日志以明文形式存储,可能被物…...

智能焊机监测系统:打造工业安全的数字化盾牌

在现代工业生产中,焊机作为核心设备之一,其稳定性和安全性直接关系到生产效率和产品质量。德州迪格特科技有限公司推出的智能焊机监测系统,通过先进的技术手段,为工业生产构筑了一道坚固的安全防线。 智能监测,保障焊…...

【redis】string类型相关操作:SET、GET、MSET、MGET、SETNX、SETEX、PSETEX

文章目录 二进制存储编码转换SET 和 GETSETGET MSET 和 MGETSETNX、SETEX 和 PSETEX Redis 所有的 key 都是字符串,value 的类型是存在差异的 二进制存储 Redis 中的字符串,直接就是按照二进制数据的方式存储的 不仅仅可以存储文本数据,还可…...

GaussDB安全配置指南:从认证到防御的全方面防护

一、引言 随着企业数据规模的扩大和云端化进程加速,数据库安全性成为运维的核心挑战之一。GaussDB作为一款高性能分布式数据库,提供了丰富的安全功能。本文将从 ​认证机制、权限控制、数据加密、审计日志​ 等维度,系统性地讲解如何加固 Ga…...

总结学习课程

1. 数据加载与预处理 PyTorch工具加载和预处理数据(如MNIST数据集)。 2. 定义模型 - 使用nn.Module构建神经网络,定义各层和前向传播。 3. 损失函数与优化器: 选择损失函数(如交叉熵损失)和优化…...

Ubuntu20.04搭建gerrit code review

一、环境准备 1. 安装 Java 环境‌ Gerrit 依赖 Java 运行环境(推荐 JDK 8): sudo apt install openjdk-11-jdk 验证安装: java -version ‌2. 安装 Git sudo apt install git ‌3. 可选依赖 数据库‌:Gerrit …...

MacOS安装FFmpeg和FFprobe

按照网上很多教程安装,结果都失败了,后来才发现是路径问题,其实安装过程很简单(无奈) 第一步: 在官网下载 打开页面后,可以看到FFmpeg、FFprobe、FFplay和FFserver的下载图标 第二步&#xff1…...

Redis7系列:设置开机自启

前面的文章讲了Redis和Redis Stack的安装,随着服务器的重启,导致Redis 客户端无法连接。原来的是Redis没有配置开机自启。此文记录一下如何配置开机自启。 1、修改配置文件 前面的Redis和Redis Stack的安装的文章中已经讲了redis.config的配置&#xf…...

SpringAI介绍及本地模型使用方法

博客原文地址 前言 Spring在Java语言中一直稳居高位,与AI的洪流碰撞后也产生了一些有趣的”化学反应“,当然你要非要说碰撞属于物理反应也可以, 在经历了一系列复杂的反应方程后,Spring家族的新成员——SpringAI,就…...

Zookeeper实践指南

Zookeeper实践指南 1. 什么是 Zookeeper? Zookeeper 是 Apache 旗下的一个开源分布式协调框架,主要用于解决分布式系统中的一致性问题,提供高效可靠的分布式数据管理能力。 1.1 Zookeeper 的核心特性 顺序一致性:客户端的更新…...