Andorid达成TCP通讯
Andorid实现TCP通讯
android官方文档:https://developer.android.google.cn/reference/kotlin/java/net/Socket?authuser=002
软件一共有两种模式分别是TCP客户端模式和服务端模式
上效果
客户端模式(点对点通讯,一对一的意思):
服务端模式(一对多,广播模式):
一、图片资源
将图片复制保存于文件目录drawable文件夹下
一、介绍
服务端(Server)步骤:
创建ServerSocket:服务端通过创建一个ServerSocket对象并绑定到指定端口来监听客户端的连接请求。
等待客户端连接:调用ServerSocket的accept()方法,该方法会阻塞直到有客户端连接。当连接成功时,accept()方法返回一个Socket对象,代表与客户端的连接。
获取输入输出流:通过返回的Socket对象获取输入流(InputStream)和输出流(OutputStream),用于与客户端进行数据读写。
读写数据:通过输入流读取客户端发送的数据,通过输出流向客户端发送数据。
关闭连接:通信完成后,关闭输入输出流和Socket。
客户端(Client)步骤:
创建Socket:客户端创建一个Socket对象,指定服务端的IP地址和端口号,尝试连接服务端。
获取输入输出流:连接建立后,通过Socket对象获取输入流和输出流。
读写数据:通过输出流向服务端发送数据,通过输入流读取服务端返回的数据。
关闭连接:通信完成后,关闭输入输出流和Socket。
二、大致流程
1.初始界面,定义一个Switch控件实现按键切换,最底下定义ScrollView,实现一个消息的滚动

