Android网络功能开发(7)——UDP协议通信

UDP通信的流程中,接收端先监听某个端口,等待接收其它设备发来的数据包。发送端准备好数据包后,发送给接收端。接收端只需一个接收线程就可接收多个客户端发来的数据包。一个应用可以同时包含发送端和接收端。

这里通过一个例子介绍如何代码如何编写。这个例子是一个Android应用和一个JavaSE程序通过UDP通信。双方都同时实现接收端和发送端,双方可以像聊天一样随意地互相发送消息。

先看Android端接收消息的的代码。

当点击UDP的Listen按钮时,启动接收线程。在线程中先以端口号为参数创建DatagramSocket对象,再创建一个空的DatagramPacket对象用于存放收到的数据,接着再循环中调用DatagramSocket对象的receive方法。一旦接收到数据,receive方法就会返回,并且把数据保存在packet对象中。通过packet对象的getData方法,取出数据,然后构造出一个消息的字符串。最后用sendMessage方法把这个消息字符串传递给主线程显示到界面上。这是处理接收消息的代码。

Android端发送消息的代码是这样的。

先从Server文本框读取服务器的IP地址,从Content文本框读取消息内容。然后用他们创建一个DatagramPacket对象,也就是要发送的数据包。再创建一个DatagramSocket对象,调用它的send方法把数据包发出去。这些代码涉及网络操作,要在AsyncTask中异步执行。

再看JavaSE端的代码。JavaSE端的接收消息的代码都在接收线程中,主要流程是把接收到的消息输出。

接收数据的代码和Android端是一样的,都是创建一个DatagramSocket对象和一个空的DatagramPacket对象,调用DatagramSocket对象的receive方法,接收到的消息就会保存在DatagramPacket对象中。

JavaSE端的发送消息的代码在主线程中,主要流程是把键盘接收到的消息发送到Android端。

发送数据的代码和Android端是一样的,也是创建一个DatagramSocket对象,把要发送的数据保存到一个DatagramPacket对象中,然后调用DatagramSocket对象的send方法完成发送。

使用Socket接口时,经常需要进行字符转换。程序内部保存文本是字符串(String)形式,而网络传输过程中是字节流(byte[])形式,所以在发送和接收处需要进行转换。发送方需要将字符串或字符流转换为字节流,也就是String 到 byte[] 或 字符输出流到字节输出流( Writer => OutputStream)。接收方需要将字节流转换为字符串或字符流,也就是byte[] 到 String 或 字节输入流到字符输入流(InputStream => Reader)。因为通信是双向的,所以一般有四处需要转换。这四处都需要指出字符编码方式,如最常用的”utf-8”,否则容易出现中文乱码问题。

用模拟器测试UDP通信需要进行端口映射,因为模拟器和PC的IP地址相同,所有网络数据默认都会发送到PC的端口。通过端口映射,才能将发送到PC端口的数据转到模拟器端口,具体方法是:

1、用telnet连接模拟器控制台,命令是:telnet localhost 5554。(5554是模拟器的端口号,一般缺省是5554)。如果windows的telnet命令没打开,需要在控制面板-程序-启用或关闭Windows功能中打开。

2、授权,需要先执行auth <auth_token>命令,才能执行后面的端口映射命令,防止别人远程控制模拟器。<auth_token>是在C:\Users\<当前用户>\目录下的.emulator_console_auth_token文件中的内容,可以用windows记事本打开,复制到命令行窗口。

3、执行端口映射,命令是:redir add udp:8888:8888。意思是把PC8888端口接收到的UDP数据转到模拟器8888端口。

端口映射完成后,就可以测试了。如果测试过程中模拟器仍然无法收到UDP包,那么就只能用真机测试了。

