Android TCP封装工具类

news2025/3/12 2:58:30

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"; // 服务器IP
    private static final int SERVER_PORT = 8080; // 服务器端口
    private TcpClient tcpClient;

    @Override
    protected 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());
    }

    @Override
    public void onConnected() {
        Log.d("TcpClient", "Connected to server");
    }

    @Override
    public void onDisconnected() {
        Log.d("TcpClient", "Disconnected from server");
    }

    @Override
    public void onDataReceived(byte[] data) {
        String message = new String(data);
        Log.d("TcpClient", "Received data: " + message);
    }

    @Override
    public void onError(String error) {
        Log.e("TcpClient", "Error: " + error);
    }

    @Override
    protected 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.Socket

class MainActivity : AppCompatActivity(), TcpClient.TcpListener {

    private val serverIp = "192.168.1.100" // 服务器IP
    private val serverPort = 8080 // 服务器端口
    private lateinit var tcpClient: TcpClient

    override 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? = null
    private var inputStream: InputStream? = null
    private var outputStream: OutputStream? = null
    private var isConnected = false
    private val scope = CoroutineScope(Dispatchers.IO)
    private var heartbeatJob: Job? = null

    fun connect(ip: String, port: Int) {
        scope.launch {
            try {
                socket = Socket().apply {
                    connect(InetSocketAddress(ip, port), 5000) // 5秒超时
                }
                inputStream = socket?.getInputStream()
                outputStream = socket?.getOutputStream()
                isConnected = true

                withContext(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 = false

                withContext(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: Int

            while (isConnected) {
                try {
                    bytesRead = inputStream?.read(buffer) ?: -1
                    if (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)
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

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

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

⭐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、短连接、长…

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

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

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 错误…

在本地部署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…

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,就…

Unity 基础知识总结(持续更新中...)

引擎基础 Unity有哪几个主要窗口? Scene窗口 用于场景搭建和UI界面拼接 Game窗口 游戏运行预览 Hierarchy窗口 查看和调整场景对象层级结构 Project窗口 游戏工程资源 Inspector创建 属性查看器,属性设置、脚本组件挂载 Unity提供了几种光源…

IDEA接入阿里云百炼中免费的通义千问[2025版]

安装deepseek 上一篇文章IDEA安装deepseek最新教程2025中说明了怎么用idea安装codeGPT插件,并接入DeepSeek,无奈接入的官方api已经不能使用了,所以我们尝试从其他地方接入 阿里云百炼https://bailian.console.aliyun.com/ 阿里云百炼‌是阿…

3.03-3.09 Web3 游戏周报:Sunflower Land 周留存率 74.2%,谁是本周最稳链游?

回顾上周的区块链游戏概况,查看 Footprint Analytics 与 ABGA 最新发布的数据报告。 【3.03–3.09】Web3 游戏行业动态 Sui 背后开发公司 Mysten Labs 宣布收购游戏开发平台 ParasolYescoin 创始人因合伙人纠纷被警方带走,案件升级为刑事案件Animoca B…