Android-socket的基本使用,發送文字和圖片以及心跳

項目需求收集通過Socket向服務器發送圖片,之前沒搞過,網上搜搜寫了下面的例子,勉強解決了需求。

為了測試切換著方便,所以方法寫的有點碎了。。。
原文地址 http://blog.csdn.net/qq_25806863/article/details/75533109

要求發送的消息的格式是,8個字節的消息長度+消息體

因為需要8個字節,所以消息長度決定用long

如果需要4個字節,可以用int

手機客戶端接收服務器的文字消息

服務端

服務端定義好端口號,開啟以一個ServerSocket,寫入文字消息:

public class Service {
    private static Socket socket;
  //定義端口號
    private static final int POST = 30000;

    public static void main(String[] args) {
        try {
          //發送的內容
            sendMsg("來自服務器的問候");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  
    public static void sendMsg(String msg) throws IOException {
      System.out.println("開始連接");
      //創建socket服務端口是30000,并等待連接
        ServerSocket serverSocket = new ServerSocket(POST);
        Socket socket = serverSocket.accept();
      //獲取輸出流
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        sendTextMsg(out, msg);
        out.close();
        socket.close();
        serverSocket.close();
      System.out.println("通訊結束");
    }
  
    public static void sendTextMsg(DataOutputStream out, String msg) throws IOException {
      //先寫長度,就是消息體的字節數,long剛好8個字節  
        out.writeLong(msg.getBytes().length);
      //寫入消息
        out.write(msg.getBytes());
    }
}

客戶端,接收消息

客戶端首先要跟服務器進行連接,然后才能進行通訊。

Socket連接需要網絡權限:

<uses-permission android:name="android.permission.INTERNET"/>

因為屬于網絡通訊,所以socket連接也不能放在主線程中,否則會報錯

  • 添加按鈕

    在布局中加一個按鈕,點擊方法是receive

    <Button
        android:text="接收"
        android:layout_marginTop="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="receive"/>
    
  • 定義方法

    在Activity中定義方法

    因為不能再主線程中訪問,所以需要子線程。這里直接new了、、

    public void receive(View view){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Socket socket;
                try {
                  //這里進行連接服務器,
                  //host是服務器ip地址,如“192.168.2.12”
                  //post是端口,上面的服務端提供的端口號是30000
                    socket = new Socket(host, post);
                  //獲取輸入流
                    DataInputStream input = new DataInputStream(socket.getInputStream());
                  //讀取長度,也即是消息頭,
                    long len = input.readLong();
                  //創建這個長度的字節數組
                    byte[] bytes = new byte[(int)len];
                  //再讀取這個長度的字節數,也就是真正的消息體
                    input.read(bytes);
                  //將字節數組轉為String
                    String s = new String(bytes);
                    Log.i("accept", "len: "+len);
                    Log.i("accept", "msg: "+s);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    

運行測試

先運行服務端,會發現程序沒有一次執行完,在阻塞著等待連接

這時點擊app中的接收按鈕,日志中會打印接收到的信息

長度為24,消息內容為“來自服務器的問候”。

因為在utf-8中一個漢字是3個字節,所以8個漢字的消息長度是24字節。

這是看服務器端的打印:

發現服務端也正常執行完畢了。

手機客戶端向服務端發送文字消息

服務端接收消息

public class Service {
    private static Socket socket;
  //定義端口號
    private static final int POST = 30000;

    public static void main(String[] args) {
        try {
          //接收消息
            getMsg();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  
    public static void getMsg() throws IOException {
        System.out.println("開始連接");
        ServerSocket serverSocket = new ServerSocket(POST);
        Socket socket = serverSocket.accept();
      //獲取輸入流,通過這個流來讀取消息
        DataInputStream input = new DataInputStream(socket.getInputStream());
      //接收文字消息
        getTextMsg(input);
        input.close();
        socket.close();
        serverSocket.close();
        System.out.println("通訊結束");
    }
  
    public static String getTextMsg(DataInputStream input) throws IOException {
      //一樣先讀長度,再根據長度讀消息
        long len = input.readLong();
        System.out.println("len = " + len);
        byte[] bytes = new byte[(int) len];
        input.read(bytes);
        String msg = new String(bytes);
        System.out.println("msd = " + msg);
        return msg;
    }
}

客戶端發送消息

  • 增加一個發送的按鈕:
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="發送!"
        android:onClick="send"/>
  • 定義方法
public void send(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Socket socket;
                try {
                //建立連接
                    socket = new Socket(host, post);
                    //獲取輸出流,通過這個流發送消息
                    DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                    //發送文字消息
                    sendTextMsg(out,"來自手機客戶端的消息");
                    out.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
public void sendTextMsg(DataOutputStream out, String msg) throws IOException {
        byte[] bytes = msg.getBytes();
        long len = bytes.length;
  //先發送長度,在發送內容
        out.writeLong(len);
        out.write(bytes);
    }

運行測試

先運行服務端,也會停留著這個界面等待連接:

然后點擊客戶端的發送按鈕,這是服務端會變成下面這樣,完成這次消息的通訊

手機客戶端想服務端發送圖片

服務端接收圖片,并保存到本地

在上面的getMsg()方法中,將getTextMsg(input);改為getImgMsg(input);

下面是getImgMsg(input);方法:

public static void getImgMsg(DataInputStream input) throws IOException {
  //同樣是先讀長度
    long len = input.readLong();
    System.out.println("len = " + len);
    byte[] bytes = new byte[(int) len];
  //然后在讀這個長度的字節到字節數組
    input.readFully(bytes);
  //將獨到的內容保存為文件到本地
    File file = new File("/Users/xxx/" + len + ".png");
    FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write(bytes);
    System.out.println("ok");
}

客戶端發送圖片

  • 增加一個按鈕:
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="200dp"
    android:onClick="sendImg"
    android:text="發送圖片" />
  • 定義方法

    public void sendImgMsg(DataOutputStream out ) throws IOException {
      //發送的圖片為圖標,就是安卓機器人,將bitmap轉為字節數組
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG,100,bout);
      //寫入字節的長度,再寫入圖片的字節
        long len = bout.size();
      //這里打印一下發送的長度
        Log.i("sendImgMsg", "len: "+len);
        out.writeLong(len);
        out.write(bout.toByteArray());
    }
    

運行測試

還是先開啟服務端,服務器變成:

然后點擊app的發送圖片按鈕,app中打印日志:

說明發送的圖片的長度是11418個字節,大致換算大小是11k。

然后看服務端的日志:

接收到的長度也是11418,并且保存到了文件,

關于心跳包

上面的例子中,每發送一次之后就把鏈接關閉了:

out.close();
socket.close();
serverSocket.close();

心跳其實就是定期向服務端發送一個小數據,比如0.

讓服務器知道這個鏈接還有用,不用關閉。

簡單實現起來就是客戶端通過一個無限循環,不停地向服務端發送消息,服務端通過一個無限循環不停地接收消息,都不關閉這個鏈接就行了。

服務端

public static void main(String[] args) {
        try {
          System.out.println("開始接收信息");
            ServerSocket serverSocket = new ServerSocket(POST);
            socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());

            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            long len = 0;
                            len = dataInputStream.readLong();
                            System.out.println("len = " + len);
                            byte[] bytes = new byte[(int) len];
                            dataInputStream.readFully(bytes);
                            String s = new String(bytes);
                            System.out.println("data = " + s);
                        } catch (IOException e) {
                            e.printStackTrace();
                            isConnect = false;
                        }
                    }
                }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客戶端,一秒發送一次消息

try {
  //建立一次鏈接
    socket = new Socket(host,post);
    outputStream = new DataOutputStream(socket.getOutputStream());
    inputStream = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
    e.printStackTrace();
}
Log.i(TAG, "run: 開始循環發送發送心跳");
//一秒發送一個0,
while (true){
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "run: 發送心跳0");
    try {
        outputStream.writeLong("0".getBytes().length);
        outputStream.write("0".getBytes());
    } catch (IOException e) {
        e.printStackTrace();
        isConnect = false;
    }
}

運行測試

先運行服務端:

然后點擊發送心跳的按鈕,app的日志中打印:

在服務端的日志中可以看到:

一秒一次

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/190...
    archyly閱讀 1,074評論 0 8
  • __block和__weak修飾符的區別其實是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,374評論 0 6
  • 大綱 一.Socket簡介 二.BSD Socket編程準備 1.地址 2.端口 3.網絡字節序 4.半相關與全相...
    VD2012閱讀 2,453評論 0 5
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,014評論 0 7
  • 我可否出現在你那溫馨的床頭親吻你的額頭 我可曾出現在你那浪漫的夢里牽著你的手一起奔跑 你愿意夢醒的時候和我一起繼續...
    滄桑叔閱讀 241評論 0 1