安卓间的UDP通信简述
之前在学习《计算机网络》时有接触过UDP 的概念,但由于当时只是接触了点概念上的东西,所以理解得还不够透彻,恰好现在公司有项目需要用到这方面的技术,自己在学习之后,顺便来个小小的总结。
什么是UDP通信?它与TCP有什么区别呢?
书本上是这样介绍的:
TCP:传输控制协议,是一种提供可靠数据传输的通用协议。
UDP:用户数据报协议,是一个面向无连接的协议。采用该协议不需要两个应用程序先建立连接。UDP协议不提供差错恢复,不能提供数据重传,因此该协议传输数据安全性差。
简单点总结就是:小结TCP与UDP的区别:
1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
下面是我写的关于一个安卓内UDP通信的实例,发送方和接收方都为于同一个程序内,目录结构为:
下面是服务器接收方代码:
package com.hanson.testudp;
import android.util.Log;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* Created by ZSN on 2015/12/8.
*/
public class UDPServer implements Runnable {
private static final int PORT = 6000;
private byte[] msg = new byte[1024];
private boolean life = true;
public UDPServer() {
}
@Override
public void run() {
DatagramSocket dSocket = null;
DatagramPacket dPacket = new DatagramPacket(msg, msg.length);
try {
dSocket = new DatagramSocket(PORT);
while (life) {
try {
dSocket.receive(dPacket);//接收数据报
String s=new String(dPacket.getData(),"UTF-8");
Log.i("msg sever received",s.trim());//getData()它从实例中取得报文的byte数组编码。
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}
}
发送方代码为:
package com.hanson.testudp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UDPClient {
private static final int SERVER_PORT = 6000;
private DatagramSocket dSocket = null;
private String msg;
/**
* @param msg
*/
public UDPClient(String msg) {
super();
this.msg = msg;
}
/**
* 发送信息到服务器
*/
public String send() {
StringBuilder sb = new StringBuilder();
InetAddress local;//InetAddress是Java对IP地址的封装
local = null;
try {
local = InetAddress.getByName("localhost"); // 本机测试,参数为IP地址,需要添加异常处理
sb.append("已找到服务器,连接中...").append("/n");
} catch (UnknownHostException e) {
sb.append("未找到服务器.").append("/n");
e.printStackTrace();
}
try {
dSocket = new DatagramSocket(); /* 注意此处要先在配置文件里设置权限,否则会抛权限不足的异常,
DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port):创建实例,并固定监听Port端口的报文。*/
sb.append("正在连接服务器...").append("/n");
} catch (SocketException e) {
e.printStackTrace();
sb.append("服务器连接失败.").append("/n");
}
int msg_len = msg == null ? 0 : msg.length();
DatagramPacket dPacket = new DatagramPacket(msg.getBytes(), msg_len,
local, SERVER_PORT);//用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。
try {
dSocket.send(dPacket);//发送数据报
sb.append("消息发送成功!").append("/n");
} catch (IOException e) {
e.printStackTrace();
sb.append("消息发送失败.").append("/n");
}
dSocket.close();
return sb.toString();
}
}
布局文件代码为:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.hanson.testudp.MainActivity">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/editText"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="53dp"
android:layout_marginStart="53dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送"
android:id="@+id/button"
android:layout_below="@+id/editText"
android:layout_alignLeft="@+id/editText"
android:layout_alignStart="@+id/editText" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:id="@+id/textView"
android:layout_below="@+id/button"
android:layout_alignLeft="@+id/button"
android:layout_alignStart="@+id/button" />
</RelativeLayout>
MainActivity为:
package com.hanson.testudp;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
EditText msg_et = null;
Button send_bt = null;
TextView info_tv = null;
String msg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findview();
ExecutorService exec = Executors.newCachedThreadPool();
UDPServer server = new UDPServer();
exec.execute(server);
send_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
msg=msg_et.getText().toString();
MyTask task=new MyTask();
task.execute(100);
}
});
}
private void findview() {
msg_et = (EditText) findViewById(R.id.editText);
send_bt = (Button) findViewById(R.id.button);
info_tv = (TextView) findViewById(R.id.textView);
}
class MyTask extends AsyncTask<Integer, Integer, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(Integer... params) {
UDPClient client = new UDPClient(msg);
String result=client.send();
return result;
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
}
@Override
protected void onPostExecute(String result) {
info_tv.setText(result);
super.onPostExecute(result);
}
}
}
记得网络请求不能放在主线程中进行,会阻塞主线程,之前我就犯了这个错误,后面加了一个AsyncTask去发送信息。
整个流程是这样子的:在编辑框输入想要发送的内容,点击发送,监听回调方法里先获取编辑框的内容,
然后开启异步任务,再通过doInBacground方法去发送消息,
最后在onPostExecute方法将接收方回调的内容显示在TestView中!接收方如果成功接收到消息,将其内容通过Log打出。
还有最后很关键的一点,记得加上网络权限: