Android Socket的使用到簡單封裝(一)

在我們使用okHttp等網(wǎng)絡框架去請求網(wǎng)絡時,都是我們Android客戶端主動發(fā)起一次請求,然后服務器返回相應的數(shù)據(jù)給我們客戶端去使用,即經(jīng)常說起的一次請求一次響應,和Servlet一樣。這樣的請求都是基于HTTP協(xié)議去實現(xiàn)的,在HTTP模式下,只能是客戶端主動去請求數(shù)據(jù),服務端才會給你返回你想要的數(shù)據(jù),也就是說,服務端只能是你要了我才能給。我們主動久了,總會希望服務端也主動一點,給我們主動發(fā)一點數(shù)據(jù)。試想一下,和妹子一起么么噠的時候,妹子在你耳邊輕輕的嬌嗔:“我要,我要,我要給你,我就要給你嘛!”,是不是超級刺激?為了解決妹子的需求,我們肯定要使上九牛二虎之力,把妹子整的服服帖帖的,一陣巫山云雨,不可描述之后,妹子嬌紅著臉,把腦袋埋在你的胸口,宛如一只小綿羊。emmmmm,舒服了?;赥CP協(xié)議的Socket就是為了幫助我們解決這種需求的。它能讓你和妹子相互的“要”,相互的“給”,還能超級持久哦。

下面就簡單的在代碼里介紹一下Socket長連接的使用,栗子要實現(xiàn)的效果是:安卓客戶端給服務端發(fā)送一個消息,服務端收到消息后立馬給我返回一個消息。

看圖說話:我代表客戶端,媳婦兒代表服務端
[圖片上傳中...(image-810b3b-1532078403207-0)]


S80720-17021934.jpg

具體實現(xiàn):
下面是MainActivity的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".MainActivity">

    <Button
        android:id="@+id/bt_sendData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發(fā)送數(shù)據(jù)"
        android:layout_alignParentTop="true"/>

    <LinearLayout
        android:id="@+id/ll_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/bt_sendData"
        android:layout_marginTop="20dp"
        android:orientation="vertical"></LinearLayout>

</RelativeLayout>

接著繼續(xù)看MainActivity的代碼:
1.首先調(diào)用doConnect()方法建立服務器連接,由于網(wǎng)絡請求要寫在子線程中,所以這里新建了一個線程,在子線程中建立連接。在這個連接中使用了我本機的ip地址(用手機測試時,手機需連接wifi,和電腦處于同一個局域網(wǎng)中),以及6666端口,設置了5秒的響應超時。

private void doConnect() {
        //子線程請求網(wǎng)絡
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Socket socket = new Socket();
                    //建立Socket連接,需要服務端IP地址和端口號
                    socket.connect(new InetSocketAddress("192.168.1.188", 6666), 5 * 1000);
                    //通過Socket實例獲取輸入輸出流,作為和服務器交換數(shù)據(jù)的通道
                    bis = new BufferedInputStream(socket.getInputStream());
                    bos = new BufferedOutputStream(socket.getOutputStream());

                    readThread = new ReadThread();
                    readThread.start();

                    Log.i("Socket","socket連接成功");
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.i("Socket","socket連接失敗");
                }
            }
        }).start();
    }

2.新建子線程WriteThread,負責向服務器發(fā)送數(shù)據(jù), showText()是顯示客服端與服務端通訊數(shù)據(jù)的方法。

