Android UDP 通信总结 (终于从坑中爬起来了)

2018.08.02 

最近做的项目要求为:一端为电视端,一端为手机端,实现电视端与手机端通过UDP进行通信。

解释:就是两个机器在同一个局域网内(即两端连接的同一个WiFi),可以通过UDP,一端将信息通过局域网散播出去后,在同一个局域网内的所有设备都能接收到信息,只不过,另一端对接收到的信息进行处理,这样就完成了UDP通信。发送信息方只负责发送信息,另一方只负责接收信息,两方还可以互相发送和接收信息,反正互不干扰,谁都不会管对方是否接收到信息。

 写的比较好的文章:https://blog.csdn.net/ITermeng/article/details/73482669?locationNum=9&fps=1。

想要了解更多关于UDP与TCP的基础知识,请看https://www.jianshu.com/p/cc62e070a6d2。

UDP通信步骤:

1、建立连接的IP地址和端口号,IP即是你想要发送的地址,端口号则要选用闲置端口就是向8000~9000这样的端口号。

2、建立DatagramSocket  sendSocket/receiveSocket ,用于发送和接受数据报文包。

3、创建报文包DatagramPacket sendPackage/receivePackage,将发送或接收的内容打包。

4、通过sendSocket.send(sendPackage)或者receiveSocket.receive(receivePackage),则完成了基于Socket的UDP通信。

注意事项:

1.同一端如果既要发送消息又想接收消息,一定要将发送的socket和接收的socket分开。不能用一个socket既接收信息,又发送信息,这样一定会造成阻塞。

2.接收线程中,一定要有  while(true){     if(标志何时结束)   代码块   }  这样的方式,不然,在接收信息时,一定会造成阻塞,就是只能接收到一次信息。

3.本来想开一个线程进行接收数据和发送数据,真的是太混乱了,所以大家还是开两个线程吧。。。。

具体代码:

服务端:

package com.example.admin.learnUdpCommunicateServer;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;


/**
 * @author zhou.jn
 */
