美文网首页
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