基于UDP实现Android和PC端的双向通信
首先我们先来简单了解一下UDP协议。它和TCP协议都是网络通信中非常重要的传输协议。不同于TCP协议的端到端服务,它是面向非连接的,属不可靠协议,实际上,UDP实现了两个功能,并有它自己的优势:
- 在IP协议的基础上添加了端口
- 可以检测传输过程中可能产生的错误数据,抛弃那些已经损坏的数据
- 无需建立连接,传输速度快
- 在多播和广播时只能用UDP协议
当然UDP也有一定的不足之处:
- 无连接,传输不可靠
- 无连接,一旦一方数据报丢失,另一方将陷入无限等待(这时可以设置一个超时来解决)
接下来切入正题,在Java中UDP的实现分为两个类:DatagramPacket和DatagramSocket。DatagramPacket类将数据字节填充到UDP包中,这就是数据报;DatagramSocket来发送这个包,要接收数据。到底java如何一步步实现UDP通信呢?
- 首先创建一个DatagramSocket实例,指定本地端口号,并可以有选择地指定本地地址,此时,服务器已经准备好从任何客户端接收数据报文;
- 使用DatagramSocket实例的receive()方法接收一个DatagramPacket实例,当receive()方法返回时,数据报文就包含了客户端的地址,这样就知道了回复信息应该发送到什么地方;
- 使用DatagramSocket实例的send()方法向服务器端返回DatagramPacket实例。
了解了UDP通信的基本原理之后,下面分别从Android端作为发送方,PC端作为接收方和Android端作为接收方,PC端作为发送方来具体说明实现思路和过程。
无论是发送方还是接收方,消息的发送和接收都要放在单独的线程中执行,以免出现消息阻塞。需要特别注意的是,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()方法处阻塞,直到收到一个数据报文或等待超时,由于UDP协议是不可靠协议,如果没有收到DatagramPacket,那么程序将会一直阻塞在receive()方法处,这样客户端将永远都接收不到服务器端发送回来的数据,但是又没有任何提示。为了避免这个问题,除了放在线程中解决之外,我们也可以在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。
这样就基本实现了基于UDP的Android端与PC端的简单通信,与君共勉。