public class UdpSocketServerActivity extends Activity {
    private static final String TAG = "SocketAutoConnectServer";
    private static String IP;
    private static int BROADCAST_PORT = 9999;
    private static String BROADCAST_IP = "255.255.255.255";
    private InetAddress inetAddress = null;
    private BroadcastThread broadcastThread;
    private DatagramSocket sendSocket = null;
    private DatagramSocket receiveSocket = null;
    private Button sendUDPBrocast;
    private volatile boolean isRuning = true;
    private TextView ipInfo;
    private Button btn_send;
    private EditText et_sendInfo;
    private String sendContent;
    private TextView tv_receive;
    private List ipList = new ArrayList<>();
    private Button btnClear;
    private ReceiveThread receiveThread;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initView();
        initIp();
        initThread();
        try {
            inetAddress = InetAddress.getByName(BROADCAST_IP);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initThread() {
        broadcastThread = new BroadcastThread();
        broadcastThread.start();
        receiveThread = new ReceiveThread();
        receiveThread.start();
    }

    private void initIp() {
        //Wifi状态判断
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        if (wifiManager.isWifiEnabled()) {
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            IP = getIpString(wifiInfo.getIpAddress());
            ipInfo.append(IP);
            System.out.println("IP IP:" + IP);
        }
    }

    private void initView() {
        ipInfo = (TextView) findViewById(R.id.ip_info);
        sendUDPBrocast = (Button) findViewById(R.id.sendUDPBrocast);
        tv_receive = findViewById(R.id.tv_receive);
        et_sendInfo = findViewById(R.id.et_sendContent);
        btn_send = findViewById(R.id.btn_sendInfo);
        try {
            receiveSocket = new DatagramSocket(BROADCAST_PORT);
        } catch (SocketException e) {
            e.printStackTrace();
        }

        btn_send.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                sendMessageToThread(broadcastThread.mhandler);
            }
        });
        btnClear = findViewById(R.id.btnClear);
        btnClear.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                tv_receive.setText("");
            }
        });
        sendUDPBrocast.setOnClickListener(new SendUDPBrocastListener());
    }

    private void sendMessageToThread(Handler mhandler) {
        Message msg = Message.obtain();
        sendContent = et_sendInfo.getText().toString();
        msg.obj = sendContent;
        msg.what = 1;
        mhandler.sendMessage(msg);
    }


    /**
     * 将获取到的int型ip转成string类型
     */
    private String getIpString(int i) {
        return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "."
                + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF);
    }

    @SuppressLint("HandlerLeak")
    Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1: {
                    if (!msg.obj.equals(IP)) {
                        if (!isExistIp(msg.obj.toString())) {
                            ipList.add(msg.obj.toString());
                        }
                        tv_receive.append(msg.obj.toString() + " 接收到信息 " + "\n");
                    }
                }
                break;
                default:
                    break;
            }
        }

    };

    public class SendUDPBrocastListener implements OnClickListener {
        @Override
        public void onClick(View v) {
            if (isRuning) {
                isRuning = false;
                sendUDPBrocast.setText("发送广播");
                System.out.println("现在停止广播..");
            } else {
                isRuning = true;
                sendUDPBrocast.setText("停止广播");
                System.out.println("现在发送广播..");
            }
        }
    }

    public class BroadcastThread extends Thread {
        private Handler mhandler = null;

        @SuppressLint("HandlerLeak")
        @Override
        public void run() {
            Looper.prepare();
            mhandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    String message = (String) msg.obj;
                    byte[] data = message.getBytes();
                    DatagramPacket dpSend = null;
                    dpSend = new DatagramPacket(data, data.length, inetAddress, BROADCAST_PORT);
                    try {
                        double start = System.currentTimeMillis();
                        for (int i = 0 ; i < 15; i ++) {
                            sendSocket = new DatagramSocket();
                            sendSocket.send(dpSend);
                            sendSocket.close();
                            Thread.sleep(80);
                            Log.i(TAG, "sendMessage: data " + new String(data));
                        }
                        double end = System.currentTimeMillis();
                        double times = end - start;
                        Log.i(TAG, "receive: executed time is : "+ times +"ms");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            Looper.loop();
        }
    }

    private boolean isExistIp(String revIp) {
        if (ipList != null && ipList.size() > 0) {
            for (String ip : ipList) {
                if (ip != revIp) {
                    return false;
                }
            }
        }
        return false;
    }

    private class ReceiveThread extends Thread {
        @Override
        public void run() {
            while (true) {
                if (isRuning) {
                    byte[] receiveData = new byte[1024];
                    DatagramPacket dpReceive = null;
                    ipList.clear();
                    dpReceive = new DatagramPacket(receiveData, receiveData.length);
                    try {
                        receiveSocket.receive(dpReceive);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    String recIp = dpReceive.getAddress().toString().substring(1);
                    if (dpReceive != null) {
                        Message revMessage = Message.obtain();
                        revMessage.what = 1;
                        revMessage.obj = recIp;
                        Log.i(TAG, "handleMessage: receive ip" + recIp);
                        myHandler.sendMessage(revMessage);
                    }
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isRuning = false;
        receiveSocket.close();
        System.out.println("UDP Server程序退出,关掉socket,停止广播");
        finish();
    }
}

客户端代码:

package com.example.admin.learnudpcommunicateclent;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


import com.example.admin.udpclient.R;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhou.jn
 */
public class SocketClientActivity extends Activity {
    private static int BROADCAST_PORT = 9999;
    private static final String TAG = "SocketConnectActivity";
    private TextView ipInfo;
    private static String IP;
    private boolean isRunning = true;
    private Button btnBack;
    private DatagramSocket receiveSocket = null;
    private DatagramSocket sendSocket = null;
    private DatagramPacket dpReceive = null;
    private SendThread sendThread;
    private String previousContent = new String();
    private Button btnClear;
    private String receiveIp;
    private String sendIp;
    @SuppressLint("HandlerLeak")
    Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    Log.i(TAG, "run handleMessage: ");
                    ipInfo.append(msg.obj.toString() + "\n");
                    /**通过确认该客户端已经接收到信息后,再将自己的ip号码发送出去*/
                    new Thread(){
                        @Override
                        public void run() {
                            try {
                                Thread.sleep(1500);
                                /**开一个将前一次接收的内容置为空的线程,解决发送端两次发送相同的信息,接收端他可能就不会做处理的问题*/
                                previousContent = " ";
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }.start();
                    sendFeedBackToServer(sendThread.mHandler, msg.obj.toString());
                    break;
                case 2:
                    ipInfo.append(msg.obj.toString());
                default:
                    break;
            }
        }
    };


    private void sendFeedBackToServer(Handler mHandler, String ip) {
        Message msg = new Message();
        msg.obj = ip;
        msg.what = 1;
        mHandler.sendMessage(msg);
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ipInfo = findViewById(R.id.ip_info);
        btnBack = findViewById(R.id.btnBack);
        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        btnClear = findViewById(R.id.btnClear);
        btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ipInfo.setText("");
            }
        });
        ReceiveThread receiveThread = new ReceiveThread();
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        if (wifiManager.isWifiEnabled()) {
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            IP = getIpString(wifiInfo.getIpAddress());
            System.out.println("本机IP is :" + IP);
        }
        try {
            receiveSocket = new DatagramSocket(BROADCAST_PORT);
            sendThread = new SendThread();
            sendThread.start();
            receiveThread.start();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 将获取到的int型ip转成string类型
     */
    private String getIpString(int i) {
        return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "."
                + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF);
    }

    private class ReceiveThread extends Thread {
        @SuppressLint("LongLogTag")
        @Override
        public void run() {
            while (true) {
                if (isRunning) {
                    String receiveContent = null;
                    byte[] buf = new byte[1024];
                    dpReceive = new DatagramPacket(buf, buf.length);
                    try {
                        receiveSocket.receive(dpReceive);
                        receiveContent = new String(buf, 0, dpReceive.getLength());
                        Log.i(TAG, "run: receive message " + receiveContent);
                        Log.i(TAG, "run: " + dpReceive.getAddress().toString());
                        receiveIp = dpReceive.getAddress().toString().substring(1);
                        if (receiveIp != IP) {
                            sendIp = receiveIp;
                        }
                        Log.i(TAG, "run:1 previousContent " + previousContent + " receiveContent " + receiveContent);
                        if (!previousContent.equals(receiveContent) && !IP.equals(receiveIp)) {
                            Message msg = myHandler.obtainMessage();
                            msg.obj = "服务器:" + sendIp + "发送的内容为:" + receiveContent;
                            msg.what = 1;
                            myHandler.sendMessage(msg);
                        }
                        previousContent = receiveContent;
                        Log.i(TAG, "run:2 previousContent " + previousContent + " receiveContent " + receiveContent);

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private class SendThread extends Thread {
        private Handler mHandler;

        @SuppressLint("HandlerLeak")
        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    DatagramPacket dpSend = null;
                    byte[] ip = IP.getBytes();
                    try {
                        if (receiveIp != IP) {
                            InetAddress inetAddress = InetAddress.getByName(sendIp);
                            dpSend = new DatagramPacket(ip, ip.length, inetAddress, BROADCAST_PORT);
                        }
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    try {
                        String sendData = new String(ip, 0, dpSend.getLength());
                        sendSocket = new DatagramSocket();
                        sendSocket.send(dpSend);
                        Log.i(TAG, "run: send message : " + sendData);
                        sendSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            Looper.loop();
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        receiveSocket.close();
        isRunning = false;
        System.out.println("UDP Client程序退出,关掉socket,停止广播");
        finish();
    }
}

刚开始做这个项目的疑惑及解决办法:

问题一:用一个DatagramSocket进行发送并接收信息,会不会有问题。

答:确实很有问题,所以一定要开(1+n)个DatagramSocket啊,同志们!一个DatagramSocket用于接收信息,另外n个是每次发送新消息的时候就新建一个DatagramSocket进行发送,发送后进行关闭。

问题二:当发送信息的时候,是想着每发送一个信息,就要关闭一个信息。可我想着发送完信息关闭后,接着又要发送消息怎么办?

答:每次新建DatagramSocket再进行关闭就可以啦!

问题三:想在客户端接收到信息后,发送给服务端告诉他我接收到信息了~,可是接收方只要监听端口就可以,不用IP的,那要如何获取接收端的IP呢?

答:通过接收数据包的dpReceive.getAddress()可以获取,并且它的格式为“/10.0.12.53”,所以要取子字符串subString(1)进行获取。

问题四:接收信息的时候咋能接收到自己发送的信息?

答:确实可以接收到自己发送的信息。。。。所以需要自行进行过滤处理,不要接收自己发送的信息,不然是不是会显得很傻。。

项目讲解:

服务器端:我怕客户端接收不到信息,所以我就发了15次信息,并且每次间隔80ms,这样客户端再接收不到信息,俺也没办法了,每次发送信息的时间大约为1200多ms,这么长时间总能接到一条吧,老兄!

                 发送端拨号为4个255,听说这样是可以发送给局域网,反正IP是个随意的大数就行,比如244.244.0.1都行~

客 户 端:服务器这老兄给我发这么多条相同的信息,我还每条都处理,是不是很傻!所以就进行判断我是不是重复接收了客户端的相同信息,如果相同,处理一次就够了。。。。

ps:UDP中没什么客户端,服务端,只是接收方,发送方而已,我只是这么称呼他们。。。

源码地址:https://download.csdn.net/download/weixin_37716758/10582295(优化的)

源码地址:https://download.csdn.net/download/weixin_37716758/10580690(带有APK)

 

你可能感兴趣的:(Android,NetWork)