Android Socket 使用详解

本文深入解析Socket编程中的构造方法、选项设置及其作用,涵盖连接建立、数据收发与资源释放等核心流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一、Socket 基础概念

Socket(套接字)是网络通信的基本操作单元,支持 TCP 和 UDP 两种协议:

1. TCP vs UDP 对比

特性TCP (传输控制协议)UDP (用户数据报协议)
连接方式面向连接(三次握手)无连接
可靠性可靠传输,保证数据顺序不保证可靠性和顺序
速度较慢较快
数据边界字节流,无边界数据报,有明确边界
适用场景文件传输、网页浏览等视频流、实时游戏等

二、TCP Socket 实现详解

1. 服务端实现

// 创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888); // 监听8888端口

// 接受客户端连接(阻塞方法)
Socket clientSocket = serverSocket.accept();

// 获取输入输出流
BufferedReader in = new BufferedReader(
    new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(
    new OutputStreamWriter(clientSocket.getOutputStream()), true);

// 读取客户端数据
String request = in.readLine();
System.out.println("收到客户端消息: " + request);

// 发送响应
out.println("Hello Client!");

// 关闭连接
clientSocket.close();
serverSocket.close();

2. 客户端实现

// 创建Socket并连接服务器
Socket socket = new Socket("192.168.1.100", 8888); // 服务器IP和端口

// 获取输入输出流
PrintWriter out = new PrintWriter(
    new OutputStreamWriter(socket.getOutputStream()), true);
BufferedReader in = new BufferedReader(
    new InputStreamReader(socket.getInputStream()));

// 发送请求
out.println("Hello Server!");

// 接收响应
String response = in.readLine();
System.out.println("收到服务端响应: " + response);

// 关闭连接
socket.close();

三、UDP Socket 实现详解

1. 服务端实现

// 创建DatagramSocket
DatagramSocket socket = new DatagramSocket(8888);

// 接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 阻塞方法

// 处理数据
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到UDP消息: " + message);

// 发送响应
InetAddress clientAddress = packet.getAddress();
int clientPort = packet.getPort();
byte[] responseData = "UDP Response".getBytes();
DatagramPacket responsePacket = new DatagramPacket(
    responseData, responseData.length, clientAddress, clientPort);
socket.send(responsePacket);

// 关闭
socket.close();

2. 客户端实现

// 创建DatagramSocket
DatagramSocket socket = new DatagramSocket();

// 发送数据包
InetAddress serverAddress = InetAddress.getByName("192.168.1.100");
byte[] sendData = "Hello UDP Server".getBytes();
DatagramPacket sendPacket = new DatagramPacket(
    sendData, sendData.length, serverAddress, 8888);
socket.send(sendPacket);

// 接收响应
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);

// 处理响应
String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到UDP响应: " + response);

// 关闭
socket.close();

四、Android 特殊注意事项

1. 网络权限配置

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果需要访问非WiFi网络 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2. 主线程限制

Android 不允许在主线程执行网络操作,必须使用工作线程:

new Thread(new Runnable() {
    @Override
    public void run() {
        // Socket操作代码
        runOnUiThread(() -> {
            // 更新UI
        });
    }
}).start();

3. 使用 AsyncTask(已弃用但可参考)

private class SocketTask extends AsyncTask<Void, Void, String> {
    protected String doInBackground(Void... params) {
        // 执行Socket操作
        return result;
    }
    
    protected void onPostExecute(String result) {
        // 更新UI
    }
}

4. 推荐替代方案

// 使用协程
lifecycleScope.launch(Dispatchers.IO) {
    // Socket操作
    withContext(Dispatchers.Main) {
        // 更新UI
    }
}

五、高级特性与优化

1. Socket 超时设置

// 连接超时(仅客户端)
Socket socket = new Socket();
socket.connect(new InetSocketAddress("192.168.1.100", 8888), 5000); // 5秒超时

// 读取超时
socket.setSoTimeout(3000); // 3秒超时

2. 保持长连接

// 启用Keep-Alive
socket.setKeepAlive(true);

// 自定义心跳机制
new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
        out.println("HEARTBEAT");
    }
}, 0, 30000); // 每30秒发送心跳

3. 数据序列化

推荐使用 Protocol Buffers 或 JSON:

// 使用GSON发送对象
MyData data = new MyData("name", 123);
String json = new Gson().toJson(data);
out.println(json);

// 接收端解析
MyData receivedData = new Gson().fromJson(in.readLine(), MyData.class);

六、常见问题解决方案

1. 连接拒绝

  • 检查服务端是否启动

  • 检查IP和端口是否正确

  • 检查防火墙设置

2. 数据读取阻塞

  • 设置合理的超时时间 socket.setSoTimeout()

  • 使用特定结束标记(如换行符)

  • 指定数据长度前缀

3. Android 9+ 网络限制

  • 默认禁止明文传输(HTTP),需:

    <application android:usesCleartextTraffic="true">

    配置网络安全策略

七、完整示例(TCP 聊天应用)

服务端代码

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器启动,等待连接...");
        
        while (true) {
            Socket clientSocket = serverSocket.accept();
            new Thread(new ClientHandler(clientSocket)).start();
        }
    }
    
    static class ClientHandler implements Runnable {
        private Socket socket;
        
        public ClientHandler(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            try (BufferedReader in = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
                 PrintWriter out = new PrintWriter(
                    new OutputStreamWriter(socket.getOutputStream()), true)) {
                
                String clientMessage;
                while ((clientMessage = in.readLine()) != null) {
                    System.out.println("收到: " + clientMessage);
                    out.println("服务器回复: " + clientMessage);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Android 客户端代码

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val handler = Handler(Looper.getMainLooper())
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.btnConnect.setOnClickListener { connectToServer() }
        binding.btnSend.setOnClickListener { sendMessage() }
    }
    
    private fun connectToServer() {
        thread {
            try {
                val socket = Socket("192.168.1.100", 8888)
                val output = PrintWriter(socket.getOutputStream(), true)
                val input = BufferedReader(InputStreamReader(socket.getInputStream()))
                
                // 保持连接并监听消息
                while (true) {
                    val message = input.readLine() ?: break
                    handler.post { binding.tvMessages.append("服务器: $message\n") }
                }
            } catch (e: Exception) {
                handler.post { Toast.makeText(this, "连接失败: ${e.message}", Toast.LENGTH_SHORT).show() }
            }
        }
    }
    
    private fun sendMessage() {
        val message = binding.etMessage.text.toString()
        if (message.isNotEmpty()) {
            thread {
                try {
                    val socket = Socket("192.168.1.100", 8888)
                    PrintWriter(socket.getOutputStream(), true).println(message)
                    socket.close()
                    handler.post { binding.tvMessages.append("我: $message\n") }
                } catch (e: Exception) {
                    handler.post { Toast.makeText(this, "发送失败", Toast.LENGTH_SHORT).show() }
                }
            }
        }
    }
}

八、性能优化建议

  1. 连接池管理:对频繁通信的场景使用连接池

  2. 缓冲区优化:根据数据量调整缓冲区大小

  3. NIO 替代方案:考虑使用 java.nio 包的非阻塞IO

  4. 协议设计:定义明确的应用层协议(如消息头+消息体)

  5. 流量控制:实现滑动窗口等机制防止数据淹没

通过掌握这些 Socket 编程技术,您可以在 Android 应用中实现高效的网络通信功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值