mDNS即组播DNS(multicast DNS)。使用5353端口,在内网没有DNS服务器时,就会出现此组播信息。mDNS 基于 UDP 协议。.com/
在一个局域网内,每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,告诉局域网内所有设备自己提供什么服务,服务名称,IP,端口等信息。比如A主机提供了某服务,A开启了mDNS,向局域网内广播发送mDNS消息:我的IP是 192.168.1.101,端口是9091。如果局域网中的B需要该服务,则 B 主机向局域网内广播 mDNS 服务请求,并且最终获得有一个IP地址为 192.168.1.100,端口号是 9091 的主机,也就是 A 主机提供 的服务 服务,所以 B 主机就知道了 A 主机的 IP 地址和端口号了。在我的第一篇文章中发送UDP数据包的IP和端口就是通过此方法获取的。
IPActivity.java:
package com.wifi.main;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.jushi_blub.R;
import com.wifi.connect.WIFIAdmin;
import com.wifi.mdns.Utils;
import com.wifi.utils.MyApplication;
import com.wifi.utils.WifiUtils;
public class IPActivity extends Activity implements OnClickListener{
public static final String TAG = "IPActivity";
private Button StartConnect,scan;
//目的主机ip和端口
private EditText IP,Port;
//本机监听端口
//private EditText LOCAL_PORT;
private TextView destination,porttext;
//目的ip和端口
private String Server_IP;
private String Server_Port;
private Map<String, Object> map;
private MyApplication myApplication;
private WIFIAdmin wifiAdmin;
private WifiManager mWifiManager;
Handler handler = new Handler();
//mdns服务类型
private String type = "_udpserver._udp.local.";
// private String type = "_sleep-proxy._udp.local.";
private JmDNS jmdns = null;
private ServiceListener listener = null;
private ServiceInfo serviceInfo;
//组播锁
private MulticastLock lock;
//展示扫描的mDNS服务
private ListView mlistView;
private Utils utils = new Utils();
//存放扫描的mDNS服务
private List<Map<String, Object>> mdnsList= new ArrayList<Map<String,Object>>();
private Map<String, Object> mdnsMap;
private boolean flag =true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ip);
initViews();
mlistView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long id) {
AlertDialog.Builder alert = new AlertDialog.Builder(IPActivity.this);
Server_IP = mdnsList.get(position).get("Service_IP").toString();
Server_Port = mdnsList.get(position).get("Service_Port").toString();
alert.setTitle(Server_IP);
alert.setMessage("点击获取");
alert.setPositiveButton("获取", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
IP.setText(Server_IP);
Port.setText(Server_Port);
}
});
alert.setNegativeButton("取消", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
//
//mWifiAdmin.removeWifi(mWifiAdmin.getNetworkId());
}
});
alert.create();
alert.show();
}
});
}
//开启查找服务
private void setUp() {
//打开组播
openBroadcast();
try {
//InetAddress ip = InetAddress.getByName("192.168.0.108");
jmdns = JmDNS.create();
//创建mDNS服务
//serviceInfo = ServiceInfo.create(type, "AndroidTest",5353, "plain test service from android");
//jmdns.registerService(serviceInfo);
//监听mDNS服务
jmdns.addServiceListener(type, listener = new ServiceListener() {
@Override
public void serviceResolved(ServiceEvent event) {
// mdnsMap = new HashMap<String, Object>();
//
// mdnsMap.put("Service_IP", ev.getInfo().getHostAddress());
// mdnsMap.put("Service_Port", ev.getInfo().getPort());
// if (!mdnsList.contains(mdnsMap)) {
// mdnsList.add(mdnsMap);
// }
//ServiceInfo serviceInfo = jmdns.getServiceInfo(type, "light udpServer",1);
String addr = "";
if (event.getInfo().getHostAddress() != null && event.getInfo().getInetAddress()!=null) {
addr = event.getInfo().getHostAddress();
}
mdnsMap = new HashMap<String, Object>();
mdnsMap.put("Service_IP", addr);
mdnsMap.put("Service_Port", event.getInfo().getPort());
mdnsMap.put("service_Name", event.getName());
if (!mdnsList.contains(mdnsMap)) {
mdnsList.add(mdnsMap);
}
}
@Override
public void serviceRemoved(ServiceEvent ev) {
}
@Override
public void serviceAdded(ServiceEvent event) {
// Required to force serviceResolved to be called again (after the first search)
jmdns.requestServiceInfo(event.getType(), event.getName(), 1);
}
});
} catch (IOException e) {
e.printStackTrace();
return;
}
}
@Override
protected void onStart() {
super.onStart();
}
/**
* 打开wifi组播服务
*/
public void openBroadcast() {
mWifiManager = (WifiManager) getSystemService(android.content.Context.WIFI_SERVICE);
if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(true);
}
lock =mWifiManager.createMulticastLock(getClass().getSimpleName());
lock.setReferenceCounted(true);
lock.acquire();//to receive multicast packets
}
/**
* ListView的Item显示
* @author Sunward
*
*/
public class MyAdapter extends BaseAdapter{
LayoutInflater inflater;
List<Map<String, Object>> list;
public MyAdapter(Context context, List<Map<String, Object>> list) {
this.inflater = LayoutInflater.from(context);
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
//忽略指定的警告
@SuppressLint({ "ViewHolder", "InflateParams" })
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//针对每一个数据(即每一个List ID)创建一个ListView实例,
View view = null;
view = inflater.inflate(R.layout.wifi_listitem, null);
Map<String, Object> result = list.get(position);
TextView wifi_ssid=(TextView) view.findViewById(R.id.ssid);
ImageView wifi_level=(ImageView) view.findViewById(R.id.wifi_level);
wifi_ssid.setText(result.get("service_Name")+" "+result.get("Service_IP"));
wifi_level.setImageResource(R.drawable.wifi);
//判断信号强度,显示对应的指示图标
return view;
}
}
/**
* 控件初始化
*/
public void initViews(){
//连接
StartConnect = (Button) findViewById(R.id.startCon);
//扫描
scan = (Button) findViewById(R.id.scanService);
IP = (EditText) findViewById(R.id.IPText);
Port = (EditText) findViewById(R.id.PortText);
//LOCAL_PORT = (EditText) findViewById(R.id.LOCAL_PORT);
destination = (TextView) findViewById(R.id.destination);
porttext = (TextView) findViewById(R.id.porttext);
//扫描的mdns服务
mlistView=(ListView) findViewById(R.id.mdns_list);
StartConnect.setOnClickListener(IPActivity.this);
scan.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.wifi_config) {
Intent intent = new Intent();
intent.setClass(IPActivity.this, WIFIActivity.class);
this.startActivity(intent);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* 返回键事件
*/
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.setClass(this, MainActivity.class);
startActivity(intent);
finish();
}
/**
* 按钮点击事件
*/
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.startCon:
myApplication = (MyApplication)this.getApplicationContext();
if (IP.getText().toString().trim().equals("")||
//LOCAL_PORT.getText().toString().trim().equals("")||
Port.getText().toString().trim().equals("")) {
Toast.makeText(IPActivity.this, "请输入完整", Toast.LENGTH_SHORT).show();
}else {
Server_IP = IP.getText().toString();
Server_Port = Port.getText().toString();
Toast.makeText(IPActivity.this,Server_IP+" "+Server_Port, Toast.LENGTH_SHORT).show();
//Application存储全局变量
map = new HashMap<String, Object>();
map.put("IP", Server_IP);
map.put("Port", Server_Port);
map.put("LOCAL_PORT", 8929);
myApplication.setMap(map);
Intent intent = new Intent();
intent.setClass(IPActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
break;
case R.id.scanService:
new Thread(){
@Override
public void run()
{
// Looper.prepare();
setUp();
//加载扫描到的服务
// Looper.loop();
}
}.start();
while(flag){
if (mdnsList.size()>0) {
ListAdapter mAdapter = new MyAdapter(IPActivity.this, mdnsList);
mlistView.setAdapter(mAdapter);
new WifiUtils.Utility().setListViewHeightBasedOnChildren(mlistView);
flag=false;
}
}
break;
default:
break;
}
}
}
布局文件activity_ip.xml:
<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.wifi.main.IPActivity" >
<ListView
android:id="@+id/mdns_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/startCon"
>
</ListView>
<Button
android:id="@+id/scanService"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_below="@+id/IPText"
android:layout_marginTop="80dp"
android:layout_toRightOf="@+id/destination"
android:text="扫描" />
<TextView
android:id="@+id/porttext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_below="@+id/destination"
android:layout_alignLeft="@+id/mdns_list"
android:text="站点端口" />
<Button
android:id="@+id/startCon"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/scanService"
android:layout_alignBottom="@+id/scanService"
android:layout_marginLeft="55dp"
android:layout_toRightOf="@+id/scanService"
android:text="连接" />
<TextView
android:id="@+id/destination"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/porttext"
android:layout_alignParentTop="true"
android:layout_marginTop="27dp"
android:text="站点IP" />
<EditText
android:id="@+id/PortText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/porttext"
android:layout_alignBottom="@+id/porttext"
android:layout_alignLeft="@+id/IPText"
android:layout_alignRight="@+id/mdns_list"
android:ems="10"
android:height="35dip"
android:hint="Port"
android:width="60dip" />
<EditText
android:id="@+id/IPText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/destination"
android:layout_alignRight="@+id/mdns_list"
android:layout_toRightOf="@+id/porttext"
android:ems="10"
android:hint="Please input IP"
android:width="160dip" >
<requestFocus />
</EditText>
</RelativeLayout>
监听mDNS服务用的是jmDNS,大家可以去github下,里面也有教程,注意要下最新版本的,有的版本只能在Android5.1以下使用,很是头疼,当初我弄了好久也没找到问题所在。导入JmDNS的包,使用很简单,在上面的代码中,主要的部分是
jmdns.addServiceListener(type, listener = new ServiceListener() {
@Override
public void serviceResolved(ServiceEvent event) {
String addr = "";
if (event.getInfo().getHostAddress() != null && event.getInfo().getInetAddress()!=null) {
addr = event.getInfo().getHostAddress();
}
mdnsMap = new HashMap<String, Object>();
mdnsMap.put("Service_IP", addr);
mdnsMap.put("Service_Port", event.getInfo().getPort());
mdnsMap.put("service_Name", event.getName());
if (!mdnsList.contains(mdnsMap)) {
mdnsList.add(mdnsMap);
}
}
@Override
public void serviceRemoved(ServiceEvent ev) {
}
@Override
public void serviceAdded(ServiceEvent event) {
// Required to force serviceResolved to be called again (after the first search)
jmdns.requestServiceInfo(event.getType(), event.getName(), 1);
}
});
serviceAdded(ServiceEvent event) 方法;监听服务调用的是serviceResolved(ServiceEvent event);当不想提供服务调用的是serviceRemoved(ServiceEvent ev) 方法。
效果图: