1. 程式人生 > >【Android Socket專題】:UDP通訊客戶端app的demo的實現

【Android Socket專題】:UDP通訊客戶端app的demo的實現

Android Socket 專題:

關於UDP通訊其實可以不用多做累述,多數像我一樣的朋友在此基礎上也只是為了應用,需要了解下該瞭解的就可以了,具體的想要對這個協議研究深入的,可以自己努力!我這兒只做Android客戶端的應用實現,注意是客戶端,不是伺服器,那麼伺服器怎麼實現呢? 點選上方,已經補充!!!


規劃自己的介面的(非常簡單):

XML實現效果圖如下:

附上對應XML程式碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:layout_weight="2"
    tools:context="jiugaosh.com.udpdemo.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="接收區:" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:textSize="8dp"
                android:id="@+id/btn_udpConn"
                android:text="建立連線" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:textSize="8dp"
                android:id="@+id/btn_udpClose"
                android:text="關閉連線" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:textSize="8dp"
                android:id="@+id/btn_CleanRecv"
                android:text="清除接收區" />
        </LinearLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_weight="7"
            android:layout_height="0dp"
            android:id="@+id/txt_Recv"
            android:background="@drawable/border"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layout_weight="2">
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:text="Hex顯示"/>
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"

                android:text="二進位制顯示"/>
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:text="十進位制顯示"/>

        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="傳送區:" />
        <TextView
            android:layout_width="match_parent"
            android:layout_weight="7"
            android:background="@drawable/border"
            android:id="@+id/txt_Send"
            android:layout_height="0dp" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layout_weight="2">
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:text="Hex顯示"/>
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"

                android:text="二進位制顯示"/>
            <CheckBox
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:text="十進位制顯示"/>

        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layout_weight="2">
            <EditText
                android:layout_width="0dp"
                android:layout_weight="5"
                android:id="@+id/edit_Send"
                android:background="@drawable/border"
                android:layout_height="match_parent" />
            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"
                android:layout_gravity="right"
                android:id="@+id/btn_Send"
                android:text="傳送"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

至此我們介面全部完成,我們開始實現對應功能!繼續向下看。

首先在寫程式碼前要說明下關鍵性的幾個類,我們完成udp客戶端其實主要就是通過這幾個類實現的:

1、DatagramSocket

2、DatagramPacket 

3、InetAddress

關於這幾個類到底是什麼意思,包含了什麼資訊,大家可以自己去百度谷歌,或者直接開啟JAVA API文件檢視便知,這裡不做贅述,看具體實現。

關於UDP客戶端收發這裡,實現思路是:

啟動UDP執行緒:

1、建立DatagramSocket通訊資料報

2、建立接收事件專用DatagramPacket資料包

3、建立超時(這個和後面關閉通訊有關)

4、建立監聽接收訊息迴圈機制(接收訊息處理在此處,接收到的訊息通過

BroadcastReceiver傳送給主介面)

5、結束迴圈,關閉資料報。

以上為收取資訊思路,傳送資訊則為UDP執行緒中的一個方法,直接被呼叫,共享了接收塊的DatagramSocket資料報:

UDP Thread Run--->[Function方法]send(String SendMsg)

下面看具體程式碼實現:

package jiugaosh.com.udpdemo;

import android.content.Intent;
import android.util.Log;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * Created by lenovo on 2016/2/23.
 */
public class UDPClient implements Runnable{
    final static int udpPort = 9999;
    final static String hostIp = "192.168.1.4";
    private static DatagramSocket socket = null;
    private static DatagramPacket packetSend,packetRcv;
    private boolean udpLife = true; //udp生命執行緒
    private byte[] msgRcv = new byte[1024]; //接收訊息

    public UDPClient(){
        super();
    }

    //返回udp生命執行緒因子是否存活
    public boolean isUdpLife(){
        if (udpLife){
            return true;
        }

        return false;
    }

    //更改UDP生命執行緒因子
    public void setUdpLife(boolean b){
        udpLife = b;
    }

    //傳送訊息
    public String send(String msgSend){
        InetAddress hostAddress = null;

        try {
            hostAddress = InetAddress.getByName(hostIp);
        } catch (UnknownHostException e) {
            Log.i("udpClient","未找到伺服器");
            e.printStackTrace();
        }

/*        try {
            socket = new DatagramSocket();
        } catch (SocketException e) {
            Log.i("udpClient","建立傳送資料報失敗");
            e.printStackTrace();
        }*/

         packetSend = new DatagramPacket(msgSend.getBytes() , msgSend.getBytes().length,hostAddress,udpPort);

        try {
            socket.send(packetSend);
        } catch (IOException e) {
            e.printStackTrace();
            Log.i("udpClient","傳送失敗");
        }
     //   socket.close();
        return msgSend;
    }

    @Override
    public void run() {

        try {
            socket = new DatagramSocket();
            socket.setSoTimeout(3000);//設定超時為3s
        } catch (SocketException e) {
            Log.i("udpClient","建立接收資料報失敗");
            e.printStackTrace();
        }
        packetRcv = new DatagramPacket(msgRcv,msgRcv.length);
        while (udpLife){
            try {
                Log.i("udpClient", "UDP監聽");
                socket.receive(packetRcv);
                String RcvMsg = new String(packetRcv.getData(),packetRcv.getOffset(),packetRcv.getLength());
                //將收到的訊息發給主介面
                Intent RcvIntent = new Intent();
                RcvIntent.setAction("udpRcvMsg");
                RcvIntent.putExtra("udpRcvMsg", RcvMsg);
                MainActivity.context.sendBroadcast(RcvIntent);

                Log.i("Rcv",RcvMsg);
            }catch (IOException e){
                e.printStackTrace();
            }
        }

        Log.i("udpClient","UDP監聽關閉");
        socket.close();
    }
}

