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

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…...

解决火绒启动时,报安全服务异常,无法保障计算机安全

1.找到控制面板-安全和维护-更改用户账户控制设置 重启启动电脑解决。...

Spring Boot框架总结(超级详细)

前言 本篇文章包含Springboot配置文件解释、热部署、自动装配原理源码级剖析、内嵌tomcat源码级剖析、缓存深入、多环境部署等等,如果能耐心看完,想必会有不少收获。 一、Spring Boot基础应用 Spring Boot特征 概念: 约定优于配置&#…...

为什么要使用前缀索引,以及建立前缀索引:sql示例

背景: 你想啊,数据库里有些字段,它老长了,就像那种 varchar(255) 的字段,这玩意儿要是整个字段都拿来建索引,那可太占地方了。打个比方,这就好比你要在一个超级大的笔记本上记东西,每…...

Nuxt3 ssr build/dev时区分不同的环境

package.json "scripts": {"build": "nuxt build --dotenv .env.prod","build:dev": "nuxt build --dotenv .env.dev","postbuild": "mv -f .output ./dist/.output", //支持自定义文件名"dev&quo…...

嵌入式学习第二十四天--网络 服务器

服务器模型 tcp服务器: socket bind listen accept recv/send close 1.支持多客户端访问 //单循环服务器 socket bind listen while(1) { accept while(1) { recv/send } } close 2.支持多客户端同时访问 (并发能力) 并发服务器 socket bind …...

tcp/ip协议配置参数有哪些?tcp/ip协议需要设置的参数有哪些

TCP/IP协议的配置参数是确保网络设备能够正确接入互联网并与其他设备进行通信的关键设置。这些参数主要包括以下几个方面: 1. IP地址 定义:IP地址是网络中设备的唯一标识符,用于标识和定位设备。它由32位二进制数组成,通常采用点…...

我有点担心开始AI中台了

有个特点历史教训是很难吸取的 从大数据开始就是一窝蜂的去搞,不管有没有什么数据量。反正要来个Hadoop。其实有些企业数据一块硬盘都放得下。 微服务来了,也不管自己的系统是不是适合微服务。我个人经验得出,to B和to G的业务场景&#xf…...

《用Python+PyGame开发双人生存游戏!源码解析+完整开发思路分享》

导语​ "你是否想过用Python开发一款可玩性高的双人合作游戏?本文将分享如何从零开始实现一款类《吸血鬼幸存者》的生存射击游戏!包含完整源码解析、角色系统设计、敌人AI逻辑等核心技术点,文末提供完整代码包下载!" 哈…...

优选算法系列(1. 双指针_上)

目录 双指针 一:移动零(easy) 题目链接:移动零 解法: 代码: 二:复写零(easy) 题目链接:复写零 ​编辑 解法: 代码: 三:快乐…...

永洪科技深度分析实战,零售企业的销量预测

随着人工智能技术的不断发展,智能预测已经成为各个领域的重要应用之一。现在,智能预测技术已经广泛应用于金融、零售、医疗、能源等领域,为企业和个人提供决策支持。 智能预测技术通过分析大量的数据,利用机器学习和深度学习算法…...

c语言笔记 函数参数的等价(上)

这三种写法在 C 语言中是等价的,因为它们都用于声明一个指向二维数组的指针,或者用于声明一个二维数组作为函数参数。它们的等价性源于 C 语言中数组和指针之间的密切关系。让我们逐一分析这三种写法: 在C语言中,当数组作为函数参…...

hive面试题--left join的坑

student 表&#xff1a; 课程表course: 1、key为null, 不关联 select * from student s left join course c on s.id c.s_id;2、on中过滤条件 与 where 过滤条件区别 on and c.id<>‘1001’ 先过滤右表数据&#xff0c;然后与左表关联 select * from student s le…...

CEH与OSCP:网络安全认证对比分析

在网络安全领域&#xff0c;渗透测试被视为至关重要的一环&#xff0c;帮助企业检测和修复系统漏洞。为提升行业标准&#xff0c;许多认证应运而生&#xff0c;其中CEH和OSCP作为行业认可度较高的认证&#xff0c;广泛被网络安全从业者选择。尽管这两者都涉及渗透测试领域&…...

HTML 属性详解:为网页元素赋予更多功能

在构建网页的过程中&#xff0c;HTML 是基础的标记语言&#xff0c;而 HTML 属性则是为 HTML 元素提供附加信息的重要组成部分。 一、属性的基本概念与使用 属性通常出现在 HTML 标签的开始标签内&#xff0c;以 “name"value"” 的形式存在。这里的 “name” 是属…...

Ceph(2):Ceph简介

1 Ceph简介 Ceph使用C语言开发&#xff0c;遵循LGPL协议开源。Sage Weil(Ceph论文发表者)于2011年创立了以Inktank公司主导Ceph的开发和社区维护。2014年Redhat收购inktank公司&#xff0c;并发布Inktank Ceph企业版&#xff08;ICE&#xff09;软件&#xff0c;业务场景聚焦云…...

国产编辑器EverEdit - 设置文件类型关联为EverEdit

1 设置-文件关联 1.1 应用场景 文件关联是指在文件管理器中双击某类型的文件&#xff0c;操作系统自动调用可以打开该文件的应用程序&#xff0c;比如&#xff1a;用户双击XXXX.txt文件&#xff0c;系统默认会使用记事本打开该文件。   由于各行各业都会定义特有的文件类型&…...

2025网络安全工程师:软考新挑战与职业发展探析

网络安全工程师的崛起 随着信息技术的迅猛发展&#xff0c;网络安全问题日益凸显&#xff0c;网络安全工程师这一职业逐渐受到社会各界的广泛关注。特别是在2025年&#xff0c;随着各项网络安全法规的完善和实施&#xff0c;网络安全工程师的角色愈发重要。他们不仅是企业信息…...

设计模式之建造者模式:原理、实现与应用

引言 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它通过将复杂对象的构建过程分解为多个简单的步骤&#xff0c;使得对象的创建更加灵活和可维护。建造者模式特别适用于构建具有多个组成部分的复杂对象。本文将深入探讨建造者模式的原…...

【Leetcode 每日一题 - 补卡】2070. 每一个查询的最大美丽值

问题背景 给你一个二维整数数组 i t e m s items items&#xff0c;其中 i t e m s [ i ] [ p r i c e i , b e a u t y i ] items[i] [price_i, beauty_i] items[i][pricei​,beautyi​] 分别表示每一个物品的 价格 和 美丽值 。 同时给你一个下标从 0 0 0 开始的整数数…...

雪藏HsFreezer(游戏冻结工具) v2.21

HsFreezer 是一款让你可以随心冻结游戏的软件(游戏暂停软件、系统优化软件、进程管理软件),想玩就玩,想停就停,快捷键随心瞬发,单锁模式极致的丝滑切换,当然,不止适用游戏。更有丰富的特色系统优化功能。 PC主机,win掌机,笔记本--无脑装就对了,超大按键超大列表,触控盲操,非常巴…...

2019年蓝桥杯第十届CC++大学B组真题及代码

目录 1A&#xff1a;组队&#xff08;填空5分_手算&#xff09; 2B&#xff1a;年号字符&#xff08;填空5分_进制&#xff09; 3C&#xff1a;数列求值&#xff08;填空10分_枚举&#xff09; 4D&#xff1a;数的分解&#xff08;填空10分&#xff09; 5E&#xff1a;迷宫…...

前端安全面试题汇总及参考答案

目录 简述 XSS 攻击的原理及三种常见类型(存储型、反射型、DOM 型) 如何在前端防御 XSS 攻击?列举编码、过滤、CSP 策略的具体实现方式 富文本编辑器场景下如何安全处理用户输入的 HTML 内容? 如何通过 HttpOnly 属性增强 Cookie 安全性?它与 XSS 防御的关系是什么? …...

修复ubuntu下找不到音频设备的问题

出现问题的状态&#xff1a; ALSA 已正确识别到 ZOOM H2n 设备&#xff08;card 1&#xff09;sounddevice 库&#xff08;依赖 PortAudio&#xff09;未能正确枚举设备 修复方法&#xff1a; 1. 强制 sounddevice 使用 ALSA 后端 默认情况下&#xff0c;sounddevice 可能尝…...

⭐LeetCode周赛 3468. 可行数组的数目——暴力与数学⭐

⭐LeetCode周赛 3468. 可行数组的数目——暴力与数学⭐ 示例 1&#xff1a; 输入&#xff1a;original [1,2,3,4], bounds [[1,2],[2,3],[3,4],[4,5]] 输出&#xff1a;2 解释&#xff1a; 可能的数组为&#xff1a; [1, 2, 3, 4] [2, 3, 4, 5] 示例 2&#xff1a; 输入&…...

在线json转ArkTs-harmonyos

轻松将 JSON 数据转换为类型安全的 ArkTs 接口。快速准确地生成代码&#xff0c;提升开发效率&#xff0c;告别手动编写&#xff0c;让您的开发流程更加流畅&#xff01; gotool...

Vue 实现AI对话和AI绘图(AIGC)人工智能

我司是主要是负责AIGC人工智能化平台的项目&#xff0c;俗称内容创作及智能工具平台。 授人以鱼不如授人以渔 首先我们要明白AIGC中前端需要做什么 会用到哪些技术栈 。 AIGC前端需要用到的技术栈:Vue&#xff0c;Markdown&#xff0c;SSE。就这个三件套。 前沿:有人觉得AI对…...

Visual Studio Code 基本使用指南

Visual Studio Code&#xff08;简称 VSCode&#xff09;是一款由微软开发的免费、开源、跨平台的代码编辑器&#xff0c;凭借其轻量级设计、丰富的插件生态和强大的功能&#xff0c;成为全球开发者的首选工具。本文将从安装配置到核心功能&#xff0c;全面解析 VSCode 的基本使…...

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

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

网络安全工具nc(NetCat)

NetCat是一个非常简单的Unix工具&#xff0c;可以读、写TCP或UDP网络连接(network connection)。它被设计成一个可靠的后端(back-end)工具&#xff0c;能被其它的程序程序或脚本直接地或容易地驱动。同时&#xff0c;它又是一个功能丰富的 网络调试和开发工具&#xff0c;因为它…...