Android实现TCP服务端客户端(附带源码)

【Android开发实战】实现TCP服务端与客户端通信(超详细讲解+完整代码+原理解析)

下面开始正文:


一、项目介绍

在许多需要局域网通信、设备互联或点对点数据交换的应用场景中,TCP(Transmission Control Protocol) 提供了可靠、面向连接的网络传输机制。Android 平台下实现 TCP 服务端与客户端,可以用于以下场景:

  • 局域网聊天:类似局域网广播短信的实时通信;

  • 远程控制:通过手机控制局域网内的智能设备;

  • 文件传输:在局域网中相互传输大文件;

  • 分布式游戏:多人小游戏通过 TCP 保证数据一致性;

  • 物联网网关:手机作为网关接收并转发传感器数据。

本教程将从TCP 原理Android 网络模型多线程与 HandlerSocket 读写机制连接管理通信协议设计完整整合代码,全方位剖析 Android 上实现 TCP 服务端(Server)与客户端(Client)的全过程。所有示例使用原生 Java 与 AndroidX 组件。本文结构:

  • 项目背景与需求

  • TCP 基础与 Android 网络编程

  • 架构设计与通信协议

  • 线程模型与生命周期管理

  • 完整整合版代码(一个代码块,详细注释)

  • 代码解读与工作原理

  • 性能优化与异常处理

  • 安全性与扩展思路

  • 项目总结


二、TCP基础与Android网络编程

  1. TCP协议特点

    • 面向连接:通信前需建立三次握手,断开时四次挥手;

    • 可靠传输:保证数据按序到达,无丢包、无重复;

    • 流式传输:以字节流形式传输,无消息边界;

  2. Java Socket编程模型

    • ServerSocket:在服务端监听指定端口,accept() 阻塞等待连接;

    • Socket:代表客户端连接,提供 getInputStream()getOutputStream()

    • 数据读写需在独立线程,避免阻塞 UI;

  3. Android 网络限制

    • Android 9(API 28)及以上,默认禁止明文网络,需要在 Manifest 中允许;

    • 权限:在 AndroidManifest.xml 中声明 <uses-permission android:name="android.permission.INTERNET" />

    • 线程:所有网络操作必须在非 UI 线程;

  4. 多客户端管理

    • 服务端使用 ServerSocket 接受多个 Socket,为每个连接启动独立线程或线程池;

    • 使用 ConcurrentHashMap 缓存客户端连接,支持向指定客户端发送消息;

  5. 数据协议设计

    • 约定消息格式,如 长度(4字节)+类型(1字节)+正文

    • 使用 DataInputStreamDataOutputStream 按协议读写;

    • 支持心跳机制检测连接活跃度;


三、架构设计与线程模型

  1. 整体架构

    • Service:后台常驻 TCPService,同时承载服务端与客户端逻辑;

    • Activity/UI:用于启动服务、显示日志与发送消息;

    • Handler:将网络线程的消息回传主线程更新 UI;

    • ThreadPoolExecutor:管理客户端连接线程池,避免无限制创建线程;

  2. TCP服务端

    • ServerSocket serverSocket = new ServerSocket(PORT)

    • 在单独线程循环调用 accept()

    • 每次新连接后,提交 ClientHandler 到线程池;

  3. TCP客户端

    • 在需要的模块(Activity/另一个 Service)创建 Socket 并连接服务端地址;

    • 启动读写线程处理通信;

  4. 消息分发

    • 定义 Message 类型常量:MSG_NEW_CONNECTIONMSG_RECEIVEDMSG_SENT

    • Service 内部 Handler 接收网络线程通过 handler.obtainMessage(...).sendToTarget() 发送的状态;

    • Activity 通过注册 BroadcastReceiver 或绑定 Service 的 LocalBinder 获取日志更新;

  5. 生命周期管理

    • 绑定/解绑 Service 时保持连接;

    • 在 Service onDestroy() 中关闭 ServerSocket、所有 Socket 及线程池;

    • 在客户端断线时自动重连或提示;


四、完整整合版代码

// ======================================================
// AndroidManifest.xml
// - INTERNET 权限,注册 TCPService
// ======================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.tcpsocket">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application ...>
        <service android:name=".TCPService"
                 android:exported="false"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

// ======================================================
// res/layout/activity_main.xml
// - UI:日志显示、输入框、按钮
// ======================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent" android:padding="16dp">

    <ScrollView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1">
        <TextView
            android:id="@+id/tvLog"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textColor="#000000"/>
    </ScrollView>

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="输入消息"/>

    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送给客户端"/>

    <Button
        android:id="@+id/btnStartService"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动 TCP 服务"/>

</LinearLayout>

// ======================================================
// TCPService.java
// - 后台 Service:实现 TCP 服务端与客户端双功能
// ======================================================
package com.example.tcpsocket;

