即時通訊基礎

即時通訊系列閱讀

  1. 即時通訊基礎
  2. 即時通訊:XMPP基礎
  3. 即時通訊:XMPP項目實踐-微聊
  4. Smack類庫最好的學習資料

1. 即時通訊簡介

即時通訊(Instant Messaging)是目前Internet 上最為流行的通訊方式,各種各樣的即時通訊軟件也層出不窮;服務提供商也提供了越來越豐富的通訊服務功能室抽。不容置疑览祖,Internet 已經(jīng)成為真正的信息高速公路。從實際工程應用角度出發(fā),以計算機網(wǎng)絡原理為指導,結(jié)合當前網(wǎng)絡中的一些常用技術,編程實現(xiàn)基于C/S 架構的網(wǎng)絡聊天工具是切實可行的扭仁。

目前垮衷,中國市場上的企業(yè)級即時通信工具主要包括:信鴿、視高科技的視高可視協(xié)同辦公平臺乖坠、263EM搀突、群英CC2010、通軟聯(lián)合的GoCom熊泵、騰訊公司的RTX仰迁、IBM 的Lotus Sametime、點擊科技的GKE顽分、中國互聯(lián)網(wǎng)辦公
室的imo徐许、中國移動的企業(yè)飛信、華夏易聯(lián)的e-Link卒蘸、擎旗的UcStar 等雌隅。相對于個人即時通信工具而言,企業(yè)級即時通信工具更加強調(diào)安全性、實用性澄步、穩(wěn)定性和擴展性冰蘑。

1.1 即時聊天的解決方案

  • socket:套接字,連接需要ip端口村缸,分為tcp和udp兩種形式
  • xmpp:xmpp + openfire + asmack

1.2 常見協(xié)議

im_protocol.png

1.3 常見的術語

  • xmpp:基于xml的可拓展協(xié)議.
  • jabber:xmpp的前身.
  • openfire:支持xmpp的開源服務器
  • smack.jar:對xmpp協(xié)議封裝.方便開發(fā)的jar包.
  • spark.exe:基于xmpp的pc客戶端;
  • asmack.jar:smack.jar的精簡版.專門針對android端開發(fā)

2. 基本概念和原理

2.1 常用的網(wǎng)絡通信協(xié)議

TCP/IP:Transmission Control Protocol/Internet Protocol 的簡寫祠肥,中譯名為傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議,又名網(wǎng)絡通訊協(xié)議梯皿,是Internet 最基本的協(xié)議仇箱、Internet 國際互聯(lián)網(wǎng)絡的基礎,由網(wǎng)絡層的IP 協(xié)議和傳輸層的TCP協(xié)議組成东羹。TCP/IP 定義了電子設備如何連入因特網(wǎng)剂桥,以及數(shù)據(jù)如何在它們之間傳輸?shù)臉藴省f(xié)議采用了4 層的層級結(jié)構属提,每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求权逗。通俗而言:TCP 負責發(fā)現(xiàn)傳輸?shù)膯栴},一有問題就發(fā)出信號冤议,要求重新傳輸斟薇,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡亍6鳬P 是給因特網(wǎng)的每一臺聯(lián)網(wǎng)設備規(guī)定一個地址恕酸。

UDP:UDP 協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議堪滨,在網(wǎng)絡中它與TCP 協(xié)議一樣用于處理數(shù)據(jù)包点弯,是一種無連接的協(xié)議蒲犬。在OSI 模型中搬设,在第四層——傳輸層稿存,處于IP 協(xié)議的上一層涎拉。UDP 有不提供數(shù)據(jù)包分組赊级、組裝和不能對數(shù)據(jù)包進行排序的缺點陈轿,也就是說吝羞,當報文發(fā)送之后凉翻,是無法得知其是否安全完整到達的筐咧。UDP 用來支持那些需要在計算機之間傳輸數(shù)據(jù)的網(wǎng)絡應用。包括網(wǎng)絡視頻會議系統(tǒng)在內(nèi)的眾多的客戶/服務器模式的網(wǎng)絡應用都需要使用UDP協(xié)議噪矛。UDP 協(xié)議從問世至今已經(jīng)被使用了很多年量蕊,雖然其最初的光彩已經(jīng)被一些類似協(xié)議所掩蓋,但是即使是在今天UDP 仍然不失為一項非常實用和可行的網(wǎng)絡傳輸層協(xié)議艇挨。

