实例介绍
【实例截图】
【核心代码】
package me.tanghai.com; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import android.app.AlertDialog.Builder; import android.app.PendingIntent; import android.content.Intent; import android.net.VpnService; import android.os.Handler; import android.os.Message; import android.os.ParcelFileDescriptor; import android.util.Log; import android.widget.Toast; public class ToyVpnService extends VpnService implements Handler.Callback, Runnable { private static final String TAG = "ToyVpnService"; private String mServerAddress; private String mServerPort; private byte[] mSharedSecret; private PendingIntent mConfigureIntent; private Handler mHandler; private Thread mThread; private ParcelFileDescriptor mInterface; private String mParameters; @Override public int onStartCommand(Intent intent, int flags, int startId) { // The handler is only used to show messages. if (mHandler == null) { mHandler = new Handler(this); } // Stop the previous session by interrupting the thread. if (mThread != null) { mThread.interrupt(); } // Extract information from the intent. String prefix = getPackageName(); mServerAddress = intent.getStringExtra(prefix ".ADDRESS"); mServerPort = intent.getStringExtra(prefix ".PORT"); mSharedSecret = intent.getStringExtra(prefix ".SECRET").getBytes(); // Start a new session by creating a new thread. mThread = new Thread(this, "ToyVpnThread"); mThread.start(); return START_STICKY; } @Override public void onDestroy() { if (mThread != null) { mThread.interrupt(); } } @Override public boolean handleMessage(Message message) { if (message != null) { Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show(); } return true; } @Override public synchronized void run() { try { Log.i(TAG, "Starting"); // If anything needs to be obtained using the network, get it now. // This greatly reduces the complexity of seamless handover, which // tries to recreate the tunnel without shutting down everything. // In this demo, all we need to know is the server address. InetSocketAddress server = new InetSocketAddress( mServerAddress, Integer.parseInt(mServerPort)); // We try to create the tunnel for several times. The better way // is to work with ConnectivityManager, such as trying only when // the network is avaiable. Here we just use a counter to keep // things simple. for (int attempt = 0; attempt < 10; attempt) { mHandler.sendEmptyMessage(R.string.connecting); // Reset the counter if we were connected. if (run(server)) { attempt = 0; } // Sleep for a while. This also checks if we got interrupted. Thread.sleep(3000); } Log.i(TAG, "Giving up"); } catch (Exception e) { Log.e(TAG, "Got " e.toString()); } finally { try { mInterface.close(); } catch (Exception e) { // ignore } mInterface = null; mParameters = null; mHandler.sendEmptyMessage(R.string.disconnected); Log.i(TAG, "Exiting"); } } private boolean run(InetSocketAddress server) throws Exception { DatagramChannel tunnel = null; boolean connected = false; try { // Create a DatagramChannel as the VPN tunnel. tunnel = DatagramChannel.open(); // Protect the tunnel before connecting to avoid loopback. if (!protect(tunnel.socket())) { throw new IllegalStateException("Cannot protect the tunnel"); } // Connect to the server. tunnel.connect(server); // For simplicity, we use the same thread for both reading and // writing. Here we put the tunnel into non-blocking mode. tunnel.configureBlocking(false); // Authenticate and configure the virtual network interface. handshake(tunnel); // Now we are connected. Set the flag and show the message. connected = true; mHandler.sendEmptyMessage(R.string.connected); // Packets to be sent are queued in this input stream. FileInputStream in = new FileInputStream(mInterface.getFileDescriptor()); // Packets received need to be written to this output stream. FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor()); // Allocate the buffer for a single packet. ByteBuffer packet = ByteBuffer.allocate(32767); // We use a timer to determine the status of the tunnel. It // works on both sides. A positive value means sending, and // any other means receiving. We start with receiving. int timer = 0; // We keep forwarding packets till something goes wrong. while (true) { // Assume that we did not make any progress in this iteration. boolean idle = true; // Read the outgoing packet from the input stream. int length = in.read(packet.array()); if (length > 0) { // Write the outgoing packet to the tunnel. packet.limit(length); tunnel.write(packet); packet.clear(); // There might be more outgoing packets. idle = false; // If we were receiving, switch to sending. if (timer < 1) { timer = 1; } } // Read the incoming packet from the tunnel. length = tunnel.read(packet); if (length > 0) { // Ignore control messages, which start with zero. if (packet.get(0) != 0) { // Write the incoming packet to the output stream. out.write(packet.array(), 0, length); } packet.clear(); // There might be more incoming packets. idle = false; // If we were sending, switch to receiving. if (timer > 0) { timer = 0; } } // If we are idle or waiting for the network, sleep for a // fraction of time to avoid busy looping. if (idle) { Thread.sleep(100); // Increase the timer. This is inaccurate but good enough, // since everything is operated in non-blocking mode. timer = (timer > 0) ? 100 : -100; // We are receiving for a long time but not sending. if (timer < -15000) { // Send empty control messages. packet.put((byte) 0).limit(1); for (int i = 0; i < 3; i) { packet.position(0); tunnel.write(packet); } packet.clear(); // Switch to sending. timer = 1; } // We are sending for a long time but not receiving. if (timer > 20000) { throw new IllegalStateException("Timed out"); } } } } catch (InterruptedException e) { throw e; } catch (Exception e) { Log.e(TAG, "Got " e.toString()); } finally { try { tunnel.close(); } catch (Exception e) { // ignore } } return connected; } private void handshake(DatagramChannel tunnel) throws Exception { // To build a secured tunnel, we should perform mutual authentication // and exchange session keys for encryption. To keep things simple in // this demo, we just send the shared secret in plaintext and wait // for the server to send the parameters. // Allocate the buffer for handshaking. ByteBuffer packet = ByteBuffer.allocate(1024); // Control messages always start with zero. packet.put((byte) 0).put(mSharedSecret).flip(); // Send the secret several times in case of packet loss. for (int i = 0; i < 3; i) { packet.position(0); tunnel.write(packet); } packet.clear(); // Wait for the parameters within a limited time. for (int i = 0; i < 50; i) { Thread.sleep(100); // Normally we should not receive random packets. int length = tunnel.read(packet); if (length > 0 && packet.get(0) == 0) { configure(new String(packet.array(), 1, length - 1).trim()); return; } } throw new IllegalStateException("Timed out"); } private void configure(String parameters) throws Exception { // If the old interface has exactly the same parameters, use it! if (mInterface != null && parameters.equals(mParameters)) { Log.i(TAG, "Using the previous interface"); return; } // Configure a builder while parsing the parameters. Builder builder = new Builder(); for (String parameter : parameters.split(" ")) { String[] fields = parameter.split(","); try { switch (fields[0].charAt(0)) { case 'm': builder.setMtu(Short.parseShort(fields[1])); break; case 'a': builder.addAddress(fields[1], Integer.parseInt(fields[2])); break; case 'r': builder.addRoute(fields[1], Integer.parseInt(fields[2])); break; case 'd': builder.addDnsServer(fields[1]); break; case 's': builder.addSearchDomain(fields[1]); break; } } catch (Exception e) { throw new IllegalArgumentException("Bad parameter: " parameter); } } // Close the old interface since the parameters have been changed. try { mInterface.close(); } catch (Exception e) { // ignore } // Create a new interface using the builder and save the parameters. mInterface = builder.setSession(mServerAddress) .setConfigureIntent(mConfigureIntent) .establish(); mParameters = parameters; Log.i(TAG, "New interface: " parameters); } }
标签: VPN
网友评论
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)
支持(0) 盖楼(回复)