//啟動一個線程,在子線程中發(fā)送數(shù)據(jù)給服務端
   private class WriteRead extends Thread{
       @Override
       public void run() {
           try {
               String iSay = "我說:媳婦兒媳婦兒,你還行嗎?";
               bos.write(iSay.getBytes());
               //記得一定要flush一下
               bos.flush();
               showText(FROM_CLIENT, iSay);
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
   }

3.客服端通過WriteRead 向服務端發(fā)送數(shù)據(jù)后,我們肯定就是要去建立一個測試的服務端去接收數(shù)據(jù)。
新建Java類SocketServer,需要注意的是,這個類的位置需要與與包名平級,即放在As的Java目錄下。
代碼中的端口號需要與Android中設置的端口號一樣,即6666。
在連接建立后立馬創(chuàng)建了一個服務端的ReadThread去讀取信息,讀取到信息后,又立馬使用bos給客戶端回寫了一條消息。這時,我們就需要在客戶端去創(chuàng)建接受消息的方法了。

public class SocketServer {

    private static BufferedOutputStream bos;
    private static BufferedInputStream bis;
    private static ReadThread readThread;

    public static void main(String args[]){
        try{
            //通過accept方法獲取socket實例,只有在程序獲得到一個socket實例后,程序才會繼續(xù)向下執(zhí)行,否則會一直阻塞在accept方法
            ServerSocket  serverSocket = new ServerSocket(6666);
            Socket socket = serverSocket.accept();
            //通過Socket實例獲取輸入輸出流,作為和服務器交換數(shù)據(jù)的通道
            bis = new BufferedInputStream(socket.getInputStream());
            bos = new BufferedOutputStream(socket.getOutputStream());

            readThread = new ReadThread();
            readThread.start();
        }catch (Exception e){
            e.printStackTrace();
            System.out.print("完了完了,我GG了");
        }
    }
    //啟動一個線程,一直讀取從客戶端發(fā)送過來的消息
    private static class ReadThread extends Thread{
        @Override
        public void run() {
            while (true){
                byte[] data = new byte[1024];
                int size = 0;
                try {
                    //收到客服端發(fā)送的消息后,返回一個消息給客戶端
                    while((size = bis.read(data)) != -1){
                        String str = new String(data,0,size);
                        System.out.print("老公對我說:"+str);
                        bos.write("媳婦兒說:死鬼!我還要,不夠嘛。".getBytes());
                        bos.flush();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.print("完了完了,我GG了");
                }
            }
        }
    }

4.回到MainActivity去創(chuàng)建接收服務端返回消息的方法:即客戶端的ReadThread,同樣的也需要啟動一個線程,因為不管是使用bis去讀取數(shù)據(jù),還是使用bos去讀取數(shù)據(jù),都是需要去操作網(wǎng)絡的,而我們Android中只能在子線程操作網(wǎng)絡。
讀取到數(shù)據(jù)后,使用Handler將信息傳遞回主線程顯示

//啟動一個線程,一直讀取從服務端發(fā)送過來的消息
    private class ReadThread extends Thread {
        @Override
        public void run() {
            while (true) {
                byte[] data = new byte[1024];
                int size = 0;
                try {
                    //收到客服端發(fā)送的消息后,返回一個消息給客戶端
                    while((size = bis.read(data)) != -1) {
                        String str = new String(data, 0, size);
                        Message msg = new Message();
                        msg.obj = new String(str);
                        mHandler.sendMessage(msg);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

5.下面是MainActivity中所有的代碼:

public class MainActivity extends AppCompatActivity {

    private Button bt_sendData;
    private LinearLayout ll_content;
    private BufferedOutputStream bos;
    private BufferedInputStream bis;
    private ReadThread readThread;
    private WriteRead writeRead;
    private int FROM_CLIENT = 0;
    private int FROM_SERVER = 1;

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

    private void initView() {
        bt_sendData = (Button) findViewById(R.id.bt_sendData);
        ll_content = (LinearLayout) findViewById(R.id.ll_content);

        bt_sendData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeRead = new WriteRead();
                writeRead.start();
            }
        });
    }

    private void doConnect() {
        //子線程請求網(wǎng)絡
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Socket socket = new Socket();
                    //建立Socket連接,需要服務端IP地址和端口號
                    socket.connect(new InetSocketAddress("192.168.1.188", 6666), 5 * 1000);
                    //通過Socket實例獲取輸入輸出流,作為和服務器交換數(shù)據(jù)的通道
                    bis = new BufferedInputStream(socket.getInputStream());
                    bos = new BufferedOutputStream(socket.getOutputStream());

                    readThread = new ReadThread();
                    readThread.start();

                    Log.i("Socket","socket連接成功");
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.i("Socket","socket連接失敗");
                }
            }
        }).start();
    }

    //啟動一個線程,一直讀取從服務端發(fā)送過來的消息
    private class ReadThread extends Thread {
        @Override
        public void run() {
            while (true) {
                byte[] data = new byte[1024];
                int size = 0;
                try {
                    //收到客服端發(fā)送的消息后,返回一個消息給客戶端
                    while((size = bis.read(data)) != -1) {
                        String str = new String(data, 0, size);
                        Message msg = new Message();
                        msg.obj = new String(str);
                        mHandler.sendMessage(msg);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //啟動一個線程,在子線程中發(fā)送數(shù)據(jù)給服務端
    private class WriteRead extends Thread{
        @Override
        public void run() {
            try {
                String iSay = "我說:媳婦兒媳婦兒,你還行嗎?";
                bos.write(iSay.getBytes());
                //記得一定要flush一下
                bos.flush();
                showText(FROM_CLIENT, iSay);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String xifuerSay = msg.obj.toString();
            showText(FROM_SERVER, xifuerSay);
        }
    };

    //顯示客戶端與服務端之間的對話
    private void showText(final int type,final  String text) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                TextView tv = new TextView(MainActivity.this);
                tv.setText(text);
                tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
                tv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                if (type == FROM_CLIENT) {
                    tv.setTextColor(Color.parseColor("#000000"));
                } else if (type == FROM_SERVER) {
                    tv.setTextColor(Color.parseColor("#ff0000"));
                }
                ll_content.addView(tv);
            }
        });
    }
}

6.主程序就是一個MainActivity和一個SocketServer類,測試的時候首先需要在As中啟動服務端SocketServer,然后再啟動我們的APP程序。
7.這個程序有很多的不足,比如粗暴的創(chuàng)建線程,退出App后重新進入點擊按鈕,就收不到服務端返回的消息了。下篇文章將在服務中結(jié)合線程池去解決這些問題。

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

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

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