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

項(xiàng)目需求收集通過Socket向服務(wù)器發(fā)送圖片,之前沒搞過,網(wǎng)上搜搜寫了下面的例子,勉強(qiáng)解決了需求。

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

要求發(fā)送的消息的格式是,8個(gè)字節(jié)的消息長(zhǎng)度+消息體

因?yàn)樾枰?個(gè)字節(jié),所以消息長(zhǎng)度決定用long

如果需要4個(gè)字節(jié),可以用int。

手機(jī)客戶端接收服務(wù)器的文字消息

服務(wù)端

服務(wù)端定義好端口號(hào),開啟以一個(gè)ServerSocket,寫入文字消息:

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

    public static void main(String[] args) {
        try {
          //發(fā)送的內(nèi)容
            sendMsg("來自服務(wù)器的問候");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  
    public static void sendMsg(String msg) throws IOException {
      System.out.println("開始連接");
      //創(chuàng)建socket服務(wù)端口是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("通訊結(jié)束");
    }
  
    public static void sendTextMsg(DataOutputStream out, String msg) throws IOException {
      //先寫長(zhǎng)度,就是消息體的字節(jié)數(shù),long剛好8個(gè)字節(jié)  
        out.writeLong(msg.getBytes().length);
      //寫入消息
        out.write(msg.getBytes());
    }
}

客戶端,接收消息

客戶端首先要跟服務(wù)器進(jìn)行連接,然后才能進(jìn)行通訊。

Socket連接需要網(wǎng)絡(luò)權(quán)限:

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

因?yàn)閷儆诰W(wǎng)絡(luò)通訊,所以socket連接也不能放在主線程中,否則會(huì)報(bào)錯(cuò)

  • 添加按鈕

    在布局中加一個(gè)按鈕,點(diǎn)擊方法是receive

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

    在Activity中定義方法

    因?yàn)椴荒茉僦骶€程中訪問,所以需要子線程。這里直接new了、、

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

運(yùn)行測(cè)試

先運(yùn)行服務(wù)端,會(huì)發(fā)現(xiàn)程序沒有一次執(zhí)行完,在阻塞著等待連接

這時(shí)點(diǎn)擊app中的接收按鈕,日志中會(huì)打印接收到的信息

長(zhǎng)度為24,消息內(nèi)容為“來自服務(wù)器的問候”。

因?yàn)樵趗tf-8中一個(gè)漢字是3個(gè)字節(jié),所以8個(gè)漢字的消息長(zhǎng)度是24字節(jié)。

這是看服務(wù)器端的打?。?/p>

發(fā)現(xiàn)服務(wù)端也正常執(zhí)行完畢了。

手機(jī)客戶端向服務(wù)端發(fā)送文字消息

服務(wù)端接收消息

public class Service {
    private static Socket socket;
  //定義端口號(hào)
    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();
      //獲取輸入流,通過這個(gè)流來讀取消息
        DataInputStream input = new DataInputStream(socket.getInputStream());
      //接收文字消息
        getTextMsg(input);
        input.close();
        socket.close();
        serverSocket.close();
        System.out.println("通訊結(jié)束");
    }
  
    public static String getTextMsg(DataInputStream input) throws IOException {
      //一樣先讀長(zhǎng)度,再根據(jù)長(zhǎng)度讀消息
        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;
    }
}

客戶端發(fā)送消息

  • 增加一個(gè)發(fā)送的按鈕:
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="發(fā)送!"
        android:onClick="send"/>
  • 定義方法
public void send(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Socket socket;
                try {
                //建立連接
                    socket = new Socket(host, post);
                    //獲取輸出流,通過這個(gè)流發(fā)送消息
                    DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                    //發(fā)送文字消息
                    sendTextMsg(out,"來自手機(jī)客戶端的消息");
                    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;
  //先發(fā)送長(zhǎng)度,在發(fā)送內(nèi)容
        out.writeLong(len);
        out.write(bytes);
    }

運(yùn)行測(cè)試

先運(yùn)行服務(wù)端,也會(huì)停留著這個(gè)界面等待連接:

然后點(diǎn)擊客戶端的發(fā)送按鈕,這是服務(wù)端會(huì)變成下面這樣,完成這次消息的通訊

手機(jī)客戶端想服務(wù)端發(fā)送圖片

服務(wù)端接收?qǐng)D片,并保存到本地

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

下面是getImgMsg(input);方法:

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

客戶端發(fā)送圖片

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

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

運(yùn)行測(cè)試

還是先開啟服務(wù)端,服務(wù)器變成:

然后點(diǎn)擊app的發(fā)送圖片按鈕,app中打印日志:

說明發(fā)送的圖片的長(zhǎng)度是11418個(gè)字節(jié),大致?lián)Q算大小是11k。

然后看服務(wù)端的日志:

接收到的長(zhǎng)度也是11418,并且保存到了文件,

關(guān)于心跳包

上面的例子中,每發(fā)送一次之后就把鏈接關(guān)閉了:

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

心跳其實(shí)就是定期向服務(wù)端發(fā)送一個(gè)小數(shù)據(jù),比如0.

讓服務(wù)器知道這個(gè)鏈接還有用,不用關(guān)閉。

簡(jiǎn)單實(shí)現(xiàn)起來就是客戶端通過一個(gè)無限循環(huán),不停地向服務(wù)端發(fā)送消息,服務(wù)端通過一個(gè)無限循環(huán)不停地接收消息,都不關(guān)閉這個(gè)鏈接就行了。

服務(wù)端

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();
        }
    }

客戶端,一秒發(fā)送一次消息

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: 開始循環(huán)發(fā)送發(fā)送心跳");
//一秒發(fā)送一個(gè)0,
while (true){
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "run: 發(fā)送心跳0");
    try {
        outputStream.writeLong("0".getBytes().length);
        outputStream.write("0".getBytes());
    } catch (IOException e) {
        e.printStackTrace();
        isConnect = false;
    }
}

運(yùn)行測(cè)試

先運(yùn)行服務(wù)端:

然后點(diǎn)擊發(fā)送心跳的按鈕,app的日志中打?。?/p>

在服務(wù)端的日志中可以看到:

一秒一次

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/190...
    archyly閱讀 1,125評(píng)論 0 8
  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,594評(píng)論 0 6
  • 大綱 一.Socket簡(jiǎn)介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    VD2012閱讀 2,700評(píng)論 0 5
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,251評(píng)論 0 7
  • 我可否出現(xiàn)在你那溫馨的床頭親吻你的額頭 我可曾出現(xiàn)在你那浪漫的夢(mèng)里牽著你的手一起奔跑 你愿意夢(mèng)醒的時(shí)候和我一起繼續(xù)...
    滄桑叔閱讀 250評(píng)論 0 1

友情鏈接更多精彩內(nèi)容