【Android开发实战】实现TCP服务端与客户端通信(超详细讲解+完整代码+原理解析)
下面开始正文:
一、项目介绍
在许多需要局域网通信、设备互联或点对点数据交换的应用场景中,TCP(Transmission Control Protocol) 提供了可靠、面向连接的网络传输机制。Android 平台下实现 TCP 服务端与客户端,可以用于以下场景:
-
局域网聊天:类似局域网广播短信的实时通信;
-
远程控制:通过手机控制局域网内的智能设备;
-
文件传输:在局域网中相互传输大文件;
-
分布式游戏:多人小游戏通过 TCP 保证数据一致性;
-
物联网网关:手机作为网关接收并转发传感器数据。
本教程将从TCP 原理、Android 网络模型、多线程与 Handler、Socket 读写机制、连接管理、通信协议设计 到完整整合代码,全方位剖析 Android 上实现 TCP 服务端(Server)与客户端(Client)的全过程。所有示例使用原生 Java 与 AndroidX 组件。本文结构:
-
项目背景与需求
-
TCP 基础与 Android 网络编程
-
架构设计与通信协议
-
线程模型与生命周期管理
-
完整整合版代码(一个代码块,详细注释)
-
代码解读与工作原理
-
性能优化与异常处理
-
安全性与扩展思路
-
项目总结
二、TCP基础与Android网络编程
-
TCP协议特点
-
面向连接:通信前需建立三次握手,断开时四次挥手;
-
可靠传输:保证数据按序到达,无丢包、无重复;
-
流式传输:以字节流形式传输,无消息边界;
-
-
Java Socket编程模型
-
ServerSocket
:在服务端监听指定端口,accept()
阻塞等待连接; -
Socket
:代表客户端连接,提供getInputStream()
与getOutputStream()
; -
数据读写需在独立线程,避免阻塞 UI;
-
-
Android 网络限制
-
Android 9(API 28)及以上,默认禁止明文网络,需要在 Manifest 中允许;
-
权限:在
AndroidManifest.xml
中声明<uses-permission android:name="android.permission.INTERNET" />
; -
线程:所有网络操作必须在非 UI 线程;
-
-
多客户端管理
-
服务端使用
ServerSocket
接受多个Socket
,为每个连接启动独立线程或线程池; -
使用
ConcurrentHashMap
缓存客户端连接,支持向指定客户端发送消息;
-
-
数据协议设计
-
约定消息格式,如
长度(4字节)+类型(1字节)+正文
; -
使用
DataInputStream
、DataOutputStream
按协议读写; -
支持心跳机制检测连接活跃度;
-
三、架构设计与线程模型
-
整体架构
-
Service:后台常驻
TCPService
,同时承载服务端与客户端逻辑; -
Activity/UI:用于启动服务、显示日志与发送消息;
-
Handler:将网络线程的消息回传主线程更新 UI;
-
ThreadPoolExecutor:管理客户端连接线程池,避免无限制创建线程;
-
-
TCP服务端
-
ServerSocket serverSocket = new ServerSocket(PORT)
; -
在单独线程循环调用
accept()
; -
每次新连接后,提交
ClientHandler
到线程池;
-
-
TCP客户端
-
在需要的模块(Activity/另一个 Service)创建
Socket
并连接服务端地址; -
启动读写线程处理通信;
-
-
消息分发
-
定义
Message
类型常量:MSG_NEW_CONNECTION
、MSG_RECEIVED
、MSG_SENT
; -
Service 内部
Handler
接收网络线程通过handler.obtainMessage(...).sendToTarget()
发送的状态; -
Activity 通过注册
BroadcastReceiver
或绑定 Service 的LocalBinder
获取日志更新;
-
-
生命周期管理
-
绑定/解绑 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
,保证线程安全;
-
六、性能优化与异常处理
-
连接管理
-
服务端使用固定大小线程池,避免大量并发连接导致 OOM;
-
客户端可实现断线重连机制;
-
-
协议健壮性
-
增加心跳包定时发送,服务端收到超时未心跳则关闭连接;
-
数据包校验:在消息体前后添加 CRC 或 MD5 校验;
-
-
异常捕获
-
在
IOException
捕获后,清理资源并向 UI 通知; -
防止
EOFException
导致线程意外终止;
-
-
本地回显与缓冲
-
在 UI 日志中使用队列缓存,避免频繁
TextView.append()
导致卡顿; -
对底层
Socket
流进行缓冲:使用BufferedInputStream
/BufferedOutputStream
;
-
-
网络状态监听
-
监听 Wi-Fi 或移动网络变化,断网时停止服务;
-
可结合 BroadcastReceiver 监听
ConnectivityManager.CONNECTIVITY_ACTION
。
-
七、安全性与扩展思路
-
权限与安全
-
默认
INTERNET
权限已足够; -
若要在局域网外通信,需考虑 SSL/TLS 加密,使用
SSLSocketFactory
; -
自定义证书校验与双向认证提高安全性;
-
-
消息协议升级
-
增加消息类型标志位,实现多种消息(文本、文件、心跳);
-
使用 JSON、Protocol Buffers 或 Thrift 序列化消息体;
-
-
文件传输
-
在协议中增加文件头(文件名长度、文件名、文件大小),分片发送;
-
服务端重组接收到的文件片段并保存;
-
-
跨平台通信
-
服务端可用 Java、Python、C# 等多语言实现;
-
客户端仅需保持相同协议即可互通;
-
-
UI扩展
-
在
MainActivity
中使用RecyclerView
替代TextView
日志列表; -
添加消息输入历史记录与保存至本地数据库;
-
八、项目总结
本文从TCP协议基础到Android Socket编程实践,再到完整整合代码与性能、安全扩展,系统地讲解了如何在 Android 平台实现 TCP 服务端与客户端通信。通过 Service+线程池管理服务端,通过独立线程管理客户端,通过 Handler 和广播机制更新 UI,实现了一个简洁而高效的 TCP Minichat。您可以基于本教程:
-
打造局域网聊天室、文件传输工具;
-
扩展为远程控制、物联网通信;
-
增加安全加密与协议层封装;
-
集成到 MVVM/Network Module 提高复用性。