1. 程式人生 > >Android——socket連線(客戶端發訊息給服務端)

Android——socket連線(客戶端發訊息給服務端)

準備工作

一臺手機作服務端,一臺手機作客戶端,兩部手機連線同一個wifi

服務端

佈局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.jie.socketdemo.MainActivity">
<TextView android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf
="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
<TextView android:id="@+id/data" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" app:layout_constraintTop_toBottomOf="@+id/info" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

    private TextView mTvInfo;
    private TextView mTvGetData;
    private ServerSocket mServerSocket = null;
    private InputStream mInputSteam;
    private String ip;
    private int port;

    StringBuffer mStringBuffer = new StringBuffer();

    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    //本地ip和port資訊
                    mTvInfo.setText(msg.obj.toString());
                    break;
                case 2:
                    //從客戶端獲取到的訊息
                    mTvGetData.setText("CustomInfo:"+msg.obj.toString());
                    mStringBuffer.setLength(0);
                    break;

            }

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvInfo = findViewById(R.id.info);
        mTvGetData = findViewById(R.id.data);

        receiveData();
    }

    /**
     * 伺服器端接收資料
     * 需要注意以下一點:
     * 伺服器端應該是多執行緒的,因為一個伺服器可能會有多個客戶端連線在伺服器上;
    */
    public void receiveData() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                super.run();
                /*指明伺服器端的埠號*/
                try {
                    mServerSocket = new ServerSocket(8000);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                getLocalIpAddress(mServerSocket);

                Message msg = handler.obtainMessage();
                msg.what = 1;
                msg.obj = "IP:" + ip + " , PORT: " + port;
                handler.sendMessage(msg);

                //持續獲取訊息
                while (true) {
                    Socket socket = null;
                    try {
                        if (mServerSocket != null) {
                            socket = mServerSocket.accept();
                            mInputSteam = socket.getInputStream();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    new ServerThread(socket, mInputSteam).start();
                }
            }
        };
        thread.start();
    }

    private void getLocalIpAddress(ServerSocket serverSocket) {
        try {
            //for(;;)
            for (Enumeration<NetworkInterface> mEnumNetwork = NetworkInterface.getNetworkInterfaces(); mEnumNetwork.hasMoreElements();) {
                NetworkInterface mNetwork = mEnumNetwork.nextElement();
                for (Enumeration<InetAddress> mEnumIpAddress = mNetwork.getInetAddresses(); mEnumIpAddress.hasMoreElements();) {
                    InetAddress mIpAddress = mEnumIpAddress.nextElement();
                    String mIp = mIpAddress.getHostAddress().substring(0, 3);
                    if(mIp.equals("192")){
                        ip = mIpAddress.getHostAddress();    //獲取本地IP
                        port = serverSocket.getLocalPort();    //獲取本地的PORT
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    class ServerThread extends Thread {
        private Socket socket;
        private InputStream inputStream;
        private StringBuffer stringBuffer = mStringBuffer;

        public ServerThread(Socket socket, InputStream inputStream) {
            this.socket = socket;
            this.inputStream = inputStream;
        }

        @Override
        public void run() {
            int len;
            byte[] bytes = new byte[24];
            boolean isString = false;

            try {
                if (inputStream != null) {
                    //輸入流關閉時迴圈才會停止
                    //資料讀完了,再讀是等於0
                    while ((len = inputStream.read(bytes)) != -1) {
                        for (int i = 0; i < len; i++) {
                            if (bytes[i] != '\0') {
                                stringBuffer.append((char) bytes[i]);
                            } else {
                                isString = true;
                                break;
                            }
                        }
                        if (isString) {
                            Message msg = handler.obtainMessage();
                            msg.what = 2;
                            msg.obj = stringBuffer;
                            handler.sendMessage(msg);
                            isString = false;
                        }
                    }
                }
            } catch (IOException e) {
                //當這個異常發生時,說明客戶端那邊的連線已經斷開
                e.printStackTrace();
                try {
                    inputStream.close();
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        }
    }

    /*當按返回鍵時,關閉相應的socket資源*/
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        try {
            mServerSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客戶端

佈局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.jie.socketcustomdemo.MainActivity">

    <EditText
        android:id="@+id/et_ip"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="40dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="connect"
        android:onClick="connect"
        app:layout_constraintTop_toTopOf="@id/et_ip"
        app:layout_constraintLeft_toRightOf="@+id/et_ip" />

    <EditText
        android:id="@+id/et_data"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="40dp"
        app:layout_constraintTop_toBottomOf="@id/et_ip"
        app:layout_constraintLeft_toLeftOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="send"
        android:onClick="send"
        app:layout_constraintTop_toTopOf="@id/et_data"
        app:layout_constraintLeft_toRightOf="@+id/et_data" />

</android.support.constraint.ConstraintLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

    private EditText mEtIp,mEtData;
    private OutputStream mOutputStream = null;
    private Socket mSocket = null;
    private String ip;
    private String data;
    private boolean socketStatus = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mEtIp = findViewById(R.id.et_ip);
        mEtData = findViewById(R.id.et_data);
    }

    public void connect(View view){
        ip = mEtIp.getText().toString();
        Toast.makeText(MainActivity.this,""+ip,Toast.LENGTH_SHORT).show();

        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                if (!socketStatus) {
                    try {
                        //連線服務端
                        mSocket = new Socket(ip,8000);
                        if(mSocket != null){
                            socketStatus = true;
                        }

                        mOutputStream = mSocket.getOutputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
    }

    public void send(View view){
        data = mEtData.getText().toString();
        Toast.makeText(MainActivity.this,""+data,Toast.LENGTH_SHORT).show();

        if (data != null) {
            //在後面加上 '\0' ,是為了在服務端方便我們去解析;
            data = data + '\0';
        }

        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                if(socketStatus){
                    try {
                        mOutputStream.write(data.getBytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
    }

    /*當客戶端介面返回時,關閉相應的socket資源*/
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        /*關閉相應的資源*/
        try {
            mOutputStream.close();
            mSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意

服務端和客戶端都要新增許可權

<!--允許應用程式改變網路狀態-->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<!--允許應用程式改變WIFI連線狀態-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!--允許應用程式訪問有關的網路資訊-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--允許應用程式訪問WIFI網絡卡的網路資訊-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!--允許應用程式完全使用網路-->
<uses-permission android:name="android.permission.INTERNET"/>

效果

客戶端
這裡寫圖片描述
服務端
這裡寫圖片描述