Android udp 广播发送和接收
最近在和同学开发一款app,作为课程大作业。其中,涉及到udp socket (多播) 的发送和接收、tcp socket 的发送和接收。作为一个Java的门外汉,在简单地看了一些理论地资料之后,实际编程中遇到了不少问题。然后,又在网上大搜这方面的博客,找来找去,其实大家写的东西基本都一样,因为规则已经订好了。网上的代码不全,又有一些错漏,让我走了很多弯路,无数次推倒代码重写,debug,终于调出了一个实际可运行的版本。 希望初学的童鞋看到我的这篇博客能少走一些弯路.
-----------------------
转载请注明出处:
http://www.cnblogs.com/zhangzph/p/4475962.html
-----------------------
在给出代码之前,先简单介绍一下我的代码在做什么。
代码逻辑:
本机发送udp广播
本机开启线程,监听来自别的机器的udp广播,显示信息。 然后,对udp来源发送tcp连接
接收来自别的机器的tcp连接,并显示信息
(这里的udp广播,我使用udp多播代替了,多播具有广播的所有优点,而且有更少的缺点,实现上也比较简单,这里就不再过多地介绍了)
具体ui操作:
start 按钮用来启动udp 多播,stop按钮停止发送 (实际上,由于start 按钮按下之后只发送一次udp多播,stop按钮只是用于setEnabled操作)下面有两个TextView,内容为send的TextView 显示--本机发送 tcp socket 的信息; 内容为receive的TextView 显示--本机接收来自别的机器的udp socket 和 tcp socket 的信息.
几个需要注意的地方:
1. Android Manifest 权限设置、sdk版本信息:
本文所涉及到的这些功能需要获取 Android 的一些权限,下面是我的权限和版本信息
< uses-sdk android:minSdkVersion="15" android:targetSdkVersion="21" /> < uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> < uses-permission android:name="android.permission.INTERNET"/> < uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> < uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/> < uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> |
上面 条目 uses-sdk中的信息,需要在build.gradle文件中同步。
2. 注意udp广播,和udp广播监听需要绑定同一个端口
3. 其他的有关IDE抽风的问题,比如我的Android Studio,有时候你修改了代码,重新把程序烧进手机的时候,它竟然会用缓存中代码的老版本来烧程序。。。 还有,有时候project加载太慢,程序崩溃之后,logcat好长时间都不出错误信息,严重影响debug。
4. 建议使用android sdk版本比较新的手机进行测试。 我测试的时候,用一部4.4和5.1的成功了。混合另外一部4.0.x的则有时候不太灵通。
github上的项目链接:
https://github.com/zhangpzh/Anjay
主要代码:
xml 源码:
<LinearLayout 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:orientation="vertical" tools:context=".MyActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_horizontal" > <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="start" /> <Button android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="150dp" android:text="stop" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_horizontal" > <TextView android:id="@+id/send_information" android:layout_marginTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="send" android:layout_marginRight="110dp" /> <TextView android:id="@+id/receive_information" android:layout_marginTop="50dp" android:text="receive" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout>
Java 源码:
(github 上面的代码已经把各个通信内部类给模块化了,不再像下面这样,全都定义在一个Activity里。但是为了集中展示app的功能,下面仍使用一个文件显示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | package com.example.user.anjay; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.example.user.anjay.R; import org.apache.http.conn.util.InetAddressUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.Enumeration; public class MyActivity extends Activity { private static String LOG_TAG = "WifiMulticastActivity" ; Button startBroadCast; Button stopBroadCast; TextView send_label; TextView receive_label; /* 用于 udpReceiveAndTcpSend 的3个变量 */ Socket socket = null ; MulticastSocket ms = null ; DatagramPacket dp; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_my); startBroadCast = (Button) findViewById(R.id.start); stopBroadCast = (Button) findViewById(R.id.stop); send_label = (TextView) findViewById(R.id.send_information); receive_label = (TextView) findViewById(R.id.receive_information); send_label.append( "\n\n" ); receive_label.append( "\n\n" ); startBroadCast.setOnClickListener(listener); stopBroadCast.setOnClickListener(listener); /* 开一个线程接收tcp 连接*/ new tcpReceive().start(); /* 开一个线程 接收udp多播 并 发送tcp 连接*/ new udpReceiveAndtcpSend().start(); } private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { if (v == startBroadCast ) { startBroadCast.setEnabled( false ); stopBroadCast.setEnabled( true ); /* 新开一个线程 发送 udp 多播 */ new udpBroadCast( "hi ~!" ).start(); } else { startBroadCast.setEnabled( true ); stopBroadCast.setEnabled( false ); } } }; /* 发送udp多播 */ private class udpBroadCast extends Thread { MulticastSocket sender = null ; DatagramPacket dj = null ; InetAddress group = null ; byte [] data = new byte [ 1024 ]; public udpBroadCast(String dataString) { data = dataString.getBytes(); } @Override public void run() { try { sender = new MulticastSocket(); group = InetAddress.getByName( "224.0.0.1" ); dj = new DatagramPacket(data,data.length,group, 6789 ); sender.send(dj); sender.close(); } catch (IOException e) { e.printStackTrace(); } } } /*接收udp多播 并 发送tcp 连接*/ private class udpReceiveAndtcpSend extends Thread { @Override public void run() { byte [] data = new byte [ 1024 ]; try { InetAddress groupAddress = InetAddress.getByName( "224.0.0.1" ); ms = new MulticastSocket( 6789 ); ms.joinGroup(groupAddress); } catch (Exception e) { e.printStackTrace(); } while ( true ) { try { dp = new DatagramPacket(data, data.length); if (ms != null ) ms.receive(dp); } catch (Exception e) { e.printStackTrace(); } if (dp.getAddress() != null ) { final String quest_ip = dp.getAddress().toString(); /* 若udp包的ip地址 是 本机的ip地址的话,丢掉这个包(不处理)*/ //String host_ip = getLocalIPAddress(); String host_ip = getLocalHostIp(); System.out.println( "host_ip: -------------------- " + host_ip); System.out.println( "quest_ip: -------------------- " + quest_ip.substring( 1 )); if ( (!host_ip.equals( "" )) && host_ip.equals(quest_ip.substring( 1 )) ) { continue ; } final String codeString = new String(data, 0 , dp.getLength()); receive_label.post( new Runnable() { @Override public void run() { receive_label.append( "收到来自: \n" + quest_ip.substring( 1 ) + "\n" + "的udp请求\n" ); receive_label.append( "请求内容: " + codeString + "\n\n" ); } }); try { final String target_ip = dp.getAddress().toString().substring( 1 ); send_label.post( new Runnable() { @Override public void run() { send_label.append( "发送tcp请求到: \n" + target_ip + "\n" ); } }); socket = new Socket(target_ip, 8080 ); } catch (IOException e) { e.printStackTrace(); } finally { try { if (socket != null ) socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } } /* 接收tcp连接 */ private class tcpReceive extends Thread { ServerSocket serverSocket; Socket socket; BufferedReader in; String source_address; @Override public void run() { while ( true ) { serverSocket = null ; socket = null ; in = null ; try { Log.i( "Tcp Receive" , " new ServerSocket ++++++++++" ); serverSocket = new ServerSocket( 8080 ); socket = serverSocket.accept(); Log.i( "Tcp Receive" , " get socket ++++++++++++++++" ); if (socket != null ) { in = new BufferedReader( new InputStreamReader(socket.getInputStream())); StringBuilder sb = new StringBuilder(); sb.append(socket.getInetAddress().getHostAddress()); String line = null ; while ((line = in.readLine()) != null ) { sb.append(line); } source_address = sb.toString().trim(); receive_label.post( new Runnable() { @Override public void run() { receive_label.append( "收到来自: " + "\n" +source_address+ "\n" + "的tcp请求\n\n" ); } }); } } catch (IOException e1) { e1.printStackTrace(); } finally { try { if (in != null ) in.close(); if (socket != null ) socket.close(); if (serverSocket != null ) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } public String getLocalHostIp() { String ipaddress = "" ; try { Enumeration<NetworkInterface> en = NetworkInterface .getNetworkInterfaces(); // 遍历所用的网络接口 while (en.hasMoreElements()) { NetworkInterface nif = en.nextElement(); // 得到每一个网络接口绑定的所有ip Enumeration<InetAddress> inet = nif.getInetAddresses(); // 遍历每一个接口绑定的所有ip while (inet.hasMoreElements()) { InetAddress ip = inet.nextElement(); if (!ip.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ip .getHostAddress())) { return ip.getHostAddress(); } } } } catch (SocketException e) { Log.e( "feige" , "获取本地ip地址失败" ); e.printStackTrace(); } return ipaddress; } private String getLocalIPAddress() { try { for (Enumeration<NetworkInterface> en = NetworkInterface .getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf .getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress()) { return inetAddress.getHostAddress().toString(); } } } } catch (SocketException ex) { Log.e(LOG_TAG, ex.toString()); } return null ; } // 按下返回键时,关闭 多播socket ms @Override public void onBackPressed() { ms.close(); super .onBackPressed(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.my, 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.action_settings) { return true ; } return super .onOptionsItemSelected(item); } } |
如果有错漏的地方,还请批评指正。毕竟是初学者,不出错,不疏忽是不可能的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步