Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีถ่ายโอนไฟล์ผ่าน Wi-Fi Direct (Peer-to-Peer) ใน Android

ตัวอย่างนี้สาธิตวิธีการ ถ่ายโอนไฟล์/ข้อความผ่าน Wi-Fi Direct (Wi-Fi P2P) ใน Android โดยสร้าง 2 แอปแยกกัน: Server (รอการเชื่อมต่อ) และ Client (เริ่มการเชื่อมต่อ) ใช้ ServerSocket และ Socket สำหรับการสื่อสาร

ส่วนที่ 1: Server App

ขั้นตอนที่ 1: สร้างโปรเจกต์ Server

สร้างโปรเจกต์ใหม่ใน Android Studio

ขั้นตอนที่ 2: Layout Server (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context=".MainActivity">
    
    <TextView
        android:id="@+id/tvIP"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />
        
    <TextView
        android:id="@+id/tvPort"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvIP"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />
        
    <TextView
        android:id="@+id/tvConnectionStatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvPort"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />
        
    <TextView
        android:id="@+id/tvMessages"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/etMessage"
        android:layout_below="@id/tvConnectionStatus"
        android:inputType="textMultiLine"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />
        
    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/btnSend"
        android:hint="Enter Message"
        android:inputType="text" />
        
    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="SEND" />
        
</RelativeLayout>

ขั้นตอนที่ 3: Server MainActivity.java

package com.server.myapplication.server;

import android.annotation.SuppressLint;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
    ServerSocket serverSocket;
    Thread thread1 = null;
    TextView tvIP, tvPort, tvMessages;
    EditText etMessage;
    Button btnSend;
    public static String SERVER_IP = "";
    public static final int SERVER_PORT = 8080;
    String message;
    private PrintWriter output;
    private BufferedReader input;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        tvIP = findViewById(R.id.tvIP);
        tvPort = findViewById(R.id.tvPort);
        tvMessages = findViewById(R.id.tvMessages);
        etMessage = findViewById(R.id.etMessage);
        btnSend = findViewById(R.id.btnSend);

        try {
            SERVER_IP = getLocalIpAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        thread1 = new Thread(new ServerThread());
        thread1.start();

        btnSend.setOnClickListener(v -> {
            message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
                new Thread(new SendThread(message)).start();
            }
        });
    }

    private String getLocalIpAddress() throws UnknownHostException {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ipInt = wifiInfo.getIpAddress();
        return InetAddress.getByAddress(
            ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array()
        ).getHostAddress();
    }

    class ServerThread implements Runnable {
        @Override
        public void run() {
            Socket socket;
            try {
                serverSocket = new ServerSocket(SERVER_PORT);
                runOnUiThread(() -> {
                    tvMessages.setText("Waiting for connection...");
                    tvIP.setText("IP: " + SERVER_IP);
                    tvPort.setText("Port: " + SERVER_PORT);
                });

                socket = serverSocket.accept();
                output = new PrintWriter(socket.getOutputStream(), true);
                input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                runOnUiThread(() -> tvMessages.setText("Connected\n"));
                new Thread(new ReceiveThread()).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ReceiveThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    final String message = input.readLine();
                    if (message != null) {
                        runOnUiThread(() -> tvMessages.append("Client: " + message + "\n"));
                    } else {
                        // Client disconnected, restart server
                        runOnUiThread(() -> {
                            tvMessages.append("Client disconnected\n");
                            thread1 = new Thread(new ServerThread());
                            thread1.start();
                        });
                        return;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class SendThread implements Runnable {
        private String message;
        SendThread(String message) { this.message = message; }
        
        @Override
        public void run() {
            output.println(message);
            output.flush();
            runOnUiThread(() -> {
                tvMessages.append("Server: " + message + "\n");
                etMessage.setText("");
            });
        }
    }
}

ขั้นตอนที่ 4: Server AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.server.myapplication.server">
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity" android:label="Server">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

ส่วนที่ 2: Client App

ขั้นตอนที่ 1: สร้างโปรเจกต์ Client

สร้างโปรเจกต์ใหม่อีกโปรเจกต์หนึ่ง

ขั้นตอนที่ 2: Layout Client (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context=".MainActivity">
    
    <EditText
        android:id="@+id/etIP"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="IP Address"
        android:inputType="text" />
        
    <EditText
        android:id="@+id/etPort"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/etIP"
        android:hint="Port Number"
        android:inputType="number" />
        
    <Button
        android:id="@+id/btnConnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/etPort"
        android:layout_marginTop="16dp"
        android:text="Connect to Server" />
        
    <TextView
        android:id="@+id/tvMessages"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/etMessage"
        android:layout_below="@id/btnConnect"
        android:inputType="textMultiLine"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />
        
    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/btnSend"
        android:hint="Enter Message"
        android:inputType="text" />
        
    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="SEND" />
        
</RelativeLayout>

ขั้นตอนที่ 3: Client MainActivity.java

package com.client.myapplication.client;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
    Thread thread1 = null;
    EditText etIP, etPort, etMessage;
    TextView tvMessages;
    Button btnSend;
    String SERVER_IP;
    int SERVER_PORT;
    private PrintWriter output;
    private BufferedReader input;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        etIP = findViewById(R.id.etIP);
        etPort = findViewById(R.id.etPort);
        tvMessages = findViewById(R.id.tvMessages);
        etMessage = findViewById(R.id.etMessage);
        btnSend = findViewById(R.id.btnSend);
        
        Button btnConnect = findViewById(R.id.btnConnect);
        btnConnect.setOnClickListener(v -> {
            tvMessages.setText("");
            SERVER_IP = etIP.getText().toString().trim();
            SERVER_PORT = Integer.parseInt(etPort.getText().toString().trim());
            thread1 = new Thread(new ClientThread());
            thread1.start();
        });

        btnSend.setOnClickListener(v -> {
            String message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
                new Thread(new SendThread(message)).start();
            }
        });
    }

    class ClientThread implements Runnable {
        @Override
        public void run() {
            try {
                Socket socket = new Socket(SERVER_IP, SERVER_PORT);
                output = new PrintWriter(socket.getOutputStream(), true);
                input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                
                runOnUiThread(() -> tvMessages.setText("Connected\n"));
                new Thread(new ReceiveThread()).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ReceiveThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    final String message = input.readLine();
                    if (message != null) {
                        runOnUiThread(() -> tvMessages.append("Server: " + message + "\n"));
                    } else {
                        runOnUiThread(() -> tvMessages.append("Server disconnected\n"));
                        return;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class SendThread implements Runnable {
        private String message;
        SendThread(String message) { this.message = message; }
        
        @Override
        public void run() {
            output.println(message);
            output.flush();
            runOnUiThread(() -> {
                tvMessages.append("Client: " + message + "\n");
                etMessage.setText("");
            });
        }
    }
}

ขั้นตอนที่ 4: Client AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.client.myapplication.client">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity" android:label="Client">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
หมายเหตุสำคัญ:
  • ทั้งสองอุปกรณ์ต้องอยู่ใน Wi-Fi Network เดียวกัน
  • Server ต้องรันก่อน แล้วค่อยรัน Client
  • Client ต้องกรอก IP และ Port ของ Server ให้ถูกต้อง
  • ตัวอย่างนี้ส่งข้อความ (Text) สำหรับไฟล์จริง ใช้ FileInputStream/FileOutputStream แทน
  • Android 10+ อาจจำกัดการเข้าถึง MAC Address ใช้ Wi-Fi Direct API (WifiP2pManager) แทนจะดีกว่า

การทดสอบ

  1. ติดตั้ง Server App บนอุปกรณ์เครื่องแรก รันแอป จด IP Address ที่แสดง
  2. ติดตั้ง Client App บนอุปกรณ์เครื่องที่สอง รันแอป
  3. กรอก IP และ Port (8080) ใน Client แล้วกด Connect
  4. ลองส่งข้อความระหว่างกัน

Server Screen:

วิธีถ่ายโอนไฟล์ผ่าน Wi-Fi Direct (Peer-to-Peer) ใน Android

Client Screen:

วิธีถ่ายโอนไฟล์ผ่าน Wi-Fi Direct (Peer-to-Peer) ใน Android

After Connected:

วิธีถ่ายโอนไฟล์ผ่าน Wi-Fi Direct (Peer-to-Peer) ใน Android