TCP/IP 協(xié)議棧主要分為四層:應用層残炮、傳輸層、網(wǎng)絡層缩滨、數(shù)據(jù)鏈路層,每層都有相應的協(xié)議势就,如下圖:

所謂的協(xié)議就是雙方進行數(shù)據(jù)傳輸?shù)囊环N格式泉瞻。

2.2 TCP、UDP 特點對比

TCP 協(xié)議是面向連接苞冯、保證高可靠性(數(shù)據(jù)無丟失袖牙、數(shù)據(jù)無失序、數(shù)據(jù)無錯誤舅锄、數(shù)據(jù)無重復到達)傳輸層協(xié)議鞭达。UDP 協(xié)議也是傳輸層協(xié)議,它是無連接皇忿,不保證可靠的傳輸層協(xié)議畴蹭。

2.3 TCP 三次握手過程

1、請求端(通常稱為客戶)發(fā)送一個SYN 段指明客戶打算連接的服務器的端口鳍烁,以及初始序號(ISN)
2叨襟、服務器發(fā)回包含服務器的初始序號的SYN 報文段(報文段2)作為應答。同時幔荒,將確認序號設置為客戶的ISN加1 以對客戶的SYN 報文段進行確認糊闽。

TCP UDP
面向連接 面向非連接
可靠的連接 不可靠的連接
速度慢 速度快
大文件、重要的數(shù)據(jù)等 適合小數(shù)據(jù)爹梁、不重要

3右犹、客戶必須將確認序號設置為服務器的ISN 加1 以對服務器的SYN 報文段進行確認(報文段3)這三個報文段完成連接的建立。這個過程也稱為三次握手(three-way handshake)卫键。

上面的過程如下圖所示:

2.4 即時通訊形式

直接通訊

兩個不同客戶端之間不經(jīng)過服務器傀履,直接通過網(wǎng)絡進行數(shù)據(jù)的交互虱朵。常用的p2p 技術就是直接通訊的形式莉炉。

在線代理通訊

一個客戶端發(fā)送的消息先發(fā)送到服務器,服務器接收到消息后再發(fā)送給指定的另外一個客戶端碴犬。QQ 的消息
尤其是離線消息就是同在線代理的方式實現(xiàn)的絮宁。

離線代理通訊

一個客戶端發(fā)送消息給服務器,服務器存儲在數(shù)據(jù)庫中個服协,當另外一個客戶端上線后在發(fā)送過去绍昂。

離線擴展通訊

一個客戶端發(fā)送消息給服務器,服務器通過郵件偿荷、短信等其他形式將消息發(fā)送給接收者窘游。

3. ServerSocket 和Socket

3.1 使用Java 完成簡單的Socket 通信

在Java 中Socket 可以理解為客戶端或者服務器端的一個特殊的對象,這個對象有兩個關鍵的方法跳纳,一個是getInputStream 方法忍饰,另一個是getOutputStream 方法。getInputStream 方法可以得到一個輸入流寺庄,客戶端的Socket對象上的getInputStream 方法得到的輸入流其實就是從服務器端發(fā)回的數(shù)據(jù)流艾蓝。GetOutputStream 方法得到一個輸出流力崇,客戶端Socket 對象上的getOutputStream 方法返回的輸出流就是將要發(fā)送到服務器端的數(shù)據(jù)流,(其實是一個緩沖區(qū)赢织,暫時存儲將要發(fā)送過去的數(shù)據(jù))亮靴。

下面就讓我們寫一個簡單的Demo 來演示Socket 是如何使用的。

建立服務器類

服務類使用到的核心類的是ServerSocket于置。這里我們只需要建立一個Java Project 即可茧吊。

public class IMServer {
     private static ServerSocket   serverSocket;
     private static BufferedReader reader;