至此UDP核心程式碼全部部署完成,其中包含了少許其他功能, 如傳送給主介面訊息程式碼等,不影響,有不需要的可以自行刪除。其中如果想傳送訊息,那麼呼叫其中的send方法即可。

下面則是主介面程式碼實現,我的實現思路如下(各有各的實現方法,如果您有更好的,如果願意請告訴我,我十分願意學習):

MainActivity介面:

Handler類(主要處理UI更新事件)

OnCreate(繫結控制元件,事件監聽,註冊BroadcastReceiver等)

MyButtonClick類(處理各按鈕事件)

BroadcastReceiver(廣播接收器)

至於裡面各自類包含的小邏輯,就不多講了,看demo可以很快便知道如何使用對應了。

話不多說,放碼過來:

package jiugaosh.com.udpdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.os.Handler;

import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class MainActivity extends AppCompatActivity {
    TextView txt_Recv,txt_Send;
    Button   btn_CleanRecv,btn_Send,btn_UdpConn,btn_UdpClose;
    EditText edit_Send;
    private UDPClient client = null;
    public static Context context;
    private final MyHandler myHandler = new MyHandler(this);
    private StringBuffer udpRcvStrBuf=new StringBuffer(),udpSendStrBuf=new StringBuffer();



    MyBtnClick myBtnClick = new MyBtnClick();


    private class MyHandler extends Handler{
        private final WeakReference<MainActivity> mActivity;
        public MyHandler(MainActivity activity){
            mActivity = new WeakReference<MainActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    udpRcvStrBuf.append(msg.obj.toString());
                    txt_Recv.setText(udpRcvStrBuf.toString());
                    break;
                case 2:
                    udpSendStrBuf.append(msg.obj.toString());
                    txt_Send.setText(udpSendStrBuf.toString());
                    break;
                case 3:
                    txt_Recv.setText(udpRcvStrBuf.toString());
                    break;
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        bindWidget();   //控制元件繫結
        listening();    //監聽事件
        bindReceiver();//註冊broadcastReceiver接收器
        iniWidget();    //初始化控制元件狀態
    }

    private void bindWidget(){
        txt_Recv = (TextView)findViewById(R.id.txt_Recv);
        txt_Send = (TextView)findViewById(R.id.txt_Send);
        btn_CleanRecv = (Button)findViewById(R.id.btn_CleanRecv);
        btn_Send = (Button)findViewById(R.id.btn_Send);
        btn_UdpConn = (Button)findViewById(R.id.btn_udpConn);
        btn_UdpClose = (Button)findViewById(R.id.btn_udpClose);
        edit_Send = (EditText)findViewById(R.id.edit_Send);
    }

    private void listening(){
        btn_Send.setOnClickListener(myBtnClick);
        btn_UdpConn.setOnClickListener(myBtnClick);
        btn_UdpClose.setOnClickListener(myBtnClick);
        btn_CleanRecv.setOnClickListener(myBtnClick);
    }

    private void bindReceiver(){
        IntentFilter udpRcvIntentFilter = new IntentFilter("udpRcvMsg");
        registerReceiver(broadcastReceiver,udpRcvIntentFilter);
    }

    private void iniWidget(){
        btn_Send.setEnabled(false);
    }

    class MyBtnClick implements Button.OnClickListener{

        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btn_CleanRecv:
                    udpRcvStrBuf.delete(0,udpRcvStrBuf.length());
                    Message message = new Message();
                    message.what = 3;
                    myHandler.sendMessage(message);
                    break;
                case R.id.btn_udpConn:
                    //建立執行緒池
                    ExecutorService exec = Executors.newCachedThreadPool();
                    client = new UDPClient();
                    exec.execute(client);
                    btn_UdpClose.setEnabled(true);
                    btn_UdpConn.setEnabled(false);
                    btn_Send.setEnabled(true);
                    break;
                case R.id.btn_udpClose:
                    client.setUdpLife(false);
                    btn_UdpConn.setEnabled(true);
                    btn_UdpClose.setEnabled(false);
                    btn_Send.setEnabled(false);
                    break;
                case R.id.btn_Send:
                    Thread thread = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Message message = new Message();
                            message.what = 2;
                            if (edit_Send.getText().toString()!=""){
                                client.send(edit_Send.getText().toString());
                                message.obj = edit_Send.getText().toString();
                                myHandler.sendMessage(message);
                            }
                            
                        }
                    });
                    thread.start();
                    break;
            }
        }
    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.hasExtra("udpRcvMsg"))  {
                Message message = new Message();
                message.obj = intent.getStringExtra("udpRcvMsg");
                message.what = 1;
                Log.i("主介面Broadcast","收到"+message.obj.toString());
                myHandler.sendMessage(message);
            }
        }
    };


}
當然,不能忘記在AndroidMainFest檔案中加上許可權,畢竟你是要聯網的!
<uses-permission android:name="android.permission.INTERNET" />


寫在最後:實現這些功能其實較為簡單,網上有很多成熟且很棒的例子,各位多看多學,必沒有問題!其中進位制轉換其實很簡單實現,各位只要對那兩個stringbuffer進行進位制操作和顯示即可!

通訊效果請點選上方UDP server最後有圖片展示

附上demo:

http://download.csdn.net/detail/shankezh/9442049