public class ClientThread implements Runnable
{
    // 定义向UI线程发送消息的Handler对象
    private Handler handler;
    // 该线程所处理的Socket所对应的输入流
    private BufferedReader br;
    private OutputStream os;
    // 定义接收UI线程的消息的Handler对象
    Handler revHandler = new Handler();
    ClientThread(Handler handler)
    {
        this.handler = handler;
    }

    @Override
    public void run()
    {
        try {
            Socket s = new Socket('192.168.1.88', 30000);
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            os = s.getOutputStream();
            // 启动一条子线程来读取服务器响应的数据
            new Thread(() ->
            {
                String content;
                // 不断读取Socket输入流中的内容
                try
                {
                    while ((content = br.readLine()) != null) {
                        // 每当读到来自服务器的数据之后,发送消息通知
                        // 程序界面显示该数据
                        Message msg = new Message();
                        msg.what = 0x123;
                        msg.obj = content;
                        handler.sendMessage(msg);
                    }
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }).start();
            // 为当前线程初始化Looper
            Looper.prepare();
            // 创建revHandler对象
            revHandler = new Handler()
            {
                @Override
                public void handleMessage(Message msg)
                {
                    // 接收到UI线程中用户输入的数据
                    if (msg.what == 0x345) {
                        // 将用户在文本框内输入的内容写入网络
                        try {
                            os.write((msg.obj.toString() + '\r\n').getBytes('utf-8'));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            // 启动Looper
            Looper.loop();
        }
        catch (SocketTimeoutException e1)
        {
            System.out.println('网络连接超时!!');
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

public class ChatWithOthers extends AppCompatActivity {

    private TextView show;
    // 定义与服务器通信的子线程
    private ClientThread clientThread;
    static class MyHandler extends Handler // ②
    {
        private WeakReference<ChatWithOthers> mainActivity;
        MyHandler(WeakReference<ChatWithOthers> mainActivity)
        {
            this.mainActivity = mainActivity;
        }
        @Override
        public void handleMessage(Message msg)
        {
            // 如果消息来自子线程
            if (msg.what == 0x123)
            {
                // 将读取的内容追加显示在文本框中
                mainActivity.get().show.append('\n' + msg.obj.toString());

            }
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat_with_others);
        // 定义界面上的两个文本框
        EditText input = findViewById(R.id.input);
        show = findViewById(R.id.show);
        // 定义界面上的一个按钮
        Button send = findViewById(R.id.send);
        MyHandler handler = new MyHandler(new WeakReference<>(this));
        clientThread = new ClientThread(handler);
        // 客户端启动ClientThread线程创建网络连接,读取来自服务器的数据
        new Thread(clientThread).start();  // ①
        send.setOnClickListener(view -> {
            // 当用户单击“发送”按钮后,将用户输入的数据封装成Message
            // 然后发送给子线程的Handler
            Message msg = new Message();
            msg.what = 0x345;
            msg.obj = input.getText().toString();
            clientThread.revHandler.sendMessage(msg);
            // 清空input文本框
            input.setText('');
        });
    }
}

// 服务器端代码示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(30000)) {
            System.out.println('Server started on port 30000');
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println('Client connected: ' + clientSocket.getInetAddress().getHostAddress());
                new Thread(() -> {
                    try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                         PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
                        String inputLine;
                        while ((inputLine = in.readLine()) != null) {
                            System.out.println('Client: ' + inputLine);
                            out.println('Server: ' + inputLine);
                        }
                    } catch (IOException e) {
                        System.out.println('Error: ' + e.getMessage());
                    }
                }).start();
            }
        } catch (IOException e) {
            System.out.println('Error: ' + e.getMessage());
        }
    }
}

Explanation:

Client-Side

  1. ClientThread:

    • Extends Runnable to create a thread for client-server communication.
    • handler: Receives messages from the server thread and updates the UI.
    • br: Reads data from the server.
    • os: Writes data to the server.
    • revHandler: Receives messages from the UI thread to send data to the server.
  2. ChatWithOthers:

    • show: TextView to display messages from the server.
    • clientThread: Instance of the ClientThread to handle communication.
    • MyHandler: Handles messages from the server thread, updating the show TextView.
    • onCreate(): Initializes the UI elements and starts the ClientThread.
    • The 'Send' button sends user input to the server via the revHandler.

Server-Side

  1. Server Class:
    • Creates a ServerSocket to listen for client connections on port 30000.
    • Accepts client connections and starts a new thread for each client to handle communication.
    • Reads data from the client, echoes it back, and logs the messages.

Important Points:

  • Thread Safety: Use Handler to communicate between threads safely. MyHandler uses a WeakReference to avoid memory leaks.
  • Logcat: Use Log.d('TAG', 'message') in the Server code to output logs for debugging.
  • UI Update: The MyHandler updates the show TextView on the UI thread, ensuring UI updates are done safely.
  • Server Code: Ensure you have a server running on port 30000 that can handle the client's communication. The server code example provided is a basic echo server.

Troubleshooting:

  • If show is not displaying any content, check the layout file for correct TextView ID and make sure show is properly referenced in ChatWithOthers.
  • Verify that your server is running on the correct port and that the IP address in the Socket constructor is correct.
  • Use Logcat to debug any issues on both the client and server sides.

Key Features:

  • Thread-Based Communication: Efficiently handles network communication without blocking the UI thread.
  • Handler Message Passing: Facilitates safe communication between threads.
  • UI Updates: Ensures smooth UI updates from background threads.
Android Client-Server Communication with Threads and Handlers

原文地址: https://www.cveoy.top/t/topic/fo2x 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录