Android使用Socket(Tcp/Udp)協議進行資料傳輸(傳輸大檔案)

Code-Porter發表於2016-08-31

Socket是什麼:所謂Socket 通常也稱作”套接字”,用於描述IP地址和埠,是一個通訊鏈的控制程式碼。應用程式通常通過”套接字”向網路發出請求或者應答網路請求。Socket內部又有兩種協議(Tcp/Udp),我們來說說其各自的特點。

先來說說網路程式設計的三要素

1.IP地址:網路中計算機的唯一標識
2.埠
3.協議:通訊的規則

Tcp/Udp特點

一、 TCP(協議):

  • 建立連線,形成傳輸資料的通道
  • 在連線中進行大資料量傳輸
  • 通過三次握手完成連線,是可靠協議
  • 必須建立連線,效率稍微低點

二. UDP(協議):

  • 將資料及源和目的封裝在資料包中,不需要建立連線
  • 每個資料包的大小限制在64K內
  • 因無連線,是不可靠協議
  • 不需要建立連線,速度快

Socket工作圖解

Socket圖解

TCP協議進行資料傳輸,實現兩臺裝置的一個簡單資料傳送。

客戶端傳送資料

     /**
     * @param serverAddress 要傳送到服務端的ip
     * 1.建立socket並指定ip和埠號
     * 2.獲取輸出流,寫資料 
     * 3.釋放資源
     * 4.Tcp一定要先開接收端
     */
    public void send_tcp(String serverAddress) {
        try {
            Socket s = new Socket(serverAddress, WIFIAdmin.PORT);
            //為了傳送資料,應該獲得socket流中的輸出流
            OutputStream out = s.getOutputStream();
            out.write((content.getText().toString()).getBytes());
            s.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

服務端接收資料

    /**
     * TCP協議接收資料 
     * 1.建立接收端的Socket物件 
     * 2.監聽客戶端接收,返回一個Socket物件
     * 3.獲取輸入流,讀取資料顯示在控制檯
     * 4.釋放資源
     */
    public void receive_tcp() {
            try {
                //1.建立連線,監聽埠
                ServerSocket ss = new ServerSocket(WIFIAdmin.PORT);
                //2.連線客戶端物件
                while (true) {
                //阻塞式方法,只有客戶端連線了才會繼續往下執行
                    Socket accept = ss.accept();
                    //獲取ip
                    String ip = accept.getInetAddress().getHostAddress();
                    //3.獲取客戶端傳送過來的資料
                    InputStream in = accept.getInputStream();
                    //4.開始讀取,獲取輸入資訊
                    BufferedReader bff = new BufferedReader(new InputStreamReader(in));
                    //讀取資訊
                    String line;
                    final StringBuilder sb = new StringBuilder();
                    while ((line = bff.readLine()) != null) {
                        sb.append(line);
                    }
                    Message message = new Message();
                    message.obj = sb.toString();
                    handler.sendMessage(message);
                    //5.關閉
                    //ss.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

現在來看下UDP協議是怎麼進行資料傳輸的。

先來看下傳送端

    /*
     * UDP協議傳送資料:
     * 1.建立傳送端Socket物件
     * 2.建立資料,並把資料打包
     * 3.呼叫Socket物件傳送方法傳送資料包
     * 4.釋放資源
     */
    private void udpSendMessage() {
        DhcpInfo info = wifiManager.getDhcpInfo();
        String serverAddress = WIFIAdmin.intToIp(info.serverAddress);
        // 建立傳送端Socket物件
        try {
            // 建立傳送端Socket物件
            DatagramSocket ds = new DatagramSocket();
            // 建立資料,並把資料打包
            byte[] bys = content.getText().toString().getBytes();
            DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName(serverAddress),
                    WIFIAdmin.UDP_PORT);
            // 呼叫Socket物件傳送方法傳送資料包
            ds.send(dp);
            // 釋放資源
            ds.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在瞄一下接收端

    /*
     * UDP協議接收資料:
     * 1.建立接收端Socket物件
     * 2.建立一個資料包(接收容器)
     * 3.呼叫Socket物件接收方法接收資料包
     * 4.解析資料包
     * 5.釋放資源
     */
    private void receive_udp() {
        try {
            // 建立接收端Socket物件
            DatagramSocket ds = new DatagramSocket(WIFIAdmin.UDP_PORT);
            // 建立一個資料包(接收容器)
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);
            // 呼叫Socket物件接收方法接收資料包
            ds.receive(dp);
            // 獲取對方的ip
            String ip = dp.getAddress().getHostAddress();
            // 解析資料
            String data = new String(dp.getData(), 0, dp.getLength());
            Message message = new Message();
            message.obj = data;
            message.what = 2;
            handler.sendMessage(message);
            ds.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

使用Tcp協議傳輸大檔案,迴圈將資料寫入流中(Tcp傳送端)

    /*
     * @param path     檔案路徑
     * @param FileName 檔名
     */
public void sendFile(String path, String FileName) {
            DhcpInfo info = wifiManager.getDhcpInfo();
            serverAddress = WifiAdmin.intToIp(info.serverAddress);  
            Socket s = new Socket(serverAddress, PORT);
            OutputStream out = s.getOutputStream();
            //將檔名寫在流的頭部以#分割
            out.write((FileName + "#").getBytes());
            FileInputStream inputStream = new FileInputStream(new File(path));
            byte[] buf = new byte[1024];
            int len;
            //判斷是否讀到檔案末尾
            while ((len = inputStream.read(buf)) != -1) {
                out.write(buf, 0, len);//將檔案迴圈寫入輸出流
            }
            //告訴服務端,檔案已傳輸完畢
            s.shutdownOutput();
            //獲取從服務端反饋的資訊
            BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            String serverBack = in.readLine();
            Log.d("TAG", serverBack);
            //資源關閉
            s.close();
            inputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

大檔案傳輸(Tcp接收端)

public synchronized void receiveFile() {
        try {
            ServerSocket ss = new ServerSocket(WifiAdmin.PORT);
            while (true) {
                Socket socket = ss.accept();
                InputStream in = socket.getInputStream();
            int content;
            //裝載檔名的陣列
            byte[] c = new byte[1024];
            //解析流中的檔名,也就是開頭的流
            for (int i = 0; (content = in.read()) != -1; i++) {
                //表示檔名已經讀取完畢
                if (content == '#') {
                    break;
                }
                c[i] = (byte) content;
            }
            //將byte[]轉化為字元,也就是我們需要的檔名
            String FileName = new String(c, "utf-8").trim();
                //建立一個檔案,指定儲存路徑和剛才傳輸過來的檔名
                OutputStream saveFile = new FileOutputStream(
                        new File(Environment.getExternalStorageDirectory().toString(), FileName));
                byte[] buf = new byte[1024];
                int len;
                //判斷是否讀到檔案末尾
                while ((len = in.read(buf)) != -1) {
                    saveFile.write(buf, 0, len);
                }
                saveFile.flush();
                saveFile.close();
                //告訴傳送端我已經接收完畢
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write("檔案接收成功".getBytes());
                outputStream.flush();
                outputStream.close();
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

掌握了Socket的基本傳輸規則,相信你很快就能寫出一個利用區域網讓兩臺裝置進行資料傳輸了。

最後附上Demo下載地址

相關文章