Android端的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="Server:"
        android:textAppearance="?android:attr/textAppearanceLarge"
        app:layout_constraintEnd_toStartOf="@+id/editTextServer"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editTextServer"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        app:layout_constraintBaseline_toBaselineOf="@+id/textView1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView1">

        <requestFocus
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </EditText>

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:text="TCP: "
        android:textAppearance="?android:attr/textAppearanceLarge"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/buttonTCPConnect" />

    <Button
        android:id="@+id/buttonTCPConnect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Connect"
        android:textAllCaps="false"
        app:layout_constraintBaseline_toBaselineOf="@+id/buttonTCPSend"
        app:layout_constraintStart_toEndOf="@+id/textView2" />

    <Button
        android:id="@+id/buttonTCPSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:text="Send"
        android:textAllCaps="false"
        app:layout_constraintStart_toStartOf="@+id/buttonUDPSend"
        app:layout_constraintTop_toBottomOf="@+id/editTextServer" />

    <Button
        android:id="@+id/buttonTCPClose"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Close"
        android:textAllCaps="false"
        app:layout_constraintBaseline_toBaselineOf="@+id/buttonTCPSend"
        app:layout_constraintStart_toEndOf="@+id/buttonTCPSend" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:text="UDP:"
        android:textAppearance="?android:attr/textAppearanceLarge"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/buttonUDPListen" />

    <Button
        android:id="@+id/buttonUDPListen"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Listen"
        android:textAllCaps="false"
        app:layout_constraintBaseline_toBaselineOf="@+id/buttonUDPSend"
        app:layout_constraintStart_toEndOf="@+id/textView3" />

    <Button
        android:id="@+id/buttonUDPSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send"
        android:textAllCaps="false"
        app:layout_constraintStart_toEndOf="@+id/buttonUDPListen"
        app:layout_constraintTop_toBottomOf="@+id/buttonTCPConnect" />

    <Button
        android:id="@+id/buttonUDPStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop"
        android:textAllCaps="false"
        app:layout_constraintBaseline_toBaselineOf="@+id/buttonUDPSend"
        app:layout_constraintStart_toEndOf="@+id/buttonUDPSend" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Content:"
        android:textAppearance="?android:attr/textAppearanceLarge"
        app:layout_constraintBaseline_toBaselineOf="@+id/editTextContent"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/editTextContent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView4"
        app:layout_constraintTop_toBottomOf="@+id/buttonUDPListen" />

    <TextView
        android:id="@+id/textViewResult"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextContent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Android端的完整代码如下:

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class MainActivity extends AppCompatActivity {
    EditText etServer;
    EditText etContent;
    TextView tvResult;

    Handler mHandler;

    UDPReceiveThread udpReceiveThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etServer = findViewById(R.id.editTextServer);
        etContent = findViewById(R.id.editTextContent);
        tvResult = findViewById(R.id.textViewResult);

        etServer.setText("192.168.1.2");

        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                switch(msg.what) {
                    case 1:	// receive socket message
                        String received = (String)msg.obj;
                        tvResult.append(received+"\r\n");
                        break;
                }
            }
        };

        Button btnUDPListen = (Button) findViewById(R.id.buttonUDPListen);
        btnUDPListen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                udpListen();
            }
        });

        Button btnUDPSend = (Button) findViewById(R.id.buttonUDPSend);
        btnUDPSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                String serverIP = etServer.getText().toString();
                String content = "Android: " + etContent.getText().toString();
                new AsyncTask<String, Void, Void>(){
                    @Override
                    protected Void doInBackground(String... arg0) {
                        try {
                            udpSend(arg0[0], arg0[1]);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        return null;
                    }
                }.execute(serverIP, content);
            }
        });

        Button btnUDPStop = (Button) findViewById(R.id.buttonUDPStop);
        btnUDPStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                udpStop();
            }
        });
    }

    void udpListen() {
        udpReceiveThread = new UDPReceiveThread();
        udpReceiveThread.start();
    }

    void udpSend(String ip, String ctx) throws IOException {
        //Log.i("zzk", "udpSend: " + ip + ", " + ctx);
        InetAddress address = InetAddress.getByName(ip);
        byte[] buf = ctx.getBytes("UTF-8");
        DatagramPacket recPacket = new DatagramPacket(buf, buf.length, address, 9999);
        DatagramSocket socket = new DatagramSocket();
        socket.send(recPacket);
        socket.close();
    }

    void udpStop() {
        if(udpReceiveThread!=null) udpReceiveThread.setStopFlag();
    }

    void sendMessage(String str){
        Message msg = Message.obtain();	// 从Message池中取Message对象,用new创建会用到内存分配,影响效率
        msg.what = 1;
        msg.obj = str;
        mHandler.sendMessage(msg);
    }

    class UDPReceiveThread extends Thread {
        private boolean flag;
        public void setStopFlag(){
            flag = false;
        }

        @Override
        public void run(){
            flag = true;
            try {
                DatagramSocket socket = new DatagramSocket(8888);
                socket.setSoTimeout(1000);
                byte[] buf = new byte[256];
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                sendMessage("UDP listen started");
                while(flag){
                    try {
                        socket.receive(packet);
                        String s = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
                        sendMessage(s);
                    } catch(SocketTimeoutException e){}	// 为了能结束线程,每秒钟退出receive一次,检查flag
                }
                socket.close();
                sendMessage("UDP listen stoped");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

完整代码下载:

Android网络功能开发-Socket编程接口使用的例子 

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
当使用UDP协议进行网络通信时,需要使用`DatagramSocket`和`DatagramPacket`两个类来实现。下面是一个Java使用UDP协议进行网络通信的示例代码: 服务器端代码: ``` import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPServer { public static void main(String[] args) throws Exception { DatagramSocket serverSocket = new DatagramSocket(9876); // 创建UDP socket并绑定端口号 byte[] receiveData = new byte[1024]; byte[] sendData = new byte[1024]; while (true) { DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); serverSocket.receive(receivePacket); // 接收客户端请求 String sentence = new String(receivePacket.getData()); InetAddress IPAddress = receivePacket.getAddress(); int port = receivePacket.getPort(); String capitalizedSentence = sentence.toUpperCase(); sendData = capitalizedSentence.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port); serverSocket.send(sendPacket); // 发送响应给客户端 } } } ``` 客户端代码: ``` import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPClient { public static void main(String[] args) throws Exception { DatagramSocket clientSocket = new DatagramSocket(); InetAddress IPAddress = InetAddress.getByName("localhost"); int port = 9876; byte[] sendData = new byte[1024]; byte[] receiveData = new byte[1024]; String sentence = "hello server"; sendData = sentence.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port); clientSocket.send(sendPacket); // 发送请求给服务器 DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); clientSocket.receive(receivePacket); // 接收服务器响应 String modifiedSentence = new String(receivePacket.getData()); System.out.println("FROM SERVER:" + modifiedSentence); clientSocket.close(); } } ``` 这个示例代码中,客户端向服务器发送一个字符串请求,服务器收到请求后将字符串转为大写并返回给客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nanoage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值