import android.app.Service;
import android.content.Intent;
import android.os.*;
import androidx.annotation.Nullable;
import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class TCPService extends Service {

    // 日志消息类型
    private static final int MSG_LOG = 1;

    // 服务端监听端口
    private static final int SERVER_PORT = 8688;

    // Handler 回到主线程更新日志
    private Handler uiHandler;

    // ServerSocket 与线程池
    private ServerSocket serverSocket;
    private ExecutorService clientPool;

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化 Handler 于主线程
        uiHandler = new Handler(Looper.getMainLooper(), msg -> {
            if (msg.what == MSG_LOG) {
                // 通过广播或 Binder 传递给 Activity
                Intent intent = new Intent("ACTION_LOG");
                intent.putExtra("log", (String) msg.obj);
                sendBroadcast(intent);
            }
            return true;
        });

        // 初始化线程池:最多 10 个客户端
        clientPool = Executors.newFixedThreadPool(10);

        // 启动服务端线程
        new Thread(this::startServer).start();
    }

    private void startServer() {
        try {
            serverSocket = new ServerSocket(SERVER_PORT);
            log("TCP 服务已启动,监听端口 " + SERVER_PORT);

            while (!serverSocket.isClosed()) {
                Socket client = serverSocket.accept();  // 阻塞
                log("客户端已连接: " + client.getRemoteSocketAddress());
                // 交给线程池处理
                clientPool.execute(new ClientHandler(client));
            }
        } catch (IOException e) {
            log("Server异常: " + e.getMessage());
        }
    }

    // 客户端连接处理
    private class ClientHandler implements Runnable {
        private Socket socket;
        private DataInputStream in;
        private DataOutputStream out;

        ClientHandler(Socket client) {
            this.socket = client;
            try {
                in  = new DataInputStream(client.getInputStream());
                out = new DataOutputStream(client.getOutputStream());
            } catch (IOException e) {
                log("获取流失败: " + e.getMessage());
            }
        }

        @Override
        public void run() {
            try {
                while (!socket.isClosed()) {
                    // 读取长度+消息
                    int len = in.readInt();
                    byte[] data = new byte[len];
                    in.readFully(data);
                    String msg = new String(data, "UTF-8");
                    log("收到客户端消息: " + msg);

                    // 回射
                    String response = "服务已收到: " + msg;
                    byte[] respData = response.getBytes("UTF-8");
                    out.writeInt(respData.length);
                    out.write(respData);
                    out.flush();
                    log("已回射消息给客户端");
                }
            } catch (IOException e) {
                log("客户端断开: " + e.getMessage());
            } finally {
                close();
            }
        }

        void close() {
            try {
                socket.close();
            } catch (IOException ignored) { }
        }
    }

    // 发送日志到 UI
    private void log(String text) {
        Message msg = uiHandler.obtainMessage(MSG_LOG, text);
        uiHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try { serverSocket.close(); } catch (IOException ignored) { }
        clientPool.shutdownNow();
        log("TCP 服务已停止");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

// ======================================================
// MainActivity.java
// - 控制 UI:启动/停止服务,发送消息,显示日志
// ======================================================
package com.example.tcpsocket;

import android.content.*;
import android.os.Bundle;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import java.io.*;
import java.net.*;

public class MainActivity extends AppCompatActivity {

    private TextView tvLog;
    private EditText etMessage;
    private Button btnSend, btnStartService;

    private Socket clientSocket;
    private DataOutputStream out;
    private DataInputStream in;
    private Thread clientThread;

    private BroadcastReceiver logReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String log = intent.getStringExtra("log");
            tvLog.append(log + "\n");
        }
    };

    @Override
    protected void onCreate(Bundle s) {
        super.onCreate(s);
        setContentView(R.layout.activity_main);
        tvLog            = findViewById(R.id.tvLog);
        etMessage        = findViewById(R.id.etMessage);
        btnSend          = findViewById(R.id.btnSend);
        btnStartService  = findViewById(R.id.btnStartService);

        // 注册日志广播
        registerReceiver(logReceiver, new IntentFilter("ACTION_LOG"));

        // 启动服务端
        btnStartService.setOnClickListener(v -> {
            Intent intent = new Intent(this, TCPService.class);
            startService(intent);
        });

        // 连接客户端并发送
        btnSend.setOnClickListener(v -> {
            String msg = etMessage.getText().toString().trim();
            if (msg.isEmpty()) return;
            if (clientSocket == null || clientSocket.isClosed()) {
                connectToServer();
            }
            sendMessage(msg);
        });
    }

    // 启动客户端线程
    private void connectToServer() {
        clientThread = new Thread(() -> {
            try {
                clientSocket = new Socket("127.0.0.1", 8688);
                out = new DataOutputStream(clientSocket.getOutputStream());
                in  = new DataInputStream(clientSocket.getInputStream());
                runOnUiThread(() -> tvLog.append("已连接到服务端\n"));

                // 读取回射
                while (!clientSocket.isClosed()) {
                    int len = in.readInt();
                    byte[] data = new byte[len];
                    in.readFully(data);
                    String resp = new String(data, "UTF-8");
                    runOnUiThread(() -> tvLog.append("收到服务端回射: " + resp + "\n"));
                }
            } catch (IOException e) {
                runOnUiThread(() -> tvLog.append("客户端异常: " + e.getMessage() + "\n"));
            }
        });
        clientThread.start();
    }

    // 发送消息给服务端
    private void sendMessage(String msg) {
        new Thread(() -> {
            try {
                byte[] data = msg.getBytes("UTF-8");
                out.writeInt(data.length);
                out.write(data);
                out.flush();
                runOnUiThread(() -> tvLog.append("已发送: " + msg + "\n"));
            } catch (IOException e) {
                runOnUiThread(() -> tvLog.append("发送失败: " + e.getMessage() + "\n"));
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(logReceiver);
        try { clientSocket.close(); } catch (IOException ignored) { }
    }
}

五、代码解读

  • TCPService

    • 使用 ServerSocket 在后台线程监听端口 8688;

    • 接受 Socket 后由线程池 clientPool 提交 ClientHandler 处理;

    • ClientHandler 中循环读取 in.readInt()+readFully() 解包、解析消息,使用相同协议回写;

    • 通过 Handler 发送日志到主线程并广播给 MainActivity

    • Service onDestroy() 中关闭 ServerSocket 并终止线程池。

  • MainActivity

    • 注册 BroadcastReceiver 接收并展示日志;

    • 点击“启动TCP服务”启动 TCPService

    • 点击“发送消息”时,若尚未创建客户端连接,则先在新线程中建立 Socket("127.0.0.1",8688)

    • 建立后循环读取回射消息并更新 UI;

    • 每次发送时在新线程调用 out.writeInt()+write()

    • 断开时 onDestroy() 关闭 Socket

  • 协议设计

    • 前后端约定:4字节长度 + n字节UTF-8消息

    • 使用 DataInputStream/DataOutputStream 简化操作;

  • 多线程与UI

    • 服务端、客户端均在后台线程执行网络操作;

    • UI 更新使用 runOnUiThread()Handler,保证线程安全;


六、性能优化与异常处理

  1. 连接管理

    • 服务端使用固定大小线程池,避免大量并发连接导致 OOM;

    • 客户端可实现断线重连机制;

  2. 协议健壮性

    • 增加心跳包定时发送,服务端收到超时未心跳则关闭连接;

    • 数据包校验:在消息体前后添加 CRC 或 MD5 校验;

  3. 异常捕获

    • IOException 捕获后,清理资源并向 UI 通知;

    • 防止 EOFException 导致线程意外终止;

  4. 本地回显与缓冲

    • 在 UI 日志中使用队列缓存,避免频繁 TextView.append() 导致卡顿;

    • 对底层 Socket 流进行缓冲:使用 BufferedInputStream/BufferedOutputStream

  5. 网络状态监听

    • 监听 Wi-Fi 或移动网络变化,断网时停止服务;

    • 可结合 BroadcastReceiver 监听 ConnectivityManager.CONNECTIVITY_ACTION


七、安全性与扩展思路

  1. 权限与安全

    • 默认 INTERNET 权限已足够;

    • 若要在局域网外通信,需考虑 SSL/TLS 加密,使用 SSLSocketFactory

    • 自定义证书校验与双向认证提高安全性;

  2. 消息协议升级

    • 增加消息类型标志位,实现多种消息(文本、文件、心跳);

    • 使用 JSON、Protocol Buffers 或 Thrift 序列化消息体;

  3. 文件传输

    • 在协议中增加文件头(文件名长度、文件名、文件大小),分片发送;

    • 服务端重组接收到的文件片段并保存;

  4. 跨平台通信

    • 服务端可用 Java、Python、C# 等多语言实现;

    • 客户端仅需保持相同协议即可互通;

  5. UI扩展

    • MainActivity 中使用 RecyclerView 替代 TextView 日志列表;

    • 添加消息输入历史记录与保存至本地数据库;


八、项目总结

本文从TCP协议基础Android Socket编程实践,再到完整整合代码性能、安全扩展,系统地讲解了如何在 Android 平台实现 TCP 服务端与客户端通信。通过 Service+线程池管理服务端,通过独立线程管理客户端,通过 Handler 和广播机制更新 UI,实现了一个简洁而高效的 TCP Minichat。您可以基于本教程:

  • 打造局域网聊天室、文件传输工具;

  • 扩展为远程控制、物联网通信;

  • 增加安全加密与协议层封装;

  • 集成到 MVVM/Network Module 提高复用性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值