     public static void main(String[] args) {
         try {
             serverSocket = new ServerSocket(7788);
             /**
              * 等待接收客戶端連接進來,該方法是線程阻塞的
              */
             Socket accept = serverSocket.accept();
             /**

              * 獲取輸入流俱两,用于接收客戶端發(fā)來的數(shù)據(jù)
              */
             InputStream inputStream = accept.getInputStream();
             /**

              * 將字節(jié)輸入流轉(zhuǎn)化為字符輸出流
              */
             reader = new BufferedReader(new InputStreamReader(inputStream));
             /**

              * 打印數(shù)據(jù)
              */
             String tmp = null;
             while ((tmp = reader.readLine()) != null) {
                 System.out.println(tmp);
             }
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 if (serverSocket != null) {
                     serverSocket.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }
             if (reader != null) {
                 try {
                     reader.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 }

建立客戶端類

public class IMClient {
     private static Socket socket;

     private static BufferedWriter writer;

     /**
      * @param args
      */
     public static void main(String[] args) {
         try {
             socket = new Socket("127.0.0.1", 7788);
             /**
              * 獲取輸出流
              */
             OutputStream outputStream = socket.getOutputStream();
             writer = new BufferedWriter(new OutputStreamWriter(outputStream));
             writer.write("hello wo shi wzy!" + new Date().getTime());
             writer.close();
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if (socket != null) {
                 try {
                     socket.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             if (writer != null) {
                 try {
                     writer.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 }

在上面的代碼中我們僅僅實現(xiàn)了一個最簡單的服務器和客戶端饱狂,服務器啟動起來后只能接受到一次消息,然后就關閉了宪彩。如果想讓服務器一直運行休讳,應該通過死循環(huán)來處理不同的發(fā)送進來的消息。

Socket調(diào)試工具

TCP/UDP Socket調(diào)試工具提供了TCP Server,TCP Client,UDP Server,UDP Client,UDP Group 五種Socket調(diào)試方案

Socket調(diào)試工具

TCP

手機作為Client尿孔,PC作為Server

app和pc socket通信成功
public class MyClientActivity extends Activity {
    private EditText mEditText = null;
    private Button connectButton = null;
    private Button sendButton = null;
    private TextView mTextView = null;

    private Socket clientSocket = null;
    private OutputStream outStream = null;

    private Handler mHandler = null;

    private ReceiveThread mReceiveThread = null;
    private boolean stop = true;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mEditText = (EditText) this.findViewById(R.id.edittext);
        mTextView = (TextView) this.findViewById(R.id.retextview);
        connectButton = (Button) this.findViewById(R.id.connectbutton);
        sendButton = (Button) this.findViewById(R.id.sendbutton);
        sendButton.setEnabled(false);

        // 連接按鈕監(jiān)聽
        connectButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                try {
                    // 實例化對象并連接到服務器
                    clientSocket = new Socket("172.27.35.1", 60000);
                } catch (UnknownHostException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                displayToast("連接成功俊柔!");
                // 連接按鈕使能
                connectButton.setEnabled(false);
                // 發(fā)送按鈕使能
                sendButton.setEnabled(true);

                mReceiveThread = new ReceiveThread(clientSocket);
                stop = false;
                // 開啟線程
                mReceiveThread.start();
            }
        });

        // 發(fā)送數(shù)據(jù)按鈕監(jiān)聽
        sendButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                byte[] msgBuffer = null;
                // 獲得EditTex的內(nèi)容
                String text = mEditText.getText().toString();
                try {
                    // 字符編碼轉(zhuǎn)換
                    msgBuffer = text.getBytes("GB2312");
                } catch (UnsupportedEncodingException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

                try {
                    // 獲得Socket的輸出流
                    outStream = clientSocket.getOutputStream();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                try {
                    // 發(fā)送數(shù)據(jù)
                    outStream.write(msgBuffer);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // 清空內(nèi)容
                mEditText.setText("");
                displayToast("發(fā)送成功!");
            }
        });

        // 消息處理
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // 顯示接收到的內(nèi)容
                mTextView.setText((msg.obj).toString());
            }
        };

    }

    // 顯示Toast函數(shù)
    private void displayToast(String s) {
        Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
    }

    private class ReceiveThread extends Thread {
        private InputStream inStream = null;

        private byte[] buf;
        private String str = null;

        ReceiveThread(Socket s) {
            try {
                // 獲得輸入流
                this.inStream = s.getInputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            while (!stop) {
                this.buf = new byte[512];

                try {
                    // 讀取輸入數(shù)據(jù)(阻塞)
                    this.inStream.read(this.buf);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                // 字符編碼轉(zhuǎn)換
                try {
                    this.str = new String(this.buf, "GB2312").trim();
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                Message msg = new Message();
                msg.obj = this.str;
                // 發(fā)送消息
                mHandler.sendMessage(msg);

            }
        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        if (mReceiveThread != null) {
            stop = true;
            mReceiveThread.interrupt();
        }
    }

}

手機作為Server活合,PC作為Client

TCP
socket tcp
public class MyServerActivity extends Activity {
    private TextView ipTextView = null;
    private EditText mEditText = null;
    private Button sendButton = null;
    private TextView mTextView = null;

    private OutputStream outStream = null;
    private Socket clientSocket = null;
    private ServerSocket mServerSocket = null;

    private Handler mHandler = null;

    private AcceptThread mAcceptThread = null;
    private ReceiveThread mReceiveThread = null;
    private boolean stop = true;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ipTextView = (TextView) this.findViewById(R.id.iptextview);
        mEditText = (EditText) this.findViewById(R.id.sedittext);
        sendButton = (Button) this.findViewById(R.id.sendbutton);
        sendButton.setEnabled(false);
        mTextView = (TextView) this.findViewById(R.id.textview);

        // 發(fā)送數(shù)據(jù)按鈕監(jiān)聽
        sendButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                byte[] msgBuffer = null;
                // 獲得EditTex的內(nèi)容
                String text = mEditText.getText().toString();
                try {
                    // 字符編碼轉(zhuǎn)換
                    msgBuffer = text.getBytes("GB2312");
                } catch (UnsupportedEncodingException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

                try {
                    // 獲得Socket的輸出流
                    outStream = clientSocket.getOutputStream();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                try {
                    // 發(fā)送數(shù)據(jù)
                    outStream.write(msgBuffer);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // 清空內(nèi)容
                mEditText.setText("");
                displayToast("發(fā)送成功雏婶!");

            }
        });
        // 消息處理
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case 0: {
                    // 顯示客戶端IP
                    ipTextView.setText((msg.obj).toString());
                    // 使能發(fā)送按鈕
                    sendButton.setEnabled(true);
                    break;
                }
                case 1: {
                    // 顯示接收到的數(shù)據(jù)
                    mTextView.setText((msg.obj).toString());
                    break;
                }
                }

            }
        };

        mAcceptThread = new AcceptThread();
        // 開啟監(jiān)聽線程
        mAcceptThread.start();

    }

    // 顯示Toast函數(shù)
    private void displayToast(String s) {
        Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
    }

    private class AcceptThread extends Thread {
        @Override
        public void run() {
            try {
                // 實例化ServerSocket對象并設置端口號為7100
                mServerSocket = new ServerSocket(60000);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            try {
                // 等待客戶端的連接(阻塞)
                clientSocket = mServerSocket.accept();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            mReceiveThread = new ReceiveThread(clientSocket);
            stop = false;
            // 開啟接收線程
            mReceiveThread.start();

            Message msg = new Message();
            msg.what = 0;
            // 獲取客戶端IP
            msg.obj = clientSocket.getInetAddress().getHostAddress();
            // 發(fā)送消息
            mHandler.sendMessage(msg);

        }

    }

    private class ReceiveThread extends Thread {
        private InputStream mInputStream = null;
        private byte[] buf;
        private String str = null;

        ReceiveThread(Socket s) {
            try {
                // 獲得輸入流
                this.mInputStream = s.getInputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            while (!stop) {
                this.buf = new byte[512];

                // 讀取輸入的數(shù)據(jù)(阻塞讀)
                try {
                    this.mInputStream.read(buf);
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

                // 字符編碼轉(zhuǎn)換
                try {
                    this.str = new String(this.buf, "GB2312").trim();
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                Message msg = new Message();
                msg.what = 1;
                msg.obj = this.str;
                // 發(fā)送消息
                mHandler.sendMessage(msg);

            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        if (mReceiveThread != null) {
            stop = true;
            mReceiveThread.interrupt();
        }
    }

}

UDP

udp
socket udp
public class MainActivity extends Activity {
    private static String TAG = "CallActivity";
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            // 播放聲音
            initBeepSound();
            playBeepSoundAndVibrate();
            Log.i(TAG, "reciever_msg");
            String result = (String) msg.obj;
            System.out.println(result);
            tv_result.setText(result);
        };
    };
    private String ip;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ip = IpUtil.getIp(this);
        Toast.makeText(this, ip, Toast.LENGTH_LONG).show();
        System.out.println("ip:" + ip);
        tv_result = (TextView) findViewById(R.id.tv_result);
    }

    /**
     * 發(fā)送者
     */
    public void send(View v) {
        new Thread() {
            @Override
            public void run() {
                try {
                    //1.  創(chuàng)建一個DatagramSocket對象
                    DatagramSocket socket = new DatagramSocket(5678);
                    //2.  創(chuàng)建一個 InetAddress , 相當于是地址,就是想要發(fā)送的ip地址
                    InetAddress serverAddress = InetAddress.getByName("172.27.35.1");
                    //3.  這是隨意發(fā)送一個數(shù)據(jù)
                    String str = "來自android手機的問候";
                    //4.  轉(zhuǎn)為byte類型
                    byte data[] = str.getBytes("GBK");
                    //5.  創(chuàng)建一個DatagramPacket 對象白指,并指定要講這個數(shù)據(jù)包發(fā)送到網(wǎng)絡當中的哪個地址留晚,以及端口號
                    DatagramPacket pack = new DatagramPacket(data, data.length, serverAddress, 5678);
                    //6.  調(diào)用DatagramSocket對象的send方法 發(fā)送數(shù)據(jù)
                    socket.send(pack);
                } catch (SocketException e) {
                    Log.i(TAG, "error");
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    Log.i(TAG, "error");
                    e.printStackTrace();
                } catch (IOException e) {
                    Log.i(TAG, "error");
                    e.printStackTrace();
                }
            }
        }.start();

    }

    /**
     * 接收者
     * @param v
     */
    public void receive(View v) {
        new Thread() {
            @Override
            public void run() {
                // 執(zhí)行完畢后給handler發(fā)送一個空消息
                try {
                    // 1. 創(chuàng)建一個DatagramSocket對象,并指定監(jiān)聽的端口號/\
                    DatagramSocket socket = new DatagramSocket(5678);
                    // 2. 創(chuàng)建一個byte數(shù)組用于接收
                    byte data[] = new byte[1024];
                    // 3. 創(chuàng)建一個空的DatagramPackage對象
                    DatagramPacket pack = new DatagramPacket(data, data.length);
                    // 4. 使用receive方法接收發(fā)送方所發(fā)送的數(shù)據(jù),同時這也是一個阻塞的方法
                    while (true) {
                        Log.i(TAG, "reciever_1");
                        socket.receive(pack);
                        Log.i(TAG, "reciever_2");
                        // 5. 得到發(fā)送過來的數(shù)據(jù)
                        //                      String result = new String(pack.getData(), pack.getOffset(), pack.getLength(),"GBK");
                        String result = new String(pack.getData(), pack.getOffset(), pack.getLength());
                        Message msg = new Message();
                        msg.obj = result;
                        handler.sendMessage(msg);
                        Log.i(TAG, "sendmsg_1");
                    }
                } catch (UnknownHostException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Log.i(TAG, "error");
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    Log.i(TAG, "error");
                }
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    // 播放聲音
    private static final float BEEP_VOLUME = 0.10f;
    private MediaPlayer mediaPlayer;

    private void initBeepSound() {
        if (mediaPlayer == null) {
            setVolumeControlStream(AudioManager.STREAM_MUSIC);
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mediaPlayer.setOnCompletionListener(beepListener);
            AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.beep);
            try {
                mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
                file.close();
                mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
                mediaPlayer.prepare();
            } catch (IOException e) {
                mediaPlayer = null;
            }
        }
    }

    private void playBeepSoundAndVibrate() {
        if (mediaPlayer != null) {
            mediaPlayer.start();
        }
        // 震動
        Vibrator mVibrator = (Vibrator) getApplication().getSystemService(Service.VIBRATOR_SERVICE);
        mVibrator.vibrate(2000);
        long[] pattern = { 0, 100, 200, 100, 200 };
        mVibrator.vibrate(pattern, -1);
    }

    private final OnCompletionListener beepListener = new OnCompletionListener() {
        public void onCompletion(MediaPlayer mediaPlayer) {
            mediaPlayer.seekTo(0);
        }
    };
    // 退出提醒
    private long exitTime;
    private TextView tv_result;

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if ((System.currentTimeMillis() - exitTime) > 2000) {
                Toast.makeText(getApplicationContext(), "再按一次退出" + getResources().getString(R.string.app_name),
                        Toast.LENGTH_SHORT).show();
                exitTime = System.currentTimeMillis();
            } else {
                finish();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

IpUtil.java

public class IpUtil {
    /**
     * 獲取手機ip
     * 
     * @return
     */
    public static String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        String ip = inetAddress.getHostAddress().toString();
                        System.out.println("getLocalIpAddressIP:"+ip);
                        return ip;
                    }
                }
            }
        } catch (SocketException ex) {
            Log.e("ifo", ex.toString());
        }
        return "";
    }

    public static String getIp(Activity activity) {
        WifiManager wifiManager = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ipAddress = wifiInfo.getIpAddress();

        // 格式化IP address告嘲,例如:格式化前:1828825280错维,格式化后:192.168.1.109
        String ip = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
        System.out.println("getIpIP:"+ip);
        return ip;

    }

}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市橄唬,隨后出現(xiàn)的幾起案子赋焕,更是在濱河造成了極大的恐慌,老刑警劉巖仰楚,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隆判,死亡現(xiàn)場離奇詭異,居然都是意外死亡僧界,警方通過查閱死者的電腦和手機侨嘀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捂襟,“玉大人咬腕,你說我怎么就攤上這事“驶恚” “怎么了郎汪?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵赤赊,是天一觀的道長。 經(jīng)常有香客問我煞赢,道長抛计,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任照筑,我火速辦了婚禮吹截,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凝危。我一直安慰自己波俄,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布蛾默。 她就那樣靜靜地躺著懦铺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪支鸡。 梳的紋絲不亂的頭發(fā)上冬念,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音牧挣,去河邊找鬼急前。 笑死,一個胖子當著我的面吹牛瀑构,可吹牛的內(nèi)容都是我干的裆针。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼寺晌,長吁一口氣:“原來是場噩夢啊……” “哼世吨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起折剃,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤另假,失蹤者是張志新(化名)和其女友劉穎像屋,沒想到半個月后怕犁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡己莺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年奏甫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凌受。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡阵子,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胜蛉,到底是詐尸還是另有隱情挠进,我是刑警寧澤色乾,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站领突,受9級特大地震影響暖璧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜君旦,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一澎办、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧金砍,春花似錦局蚀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鹅巍,卻和暖如春奉件,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昆著。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工县貌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凑懂。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓煤痕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親接谨。 傳聞我的和親對象是個殘疾皇子摆碉,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 前面關于即時通訊基礎Socket,大家學習使用XMPP之前可以先看看即時通訊系列之Socket簡介 前言 前段時間...
    音符上的碼字員閱讀 4,216評論 3 16
  • 前言 本文會用實例的方式扫夜,將iOS各種IM的方案都簡單的實現(xiàn)一遍楞泼。并且提供一些選型、實現(xiàn)細節(jié)以及優(yōu)化的建議笤闯。 注:...
    涂耀輝閱讀 94,244評論 232 1,748
  • 大學學習網(wǎng)絡基礎的時候老師講過时呀,網(wǎng)絡由下往上分為物理層张漂、數(shù)據(jù)鏈路層、網(wǎng)絡層谨娜、傳輸層鹃锈、會話層、表示層和應用層瞧预。通過初...
    西門丨不吹雪閱讀 1,783評論 0 19
  • 現(xiàn)在很多社交軟件都很火屎债,F(xiàn)aceBook、易信垢油、陌陌等盆驹,QQ、微信當然依然是中文社交最大霸主滩愁。除此之外伙星也很火躯喇。...
    軟工官博閱讀 1,306評論 0 5
  • 想了半天不知道應該用什么樣的開頭,以后尤其注意下別人的開頭硝枉。 來到1z恰好一周廉丽,從上周六到店,這周六上完妻味,已然過去...
    綿花不白閱讀 545評論 0 1