美文网首页
UDP 通信简单测试记录

UDP 通信简单测试记录

作者: 梧叶已秋声 | 来源:发表于2020-07-27 09:31 被阅读0次

    现有2台手机,小米和魅族。魅族分享热点给小米用,魅族做服务端,小米做客户端。

    一开始我以为要获取2台手机的ip地址,但是后来发现不用。
    ip地址获取是通过DhcpInfo去获取的。ipAddress代表本机ip地址,gateway代表网关地址,即本机设备所连接的路由器ip地址。

            WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE);
            DhcpInfo info = manager.getDhcpInfo();
            Log.d(TAG,"gateway " + formatIP(info.gateway));
            Log.d(TAG,"ipAddress " + formatIP(info.ipAddress));
    
        private String formatIP(int ip) {
            return String.format(
                    "%d.%d.%d.%d",
                    (ip & 0xff),
                    (ip >> 8 & 0xff),
                    (ip >> 16 & 0xff),
                    (ip >> 24 & 0xff)
            );
        }
    

    并且要给权限。

        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
        <uses-permission android:name="android.permission.INTERNET"/>
    
    魅族ip地址为192.168.43.1
    小米ip地址为192.168.43.213
    

    由于小米手机连接的是魅族手机分享的热点,因此小米手机连接网络的路由器地址就是192.168.43.1

    要在2台手机上进行UDP通讯的话,,由于魅族做服务端,因此需要192.168.43.1这个地址。

        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    

    Android默认热点分享的ip是192.168.43.1,源码里写死了的,所以这里可以我直接写死而不是通过动态获取赋值。在UDP通讯过程中,只需要一个ip地址,那就是192.168.43.1

    下面新建2个工程,serviceclient。注意通讯端口要保持一致,这里我都选择了3000
    service代码如下
    首先加权限:

        <uses-permission android:name="android.permission.INTERNET" />
    
    
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        private static final int SERVER_PORT = 3000;
        private byte bufServer[] = new byte[1024];
        private static final int BUF_LENGTH = 1024;
        private boolean mIsRunning;
    
        private DatagramSocket mDatagramSocket;
        private DatagramPacket mDatagramPacket;
        private TextView mTextView;
        private Thread mThread;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
            createServer();
        }
    
        private void createServer() {
            mIsRunning = true;
            try {
                mDatagramSocket = new DatagramSocket(SERVER_PORT);
                mDatagramPacket = new DatagramPacket(bufServer, BUF_LENGTH);
                startServerThread();
            } catch (SocketException e) {
                e.printStackTrace();
            }
        }
    
        private void startServerThread() {
            mThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        while (mIsRunning) {
                            mDatagramSocket.receive(mDatagramPacket);
                            final String result = new String(mDatagramPacket.getData(), mDatagramPacket.getOffset(), mDatagramPacket.getLength());
                            Log.d(TAG,"result " + result);
    
                            // 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。
                            if (mDatagramPacket != null) {
                                mDatagramPacket.setLength(BUF_LENGTH);
                            }
    
                            mTextView.post(new Runnable() {
                                @Override
                                public void run() {
                                    mTextView.setText(result);
                                }
                            });
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            mThread.start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mIsRunning = false;
            mDatagramSocket.close();
        }
    }
    

    client代码如下。
    首先,加权限

        <uses-permission android:name="android.permission.INTERNET" />
    
    public class HeartbeatTimer {
    
        private Timer timer;
        private TimerTask task;
        private OnScheduleListener mListener;
    
        public HeartbeatTimer() {
            timer = new Timer();
        }
    
        public void startTimer(long delay, long period) {
            task = new TimerTask() {
                @Override
                public void run() {
                    if (mListener != null) {
                        mListener.onSchedule();
                    }
                }
            };
            timer.schedule(task, delay, period);
        }
    
        public void exit() {
            if (timer != null) {
                timer.cancel();
            }
            if (task != null) {
                task.cancel();
            }
        }
    
        public interface OnScheduleListener {
            void onSchedule();
        }
    
        public void setOnScheduleListener(OnScheduleListener listener) {
            this.mListener = listener;
        }
    }
    
    public class UDPSocket {
        private static final String TAG = "UDPSocket";
        private static final String SERVICE_IP = "192.168.43.1";
    
        // 单个CPU线程池大小
        private static final int POOL_SIZE = 5;
        public static final int CLIENT_PORT = 3000;
    
        private ExecutorService mThreadPool;
    
        private DatagramSocket mDatagramSocket;
    
        private HeartbeatTimer mHeartbeatTimer;
    
        private long lastReceiveTime = 0;
        private static final long TIME_OUT = 120 * 1000;
        private static final long HEARTBEAT_MESSAGE_DURATION = 10 * 1000;
    
        public UDPSocket() {
            int cpuNumbers = Runtime.getRuntime().availableProcessors();
            // 根据CPU数目初始化线程池
            mThreadPool = Executors.newFixedThreadPool(cpuNumbers * POOL_SIZE);
            // 记录创建对象时的时间
            lastReceiveTime = System.currentTimeMillis();
    
        }
    
        public void startUDPSocket() {
            if (mDatagramSocket != null) return;
            try {
                // 表明这个 Socket 在设置的端口上监听数据。
                mDatagramSocket = new DatagramSocket(CLIENT_PORT);
                startHeartbeatTimer();
            } catch (SocketException e) {
                e.printStackTrace();
            }
        }
    
    
        public void stopUDPSocket() {
            if (mDatagramSocket != null) {
                mDatagramSocket.close();
                mDatagramSocket = null;
            }
            if (mHeartbeatTimer != null) {
                mHeartbeatTimer.exit();
            }
        }
    
        /**
         * 启动心跳,timer 间隔十秒
         */
        private void startHeartbeatTimer() {
            mHeartbeatTimer = new HeartbeatTimer();
            mHeartbeatTimer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() {
                @Override
                public void onSchedule() {
                    Log.d(TAG, "timer is onSchedule...");
                    long duration = System.currentTimeMillis() - lastReceiveTime;
                    Log.d(TAG, "duration:" + duration);
                    if (duration > TIME_OUT) {//若超过两分钟都没收到我的心跳包,则认为对方不在线。
                        Log.d(TAG, "超时,对方已经下线");
                        // 刷新时间,重新进入下一个心跳周期
                        lastReceiveTime = System.currentTimeMillis();
                    } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超过十秒他没收到我的心跳包,则重新发一个。
                        String string = "hello,this is a heartbeat message";
                        sendMessage(string);
                    }
                }
    
            });
            mHeartbeatTimer.startTimer(0, 1000 * 10);
        }
        /**
         * 发送心跳包
         *
         * @param message
         */
        public void sendMessage(final String message) {
            mThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        InetAddress targetAddress = InetAddress.getByName(SERVICE_IP);
                        byte[] sendData = createSendData(message);
                        //创建用来发送的 DatagramPacket 数据报,其中应该包含要发送的信息,以及本地地址,目标端口号。
                        DatagramPacket packet = new DatagramPacket(sendData, sendData.length, targetAddress, CLIENT_PORT);
    
                        String result = new String(packet.getData(), 0, packet.getLength());
                        Log.d(TAG,"result " + result);
                        mDatagramSocket.send(packet);
    
                        // 数据发送事件
                        Log.d(TAG, "数据发送成功");
    
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
    
                }
            });
        }
    
        //中文字符发送前要处理,否则会出现乱码,因此这里统一处理
        private byte[] createSendData(String strSend) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dataStream = new DataOutputStream(baos);
            try {
                dataStream.writeUTF(strSend);
                dataStream.close();
                return baos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return new byte[0];
        }
    }
    
    public class MainActivity extends AppCompatActivity {
        private EditText mEditText;
        private Button mButton;
        private UDPSocket mUDPSocket;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mEditText = findViewById(R.id.edit_text);
            mButton = findViewById(R.id.send_button);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mUDPSocket.sendMessage(mEditText.getText().toString());
                }
            });
            mUDPSocket = new UDPSocket();
            mUDPSocket.startUDPSocket();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mUDPSocket.stopUDPSocket();
        }
    }
    

    参考链接:
    Android 网络编程(1)——Socket编程So easy
    Android利用UDP、TCP进行局域网数据传输
    Android基于UDP的局域网聊天通信(有完整Demo)
    在 Android 上,一个完整的 UDP 通信模块应该是怎样的?
    Socket 通信之 UDP 通信
    Android UDP

    相关文章

      网友评论

          本文标题:UDP 通信简单测试记录

          本文链接:https://www.haomeiwen.com/subject/zvhslktx.html