1. 程式人生 > >PC通過USB連線Android通訊(Socket)

PC通過USB連線Android通訊(Socket)

Android端Socket伺服器

/**
 * Created by Jack Stone on 2016/11/17.
 * Socket伺服器,PC可以通過USB連線、ADB埠對映連線本伺服器,不需要通過Wifi和網路
 */

public class TCPConnect implements Runnable {
    private static final String TAG = "TCPConnect";
    private final int SERVER_PORT = 10086;
    private ServerSocket mServerSocket;
    private Socket mClient;
    private Callback callback;

    public TCPConnect(Callback callback) {
        this.callback = callback;
    }


    @Override
    public void run() {
        try {
            String ip = InetAddress.getLocalHost().getHostAddress();
            mServerSocket = new ServerSocket(SERVER_PORT);
            callback.call("建立伺服器:[" + ip + ":" + SERVER_PORT + "]");
        } catch (IOException e) {
            callback.call("建立伺服器異常:" + e.getMessage());
        }
        while (true) {

            BufferedOutputStream out = null;
            BufferedReader in = null;
            try {
                mClient = mServerSocket.accept();
                callback.call("建立連結:" + mClient.getInetAddress().toString() + ":" + mClient.getPort());
                out = new BufferedOutputStream(mClient.getOutputStream());
                in = new BufferedReader(new InputStreamReader(mClient.getInputStream()));

                String request = receive(in);
                callback.call("client:"+request);
                send(out, request);

            } catch (IOException e) {
                Log.e(TAG, "run: ", e);
                callback.call(e.getMessage());
            } finally {
                close(out);
                close(in);
                close(mClient);
            }
        }
    }

    private void send(OutputStream out, String msg) throws IOException {
        msg += "\n";
        out.write(msg.getBytes("utf-8"));
    }

    private String receive(BufferedReader in) throws IOException {
        String r = in.readLine();
        callback.call("origin request:"+r);
        if(r.contains("\\&")) {
            callback.call("進行\\&替換!");
            r = r.replaceAll("\\\\&","\n");
        }
        return r;
    }

    private void close(OutputStream out) {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void close(BufferedReader in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void close(Socket socket) {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                Log.e(TAG, "run: ", e);
            }
        }
    }

    public interface Callback {
        void call(String msg);
    }
}

PC客戶端
using CMDExecutor;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;

namespace AndroidUSBSocket
{
    public class SocketClient
    {
        public const string LOCAL_PORT = "12580";

        public SocketClient(string adb_path, string remote_port)
        {
            AdbExecutor adb = AdbExecutor.GetInstance(adb_path);
            adb.Forward(LOCAL_PORT, remote_port);
        }

        public string Request(string msg)
        {
            Socket client = null;
            try
            {
                client = create();
                connect(client, LOCAL_PORT);
                if (client.Connected)
                {
                    send(client, msg);
                    return receive(client);
                }
                else
                {
                    return "連線失敗!";
                }
            }
            catch(Exception e)
            {
                return $"Error:{e.Message}";
            }
            finally
            {
                client.Shutdown(SocketShutdown.Both);
                client?.Close();
                Request(msg);
            }
        }

        private static void connect(Socket socket,string port)
        {
            IPAddress myIP = IPAddress.Parse("127.0.0.1");
            IPEndPoint EPhost = new IPEndPoint(myIP, int.Parse(port));
            socket.Connect(EPhost);     //create connection
        }

        private static Socket create()
        {
            return new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp);
        }

        private static void send(Socket socket, string msg)
        {
            //request
            msg = msg.Replace("\n", "\\&");                 //replace all '\n' to '\&', those will be replace to '\n' when server received this msg
            msg += "\n";                                    //server is using readLine(), if not send a '\n' the stream won't push data to server
            byte[] data = Encoding.UTF8.GetBytes(msg);
            socket.Send(data);                              //send message to server
            //socket.Shutdown(SocketShutdown.Send);         //N-E-V-E-R close output stream, the socket will be closed together
            Debug.WriteLine($"傳送完畢(長度[{msg.Length}] 位元組[{data.Length}]):\n{msg}");
        }

        private static string receive(Socket socket)
        {
            //get response
            string str = "";
            byte[] data = new byte[10240];
            int len = 0;
            int i = 0;
            while ((i = socket.Receive(data)) != 0)         //read response string
            {
                len += i;
                string piece = Encoding.UTF8.GetString(data, 0, i);
                str += piece;
            }
            Debug.WriteLine($"接收完畢(長度[{str.Length}] 位元組[{len}]):\n{str}");
            return str;
        }
    }
}

使用ADB進行埠的對映轉發:adb forward tcp:local_port tcp:remote_port

SocketClient構造中的ADBExecutor就是用來執行這個命令的

另外有一點不明白的是 SocketClient的receive方法中,byte[]的size我定的是10240

因為如果定小了,在獲取字串的過程中就會因為這個尺寸不合適導致在邊界上的字元變成???

C#不是很熟悉,這個地方不知道該怎麼做,索性把size定得大一點,一般用不了這麼長的位元組陣列

使用這個方法要注意的是,只能通過PC主動向Android端發起請求,看了很多文件,貌似Android端是不能主動請求PC的,這個好像跟ADB的機制有關

但是ADB我並不是理解得特別清楚,所以如果有大神知道如何讓Android端主動請求PC端的伺服器的話,希望不吝賜教!

有一個帖子說,Android通過USB連線電腦後,預設電腦的IP就是10.0.2.2,我試了一下,沒啥成果,如果有人成功請聯絡我。

其他需要注意的地方在程式碼中的註釋我都清楚地寫出來了,歡迎交流。