基于UDP實現(xiàn)Android和PC端的雙向通信

首先我們先來簡單了解一下UDP協(xié)議。它和TCP協(xié)議都是網絡通信中非常重要的傳輸協(xié)議。不同于TCP協(xié)議的端到端服務,它是面向非連接的,屬不可靠協(xié)議,實際上,UDP實現(xiàn)了兩個功能,并有它自己的優(yōu)勢:

  • 在IP協(xié)議的基礎上添加了端口
  • 可以檢測傳輸過程中可能產生的錯誤數(shù)據(jù),拋棄那些已經損壞的數(shù)據(jù)
  • 無需建立連接,傳輸速度快
  • 在多播和廣播時只能用UDP協(xié)議

當然UDP也有一定的不足之處:

  • 無連接,傳輸不可靠
  • 無連接,一旦一方數(shù)據(jù)報丟失,另一方將陷入無限等待(這時可以設置一個超時來解決)

接下來切入正題,在Java中UDP的實現(xiàn)分為兩個類:DatagramPacket和DatagramSocket。DatagramPacket類將數(shù)據(jù)字節(jié)填充到UDP包中,這就是數(shù)據(jù)報;DatagramSocket來發(fā)送這個包,要接收數(shù)據(jù)。到底java如何一步步實現(xiàn)UDP通信呢?

  • 首先創(chuàng)建一個DatagramSocket實例,指定本地端口號,并可以有選擇地指定本地地址,此時,服務器已經準備好從任何客戶端接收數(shù)據(jù)報文;
  • 使用DatagramSocket實例的receive()方法接收一個DatagramPacket實例,當receive()方法返回時,數(shù)據(jù)報文就包含了客戶端的地址,這樣就知道了回復信息應該發(fā)送到什么地方;
  • 使用DatagramSocket實例的send()方法向服務器端返回DatagramPacket實例。

了解了UDP通信的基本原理之后,下面分別從Android端作為發(fā)送方,PC端作為接收方和Android端作為接收方,PC端作為發(fā)送方來具體說明實現(xiàn)思路和過程。

無論是發(fā)送方還是接收方,消息的發(fā)送和接收都要放在單獨的線程中執(zhí)行,以免出現(xiàn)消息阻塞。需要特別注意的是,Android端作為接收方要考慮Android的UI主線程和子線程,也就是說,UI主線程里不能有任何延時的操作。所以Android端接收到的消息要放進消息隊列中,用handle消息處理機制,在子線程中接收消息并處理。

Android端:
public class MainActivity extends AppCompatActivity {

    private EditText etmessage;
    private TextView showmessage;
    private String message;


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btnsend = this.findViewById(R.id.btnSend);
        etmessage = this.findViewById(R.id.etMessage);
        showmessage = this.findViewById(R.id.showMessage);

        Log.v("shoudao", "msg");

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        receiveMsg();
                    } catch (IOException e) {

                    }
                }
            }
        }).start();

        btnsend.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String s = etmessage.getText().toString();
                            sendMsg(s);
                            Log.v("client send", s);
                        } catch (IOException e) {

                        }
                    }
                }).start();

            }

        });
    }

    public void sendMsg(String msg) throws IOException {

        Log.v("client send", msg);

        DatagramSocket socket = new DatagramSocket(0);
        InetAddress host = InetAddress.getByName("192.168.43.171");
        byte[] data = msg.getBytes();
        DatagramPacket request = new DatagramPacket(data, data.length, host, 9988);
        socket.send(request);

    }

    public void receiveMsg() throws IOException {

        Log.v("Android recieve", "start..");

        DatagramSocket socket = new DatagramSocket(6666);
        Log.v("Android port", "start..");
        byte[] container = new byte[10];
        DatagramPacket request = new DatagramPacket(container, 10);
        Log.v("Android port", "start..");

        socket.receive(request);
        message = new String(container);

        Message msg = handle.obtainMessage();
        msg.obj = message;
        handle.sendMessage(msg);

        Log.v("shou dao xiaoxi", message);

    }

     private Handler handle=new Handler(){
        public void handleMessage(Message msg){

            String s=(String)msg.obj;
            showmessage.setText(s);
         }
    };

}

PC端:
public class UDPServer {
    public static void main(String[] args) throws IOException {
        System.out.println("UDP recive server start...");

       DatagramSocket socket=new DatagramSocket(9988);
       byte[]container=new byte[10];

       DatagramPacket request=new DatagramPacket(container,10);

        System.out.println("go");
        SendMsg send=new SendMsg();
        send.start();
        System.out.println("go1");

        while(true) {
            socket.receive(request);
            String address=request.getAddress().toString();
            String s = new String(container);
            System.out.println("shou dao xiaoxi" + s+"from"+address);
        }

    }

public class SendMsg extends Thread {

    @Override
    public void run(){
        String message= null;
        System.out.println("go2");
        try {
            message = sendMsg();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("fasong:"+message);
    }

    public String sendMsg() throws IOException {

        String s="run";
        DatagramSocket socket = new DatagramSocket(0);

        InetAddress host = InetAddress.getByName("192.168.43.1");

        byte[] data = s.getBytes();
        DatagramPacket request = new DatagramPacket(data, data.length, host, 6666);

        socket.send(request);
        return s;

    }
}

UDP程序在receive()方法處阻塞,直到收到一個數(shù)據(jù)報文或等待超時,由于UDP協(xié)議是不可靠協(xié)議,如果沒有收到DatagramPacket,那么程序將會一直阻塞在receive()方法處,這樣客戶端將永遠都接收不到服務器端發(fā)送回來的數(shù)據(jù),但是又沒有任何提示。為了避免這個問題,除了放在線程中解決之外,我們也可以在客戶端使用DatagramSocket類的setSoTimeout()方法來制定receive()方法的最長阻塞時間,并指定重發(fā)數(shù)據(jù)報的次數(shù),如果每次阻塞都超時,并且重發(fā)次數(shù)達到了設置的上限,則關閉客戶端。

這樣就基本實現(xiàn)了基于UDP的Android端與PC端的簡單通信,與君共勉。

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

相關閱讀更多精彩內容

  • 計算機網絡概述 網絡編程的實質就是兩個(或多個)設備(例如計算機)之間的數(shù)據(jù)傳輸。 按照計算機網絡的定義,通過一定...
    蛋炒飯_By閱讀 1,366評論 0 10
  • 一、簡歷準備 1、個人技能 (1)自定義控件、UI設計、常用動畫特效 自定義控件 ①為什么要自定義控件? Andr...
    lucas777閱讀 5,387評論 2 54
  • 在 Android 上,一個完整的 UDP 通信模塊應該是怎樣的? 本文原創(chuàng),轉載請注明出處。歡迎關注我的 簡書 ...
    MeloDev閱讀 19,653評論 6 36
  • 1 網絡編程----UDPNo25 【Scanner scanner =new Scanner(System....
    征程_Journey閱讀 502評論 0 0
  • 多思不若養(yǎng)志,多言不若守靜,多才不若蓄德。低調的人,舉千鈞若扛一羽,擁萬物若攜微毫,懷天下若捧一芥。思無邪,意無狂...
    一秒種記憶閱讀 107評論 1 1

友情鏈接更多精彩內容