代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#D7F7FF"
android:padding="16dp">
<Switch
android:id="@+id/switch1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:checked="true"
android:text="当前连接模式为:TCPClient" />
<TextView
android:id="@+id/tvStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="未连接"
android:textColor="#000"
android:textSize="18sp" />
<!-- 服务器模式下的端口输入 -->
<LinearLayout
android:id="@+id/serverPortLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<EditText
android:id="@+id/etServerPort"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="服务器端口"
android:inputType="number"
android:text="8080" />
</LinearLayout>
<!-- 客户端模式下的IP和端口输入 -->
<LinearLayout
android:id="@+id/clientLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/etServerIP"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="服务器IP"
android:text="192.168.1.100" />
<EditText
android:id="@+id/etClientPort"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="端口"
android:inputType="number"
android:text="8080" />
</LinearLayout>
<Button
android:id="@+id/btnConnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="连接" />
<Button
android:id="@+id/btnDisconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="断开连接"
android:enabled="false" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ccc"
android:layout_marginVertical="8dp" />
<EditText
android:id="@+id/etMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000"
android:hint="输入消息" />
<Button
android:id="@+id/btnSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送"
android:enabled="false" />
<TextView
android:id="@+id/tvreceivet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送的消息:"
android:textStyle="bold" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="0.2"
app:srcCompat="@drawable/people" />
<TextView
android:id="@+id/tvMessages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#000"
android:textSize="20dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
在这里面界面采用的是LinearLayout即线性布局,这种好处是有着极大的适配性,线性布局有两种,水平布局和垂直布局,有兴趣的可以尝试一下。
新建文件
我们找到java文件下新建类,将名称命名为SimpleSocketServer
代码如下:
SimpleSocketServer.java
package com.example.soket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 实现TCP通信服务端功能,支持多客户端连接和消息广播
*/
public class SimpleSocketServer {
// 服务器套接字,用于监听客户端连接
private ServerSocket serverSocket;
// 客户端连接处理器列表,用于管理所有连接的客户端
private List<ClientHandler> clients = new ArrayList<>();
// 服务器运行状态标志
private boolean isRunning = false;
/**
* 启动服务器并在指定端口监听
* port为端口号
*/
public void start(int port) {
try {
// 创建服务器套接字,绑定到指定端口
serverSocket = new ServerSocket(port);
isRunning = true;
System.out.println("服务器启动,监听端口: " + port);
// 主循环,持续接受客户端连接
while (isRunning) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接: " + clientSocket.getInetAddress());
// 为每个客户端创建独立的处理器,多线程防止卡死
ClientHandler clientHandler = new ClientHandler(clientSocket);
clients.add(clientHandler);
// 在新线程中处理客户端通信
new Thread(clientHandler).start();
}
} catch (IOException e) {
System.out.println("服务器异常: " + e.getMessage());
}
}
/**
* 停止服务器并释放资源
*/
public void stop() {
isRunning = false;
try {
// 停止所有客户端连接
for (ClientHandler client : clients) {
client.stop();
}
// 关闭服务器套接字
if (serverSocket != null) {
serverSocket.close();
}
System.out.println("服务器已停止");
} catch (IOException e) {
//否抛出异常
System.out.println("停止服务器时出错: " + e.getMessage());
}
}
/**
* 单个客户端的通信
*/
private class ClientHandler implements Runnable {
// 客户端套接字
private Socket clientSocket;
// 输出流,用于向客户端发送数据
private PrintWriter out;
// 输入流,用于接收客户端数据
private BufferedReader in;
// 客户端连接状态标志
private boolean isConnected = false;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
/**
* 客户端通信处理主方法
*/
@Override
public void run() {
try {
// 初始化输入输出流
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
isConnected = true;
String inputLine;
// 持续读取客户端发送的消息
while (isConnected && (inputLine = in.readLine()) != null) {
System.out.println("收到消息: " + inputLine);
// 将收到的消息广播给所有客户端
broadcastMessage("客户端说: " + inputLine);
}
} catch (IOException e) {
System.out.println("客户端处理异常: " + e.getMessage());
} finally {
// 确保连接被正确关闭
stop();
}
}
/**
* 向当前客户端发送消息
* message为要发送消息的内容
*/
public void sendMessage(String message) {
if (out != null) {
out.println(message);
}
}
/**
* 停止当前客户端连接并释放资源
*/
public void stop() {
isConnected = false;
try {
// 关闭所有流和套接字
if (in != null) in.close();
if (out != null) out.close();
if (clientSocket != null) clientSocket.close();
// 从客户端列表中移除
clients.remove(this);
System.out.println("客户端断开连接");
} catch (IOException e) {
System.out.println("关闭客户端连接时出错: " + e.getMessage());
}
}
}
/**
* 广播消息给所有连接的客户端,服务端为一对多
*/
private void broadcastMessage(String message) {
for (ClientHandler client : clients) {
client.sendMessage(message);
}
}
/**
* 服务器主入口方法
*/
public static void main(String[] args) {
SimpleSocketServer server = new SimpleSocketServer();
// 启动服务器,监听2333端口
server.start(2333);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("正在关闭服务器...");
server.stop();
}));
// 控制台输入处理:支持服务器向所有客户端发送消息
try (BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {
String command;
// 读取控制台输入
while ((command = consoleReader.readLine()) != null) {
if ("stop".equalsIgnoreCase(command)) {
// 输入"stop"命令停止服务器
server.stop();
break;
} else {
// 其他输入作为消息广播给所有客户端
server.broadcastMessage("服务器说: " + command);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
MainActivity.java:
package com.example.soket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private TextView tvStatus, tvMessages, tvrect;
private EditText etServerIP, etClientPort, etServerPort, etMessage;
private Button btnConnect, btnDisconnect, btnSend;
private LinearLayout serverPortLayout, clientLayout;
private Switch aSwitch;
// 客户端相关
private Socket clientSocket;
private PrintWriter clientOut;
private BufferedReader clientIn;
private boolean isClientConnected = false;
// 服务器相关
private ServerSocket serverSocket;
private Socket serverClientSocket;
private PrintWriter serverOut;
private BufferedReader serverIn;
private boolean isServerRunning = false;
private boolean isServerClientConnected = false;
private Handler handler = new Handler(Looper.getMainLooper());
private ExecutorService executorService = Executors.newFixedThreadPool(2);
private boolean isServerMode = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
setupListeners();
}
private void initViews() {
tvStatus = findViewById(R.id.tvStatus);
tvMessages = findViewById(R.id.tvMessages);
etServerIP = findViewById(R.id.etServerIP);
etClientPort = findViewById(R.id.etClientPort);
etServerPort = findViewById(R.id.etServerPort);
etMessage = findViewById(R.id.etMessage);
btnConnect = findViewById(R.id.btnConnect);
btnDisconnect = findViewById(R.id.btnDisconnect);
btnSend = findViewById(R.id.btnSend);
aSwitch = findViewById(R.id.switch1);
tvrect = findViewById(R.id.tvreceivet);
serverPortLayout = findViewById(R.id.serverPortLayout);
clientLayout = findViewById(R.id.clientLayout);
switchserver();
}
private void setupListeners() {
btnConnect.setOnClickListener(v -> {
if (isServerMode) {
startServer();
} else {
connectToServer();
}
});
btnDisconnect.setOnClickListener(v -> {
if (isServerMode) {
stopServer();
} else {
disconnectFromServer();
}
});
btnSend.setOnClickListener(v -> sendMessage());
}
private void connectToServer() {
String ip = etServerIP.getText().toString();
String portStr = etClientPort.getText().toString();
if (ip.isEmpty() || portStr.isEmpty()) {
updateStatus("请输入有效的IP和端口");
return;
}
int port = Integer.parseInt(portStr);
updateStatus("正在连接...");
executorService.execute(() -> {
try {
clientSocket = new Socket(ip, port);
clientOut = new PrintWriter(clientSocket.getOutputStream(), true);
clientIn = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
isClientConnected = true;
handler.post(() -> {
updateStatus("已连接到服务器");
btnConnect.setEnabled(false);
btnDisconnect.setEnabled(true);
btnSend.setEnabled(true);
});
startReceivingClientMessages();
} catch (IOException e) {
handler.post(() -> updateStatus("连接失败: " + e.getMessage()));
}
});
}
private void disconnectFromServer() {
executorService.execute(() -> {
try {
isClientConnected = false;
if (clientOut != null) clientOut.close();
if (clientIn != null) clientIn.close();
if (clientSocket != null) clientSocket.close();
handler.post(() -> {
updateStatus("已断开连接");
btnConnect.setEnabled(true);
btnDisconnect.setEnabled(false);
btnSend.setEnabled(false);
});
} catch (IOException e) {
e.printStackTrace();
}
});
}
private void startServer() {
String portStr = etServerPort.getText().toString();
if (portStr.isEmpty()) {
updateStatus("请输入服务器端口");
return;
}
int port = Integer.parseInt(portStr);
updateStatus("正在启动服务器...");
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port);
isServerRunning = true;
handler.post(() -> {
updateStatus("服务器已启动,等待客户端连接...");
btnConnect.setEnabled(false);
btnDisconnect.setEnabled(true);
btnSend.setEnabled(false);
});
// 等待客户端连接
serverClientSocket = serverSocket.accept();
serverOut = new PrintWriter(serverClientSocket.getOutputStream(), true);
serverIn = new BufferedReader(new InputStreamReader(serverClientSocket.getInputStream()));
isServerClientConnected = true;
handler.post(() -> {
updateStatus("客户端已连接");
btnSend.setEnabled(true);
});
startReceivingServerMessages();
} catch (IOException e) {
if (isServerRunning) {
handler.post(() -> updateStatus("服务器错误: " + e.getMessage()));
}
}
});
}
private void stopServer() {
executorService.execute(() -> {
try {
isServerRunning = false;
isServerClientConnected = false;
if (serverOut != null) serverOut.close();
if (serverIn != null) serverIn.close();
if (serverClientSocket != null) serverClientSocket.close();
if (serverSocket != null) serverSocket.close();
handler.post(() -> {
updateStatus("服务器已停止");
btnConnect.setEnabled(true);
btnDisconnect.setEnabled(false);
btnSend.setEnabled(false);
});
} catch (IOException e) {
e.printStackTrace();
}
});
}
private void sendMessage() {
String message = etMessage.getText().toString();
if (message.isEmpty()) return;
executorService.execute(() -> {
if (isServerMode && isServerClientConnected && serverOut != null) {
serverOut.println(message);
handler.post(() -> {
etMessage.setText("");
appendMessage("服务器发送: " + message);
});
} else if (!isServerMode && isClientConnected && clientOut != null) {
clientOut.println(message);
handler.post(() -> {
etMessage.setText("");
appendMessage("客户端发送: " + message);
});
}
});
}
private void startReceivingClientMessages() {
executorService.execute(() -> {
try {
String message;
while (isClientConnected && (message = clientIn.readLine()) != null) {
String finalMessage = message;
handler.post(() -> appendMessage("客户端接收: " + finalMessage));
}
} catch (IOException e) {
if (isClientConnected) {
handler.post(() -> updateStatus("连接异常: " + e.getMessage()));
}
}
});
}
private void startReceivingServerMessages() {
executorService.execute(() -> {
try {
String message;
while (isServerClientConnected && (message = serverIn.readLine()) != null) {
String finalMessage = message;
handler.post(() -> appendMessage("服务器接收: " + finalMessage));
}
} catch (IOException e) {
if (isServerClientConnected) {
handler.post(() -> updateStatus("客户端断开连接"));
// 重新等待客户端连接
if (isServerRunning) {
handler.post(() -> {
updateStatus("等待客户端连接...");
btnSend.setEnabled(false);
});
stopServer();
startServer();
}
}
}
});
}
private void switchserver() {
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
isServerMode = !isChecked;
if (isChecked) {
// TCP Client 模式
aSwitch.setText("当前连接模式为:TCPClient");
tvrect.setText("Client发送:");
serverPortLayout.setVisibility(android.view.View.GONE);
clientLayout.setVisibility(android.view.View.VISIBLE);
btnConnect.setText("连接");
btnDisconnect.setText("断开连接");
} else {
// TCP Server 模式
aSwitch.setText("当前连接模式为:TCPServer");
tvrect.setText("Server发送:");
serverPortLayout.setVisibility(android.view.View.VISIBLE);
clientLayout.setVisibility(android.view.View.GONE);
btnConnect.setText("启动服务器");
btnDisconnect.setText("停止服务器");
}
// 重置状态
resetConnection();
}
});
}
private void resetConnection() {
// 断开现有连接
if (isServerMode && isServerRunning) {
stopServer();
} else if (!isServerMode && isClientConnected) {
disconnectFromServer();
}
handler.post(() -> {
updateStatus("未连接");
btnConnect.setEnabled(true);
btnDisconnect.setEnabled(false);
btnSend.setEnabled(false);
tvMessages.setText("");
});
}
private void updateStatus(String status) {
tvStatus.setText("状态: " + status);
}
private void appendMessage(String message) {
tvMessages.append(message + "\n");
}
@Override
protected void onDestroy() {
super.onDestroy();
executorService.shutdown();
if (isServerMode) {
stopServer();
} else {
disconnectFromServer();
}
}
}
2.设置网络权限
打开AndroidMainfest.xml
添加网络权限
<uses-permission android:name="android.permission.INTERNET" /><!--网络权限-->
3.实现效果:


总结:
服务端模式:启动本地服务监听端口,等待客户端连接
客户端模式:连接指定IP和端口的远程服务器
实时通信:支持双向消息收发,完整显示通信记录
技术特点
使用ExecutorService线程池处理网络操作,避免主线程阻塞
Handler机制实现线程间UI更新
完整的连接状态管理和异常处理
自动资源释放,防止内存泄漏
工作流程
模式选择 → 2. 参数配置 → 3. 建立连接 → 4. 消息通信 → 5. 断开清理
代码文件:
通过网盘分享的文件:soket.rar
链接: https://pan.baidu.com/s/1_Dg1kdrS1LNUVc3mX6NUMA?pwd=1111 提取码: 1111
网络调试助手:
通过网盘分享的文件:网络调试助手.exe
链接: https://pan.baidu.com/s/1lSJ28OpqA-v6qjJ_W6d_5g?pwd=1111 提取码: 1111
声明:
萌新一枚,如有错误地方,恳请各位大佬批评指正!
图片素材和软件仅为个人学习、研究及欣赏之用。本人尊重并感谢所有设计师的创作成果。相关图片版权归原作者所有,任何商业用途均须获得原作者授权。如涉及侵权,请及时联系我进行删除。
浙公网安备 33010602011771号