学习Android有几个月了,最近喜欢上了网络编程,于是想通过Android写一些一个小程序用于连接外网.在这里非常感谢雪夜圣诞的支持,非常感谢,给我打开新的一扇门.

1.声明,本程序只能用于西南大学连接外网登录,其他网站需要自己进行抓包测试.

2.声明,本文更多的是关注网络抓包已经,本地构造,如果有什么错误,请尽情指教,非常感谢.

3.声明,最后源代码,以全部上传github,需要的同志可以自行下载,文章结尾会附带链接.

废话不多说,正文开始:

学校官网

第一步,首先需要实现的是登录操作:

当我们点击登录外网会出现以下页面:

这个页面时关键,我们就要在这个页面进行抓包处理.我使用的是chorme浏览器,我打开chrome浏览器的开发者工具,从中选择network进行信息监控以下界面:

这里需要关注的是,我们需要勾选上Preserve log,这样页面跳转时,发送的信息就不会消失了.然后,我们点击连接按钮,我们我可以发现以下情况:

其实我们可以发现我们要实现登录按钮,我们需要使用的url就是第一个,我们点击第一个url查看数据包详情,这样子我们就可以知道这个url需要哪些数据:

这里我们可以发现,其实浏览器是向这个url发送了一个post请求,在post中放置了如下数据(userId,password,service,queryString,operatroPwd,operatorUserId,validcode).

我们很容易就发现userId和passwordId(就是账号和密码),service经过我多次测试并不会改变,应该是固定值,除了queryString之外的属性都是空的.难点就在这个queryString,我们点击view source查看原来编码(这里需要特别注意,浏览器会进行一次编码显示给我们,我们使用的应该是source原来的value值)

我们可以发现,其实两者的内容都是一样的,就是=编码的格式不同而已,因此我们只要向http://222.198.127.170/发送一个get请求,然后把对应的内容截取出来就可以了.

因此登录很简单了,网址有了,填充的数据也知道了,我只要发送一个post请求就可以实现登录功能了.这里贴一下登录函数的代码

    //进行登录操作
private boolean loginValidate(String username,String passwd) throws Exception
{
final String html = HttpUtil.sendGetRequest("http://222.198.127.170/", false, null, "gbk");
//使用正则表达式获取对应的填充数据
String p = "jsp\\?(.+?)'</script>";
Pattern reg = Pattern.compile(p);
Matcher m= reg.matcher(html);
String FillingStr = "";
if(m.find())
{
FillingStr = m.group(1);
}
//这里需要注意,需要使用utf-8格式进行编码
FillingStr = URLEncoder.encode(FillingStr,"utf-8");
final String url = "http://222.198.127.170/eportal/InterFace.do?method=login";
final String data="userId="+username+"&password="+passwd+"&service=%25E9%25BB%2598%25E8%25AE%25A4&queryString="+FillingStr+"&operatorPwd=&operatorUserId=&validcode=";
//发送登录请求
String html2=HttpUtil.sendPostRequest(url, data, false, null, "gbk");
if(html2.contains("success"))
return true;
return false;
}

HttpUtil是我自己写的一个发送Http请求的工具类,我把工具类列出来,github中有源码,需要的可以进行查阅.

package com.network.cjyong.networklogin.util;

/**
* Created by cjyong on 2017/3/5.
*/ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; public class HttpUtil
{
/**
* 向对应的网址发送get请求,以String的形式返回服务器的相应
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param usecookie 是否使用cookie
* @param cookie 需要携带的cookie
* @param encoding 编码格式
* @return 以string的形式返回服务器的响应
* @throws Exception
*/
public static String sendGetRequest(final String url,final boolean usecookie,final String cookie,final String encoding) throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{
@Override
public String call() throws Exception
{
URL turl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) turl.openConnection();
//设置时间限制,抛出异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if(usecookie)
conn.setRequestProperty("Cookie", cookie);
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,encoding));
StringBuilder sb = new StringBuilder();
String line = null;
while((line = reader.readLine())!= null)
sb.append(line+"\n");
return sb.toString();
}
});
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} /**
* 向对应的网址发送post请求,以String的形式返回服务器的相应
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param data 发送post请求携带的数据
* @param usecookie 是否使用cookie
* @param cookie 需要携带的cookie
* @param encoding 编码格式
* @return 以string的形式返回服务器的响应
* @throws Exception
*/
public static String sendPostRequest(final String url,final String data,final boolean usecookie,final String cookie,final String encoding) throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{
@Override
public String call() throws Exception
{
URL turl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) turl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//设置时间限制,抛出异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if(usecookie)
conn.setRequestProperty("Cookie", cookie);
OutputStream outStream = conn.getOutputStream();
outStream.write(data.getBytes());
outStream.flush();
outStream.close();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,encoding));
StringBuilder sb = new StringBuilder();
String line = null;
while((line = reader.readLine())!= null)
sb.append(line+"\n");
return sb.toString();
}
});
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} /**
* 向对应的网址发送post请求,获取对应的cookie,以备后用
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param data 发送post请求携带的数据
* @return 以string的形式返回服务器的响应
* @throws Exception
*/ public static String getCookie(final String url,final String data)throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{ @Override
public String call() throws Exception
{
byte[] Data = data.getBytes();
URL turl=new URL(url);
HttpURLConnection conn = (HttpURLConnection)turl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//设置连接与读取时间过期返回异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
OutputStream outStream = conn.getOutputStream();
outStream.write(Data);
outStream.flush();
outStream.close();
String Cookie=conn.getHeaderField("Set-Cookie");
return Cookie;
} }
);
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} }

登出功能类似,我这里就不在赘述了,贴一下登出函数的代码:

    //进行登出操作
private boolean logoutValidate() throws Exception
{
String html = HttpUtil.sendGetRequest("http://222.198.127.170/eportal/InterFace.do?method=logout",false,null,"utf-8");
if(html.contains("success"))
return true;
return false;
}

最后来讲一下,强制下线功能的实现(这个需要用到cookie进行信息的交流,比较有代表性)

在学校网络管理中心,有校园网推出选项,当我们点击时,会出现如下情况:

这说明,强制下线并不是单纯向一个url发送一个链接就可以完成的,在这里我们就需要进行抓包处理:

这里我们可以发现,这里发送的请求也很简单,就是向login_judge.jsf发送一个post请求,post中数据也很简单,就两个内容(name,password)

然后我们点击我的设备,就可以进行退出网络操作了.我们截取一下包:

我们可以发现,这里需要向userself_ajax.jsf?methodName=xxxxx,发送一个post请求,其中数据特别简单一个key和一串数字,并没有用户名和密码,而Cookie中出现了,很明显2个页面之间的交流是通过cookie来是实现的,所以我们需要在登录的页面获取对应的cookie进行编辑,向这个url发送post请求.难点在于,封装的第二个数据是什么?这里就要进行苦逼的网页代码查询了,我们点开onlinedevice_list.jsf进行代码查询: (由于网页代码太长了,我截取一部分有用的进行分享)

我们可以发现的第二个数据,其实就是我们不同设备的局域网ip地址,这样子,数据也获取到了,cookie也得到了,我们只要向指定url发送post请求就可以了.

这里贴一下强制下线函数的代码:

 //进行强制下线操作
private boolean forceLogoutValidate(String username,String passwd) throws Exception
{
//构造填充参数
String data ="name="+username+"&password="+passwd;
String url= "http://service2.swu.edu.cn/selfservice/module/scgroup/web/login_judge.jsf";
//构造cookie
String Cookie=HttpUtil.getCookie(url,data);
Cookie=String.format(Cookie+" rmbUser=true; userName=%s; passWord=%s; oldpassWord=%s;", username,passwd,passwd);
String listurl= "http://service2.swu.edu.cn/selfservice/module/webcontent/web/onlinedevice_list.jsf";
String html= HttpUtil.sendGetRequest(listurl, true, Cookie, "gbk");
//账号密码错误
if(html.contains("您还未登录或会话过期"))
return false;
//获取设备的IP地址构造填充数据
String p = "<span id=\"a1\">IP : (.+?)</span >";
Pattern reg = Pattern.compile(p);
Matcher m=reg.matcher(html);
//将所有的设备进行下线
while(m.find())
{
//执行下线操作
String myurl = "http://service2.swu.edu.cn/selfservice/module/userself/web/userself_ajax.jsf?methodName=indexBean.kickUserBySelfForAjax";
String mydata = "key= "+username+":" +m.group(1);
HttpUtil.sendPostRequest(myurl, mydata, true, Cookie, "utf-8");
}
return true;
}

到这里,所有的重要的函数和抓包方法都已经讲解完毕,最后贴一下手机APP的截图:

贴一下MainActivity的代码:

package com.network.cjyong.networklogin;

import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
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.Toast;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.network.cjyong.networklogin.util.HttpUtil; public class MainActivity extends AppCompatActivity
{
EditText etUsername,etUserpass;
Button login,cancel,logout,forceout,help;
SharedPreferences preferences;
SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定组件
bindCompoent();
//程序初始化
init();
} //绑定各种主键并设置好监听器
private void bindCompoent()
{
//绑定各类主键
etUsername = (EditText) findViewById(R.id.userEditText);
etUserpass = (EditText) findViewById(R.id.pwdEditText);
login = (Button) findViewById(R.id.bnLogin);
cancel = (Button) findViewById(R.id.bnCancel);
logout = (Button) findViewById(R.id.bnLogout);
forceout = (Button) findViewById(R.id.bnForceLogout);
help = (Button) findViewById(R.id.bnHelp); //给取消按钮绑定监听器
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
etUsername.setText(null);
etUserpass.setText(null);
}
}); //给登录按钮监听器
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(loginValidate(username,userpasswd))
{
Toast.makeText(getApplicationContext(),
"登录成功",Toast.LENGTH_LONG).
show();
login.setEnabled(false);
logout.setEnabled(true);
//将正确的账号和密码存储到本地中去
save();
}
else
{
Toast.makeText(getApplicationContext(),
"登录失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); //给登出按钮设置监听器
logout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(logoutValidate())
{
Toast.makeText(getApplicationContext(),
"登出成功",
Toast.LENGTH_SHORT).
show();
logout.setEnabled(false);
login.setEnabled(true);
}
else
{
Toast.makeText(getApplicationContext(),
"登出失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); //给强制登出按钮设置监听器
forceout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(forceLogoutValidate(username,userpasswd))
{
Toast.makeText(getApplicationContext(),
"下线成功",
Toast.LENGTH_SHORT).
show();
logout.setEnabled(false);
login.setEnabled(true);
}
else
{
Toast.makeText(getApplicationContext(),
"下线失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); help.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(MainActivity.this).
setTitle("帮助界面").
setMessage("本软件适合在西南大学一键登录外网 \n--------by cjyong\n" +
"强制退出按钮会强制退出所有当前账号登录的设备,请谨慎使用\n"+
"如果软件有问题,请联系QQ2686600303\n").
setNegativeButton("取消",null).
show();
}
});
} private void init()
{
//判断以前是否登录过,通过存储在本地的记录进行判断
isOldUser(); //判断网络是否连接正确,是否可以联网
wifiIsGood(); } //检查网络是否可以正确连接
private void wifiIsGood()
{
//判断wifi是否连接
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo.State wifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
if(wifi== NetworkInfo.State.DISCONNECTED)
{
Toast.makeText(this,"请连接好WiFi再进行登录",Toast.LENGTH_SHORT).show();
return ;
} //判断是否可以上网
try
{
//前往bibili网站
String html = HttpUtil.sendGetRequest("http://www.bilibili.com", false, null, "utf-8");
if(html.contains("http://222.198.127.170/"))
{
return;
}
else
{
Toast.makeText(this,"你的WiFi已经可以上网,不用登陆",Toast.LENGTH_LONG).show();
return;
}
}
catch (Exception e)
{
e.printStackTrace();
}
} //通过遍历本地记录,进行判断是否之前登录过,如果登录自动填充两个对话框
private void isOldUser()
{
//这里通过preference进行储存相应数据
preferences = getSharedPreferences("userpass",0);
editor = preferences.edit();
String username = preferences.getString("username", null);
String userpass = preferences.getString("userpass",null); if(username != null && userpass!= null)
{
etUsername.setText(username);
etUserpass.setText(userpass);
}
return;
} //检查wifi状况和输入情况
private boolean validate()
{
String username = etUsername.getText().toString();
String userpass = etUserpass.getText().toString();
if(username.equals("") || userpass.equals(""))
{
Toast.makeText(this,"用户名或者密码不可以为空,请重新输入",Toast.LENGTH_SHORT).show();
return false;
}
return true;
} //将正确的账号和密码存储到本地中去
private void save()
{
String username = etUsername.getText().toString();
String userpass = etUserpass.getText().toString();
editor.putString("username",username);
editor.putString("userpass",userpass);
editor.commit();
} //进行登录操作
private boolean loginValidate(String username,String passwd) throws Exception
{
final String html = HttpUtil.sendGetRequest("http://222.198.127.170/", false, null, "gbk");
//使用正则表达式获取对应的填充数据
String p = "jsp\\?(.+?)'</script>";
Pattern reg = Pattern.compile(p);
Matcher m= reg.matcher(html);
String FillingStr = "";
if(m.find())
{
FillingStr = m.group(1);
}
//这里需要注意,需要使用utf-8格式进行编码
FillingStr = URLEncoder.encode(FillingStr,"utf-8");
final String url = "http://222.198.127.170/eportal/InterFace.do?method=login";
final String data="userId="+username+"&password="+passwd+"&service=%25E9%25BB%2598%25E8%25AE%25A4&queryString="+FillingStr+"&operatorPwd=&operatorUserId=&validcode=";
//发送登录请求
String html2=HttpUtil.sendPostRequest(url, data, false, null, "gbk");
if(html2.contains("success"))
return true;
return false;
} //进行登出操作
private boolean logoutValidate() throws Exception
{
String html = HttpUtil.sendGetRequest("http://222.198.127.170/eportal/InterFace.do?method=logout",false,null,"utf-8");
if(html.contains("success"))
return true;
return false;
} //进行强制下线操作
private boolean forceLogoutValidate(String username,String passwd) throws Exception
{
//构造填充参数
String data ="name="+username+"&password="+passwd;
String url= "http://service2.swu.edu.cn/selfservice/module/scgroup/web/login_judge.jsf";
//构造cookie
String Cookie=HttpUtil.getCookie(url,data);
Cookie=String.format(Cookie+" rmbUser=true; userName=%s; passWord=%s; oldpassWord=%s;", username,passwd,passwd);
String listurl= "http://service2.swu.edu.cn/selfservice/module/webcontent/web/onlinedevice_list.jsf";
String html= HttpUtil.sendGetRequest(listurl, true, Cookie, "gbk");
//账号密码错误
if(html.contains("您还未登录或会话过期"))
return false;
//获取设备的IP地址构造填充数据
String p = "<span id=\"a1\">IP : (.+?)</span >";
Pattern reg = Pattern.compile(p);
Matcher m=reg.matcher(html);
//将所有的设备进行下线
while(m.find())
{
//执行下线操作
String myurl = "http://service2.swu.edu.cn/selfservice/module/userself/web/userself_ajax.jsf?methodName=indexBean.kickUserBySelfForAjax";
String mydata = "key= "+username+":" +m.group(1);
HttpUtil.sendPostRequest(myurl, mydata, true, Cookie, "utf-8");
}
return true;
} }

贴一下github地址(欢迎补充):

https://github.com/cai123nb/NetworkLogin/tree/master/main

讲点废话,其实我们可以看出,编码并不困难,困难的使我们怎么抓取准确的网址和数据包,怎么填充正确的数据包.

只要我们这一点学习的好的话,,我们可以拓展开来,抓手机号码的归属地,邮件/快递的送达地址等,都是可以的.

第二,其实我的HttpUtil有点过时了,现在大多数的人都是使用HttpClient,因为HttpClient支持https,我用老版的用顺手,也就没有换了,如果

有人有不同的思路欢迎补充.在这里,抛砖引玉了,你我共勉.

非常感谢,阅读.

17:30:14

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)的更多相关文章

  1. 【bb平台刷课记】wireshark结合实例学抓包

    [bb平台刷课记]wireshark结合实例学抓包 背景:本校形势与政策课程课需要在网上观看视频的方式来修得学分,视频网页自带"播放器不可快进+离开窗口自动暂停+看完一集解锁下一集(即不能同 ...

  2. android widget 开发实例 : 桌面便签程序的实现具体解释和源代码 (上)

    如有错漏请不吝拍砖指正,转载请注明出处,很感谢 桌面便签软件是android上经常使用软件的一种,比方比較早的Sticky Note,就曾很流行, Sticky Note的介绍能够參见 http:// ...

  3. LoadRunner+Android模所器实现抓包并调试本地服务端

    步骤就是 1:新建LR脚本.协议选择Mobile Application - HTTP/HTML 2:在record里选择第三个:Record Emulator........ 3:  选择下一步后, ...

  4. R语言网络爬虫学习 基于rvest包

    R语言网络爬虫学习 基于rvest包 龙君蛋君:2015年3月26日 1.背景介绍: 前几天看到有人写了一篇用R爬虫的文章,感兴趣,于是自己学习了.好吧,其实我和那篇文章R语言爬虫初尝试-基于RVES ...

  5. Android 应用开发实例之情景模式

    2013-07-01 Android 应用开发实例 1. 情景模式 使用TabHost来实现主界面的布局. 设置一组RadioButton来切换不同的情景模式. 对比普通情景模式,定时情景模式需要加上 ...

  6. C#一步一步学网络辅助开发(1)--常用抓包工具的使用

    这次写的是一个系列,是让大家了解如何进行网络的辅助开发.要进行网络辅助开发抓包工具是必不可少的,下面就让大家熟悉一下常用的一些抓包工具, 1,Fiddler 这个工具是我目前用的最多的一款抓包工具,不 ...

  7. 《精通android网络开发》--HTTP数据通信

    No1: 例如:http://www.*****.com/china/index.htm 1)http:// 代表超文本传送协议,通知*****.com服务器显示web页,通常不用输入 2)www 代 ...

  8. android应用安全——(数据抓包)跟踪监控android数据包

    转载博客:http://blog.csdn.net/xyz_lmn/article/details/8808169 web开发中Chrome.IE.firefox等浏览器都自带提供了插件帮助开发者跟踪 ...

  9. Android手机 Fildder真机抓包

    Fiddler是一个http调试代理,它能 够记录所有的你电脑和互联网之间的http通讯,Fiddler 可以也可以让你检查所有的http通讯,设置断点,以及Fiddle 所有的“进出”的数据(指co ...

随机推荐

  1. iOS 使用 github

    1. 创建 github 账号 登陆官网 https://github.com 进行创建. 2. 创建 github 仓库 3. 添加Pods依赖库所需文件 4. github 之 下载历史版本 5. ...

  2. JS拖动技术--- 关于setCapture

    <script type="text/javascript"> <!-- window.onload=function(){   objDiv = documen ...

  3. redis 安装启动及设置密码<windows>

    redis 1. 安装 1.1 下载解压包,直接解压到任意路径下即可 windows下载地址:ttps://github.com/MSOpenTech/redis/releases 2.启动 2.1 ...

  4. js控制div显示与隐藏

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. 浅谈JavaWEB入门必备知识之Servlet入门案例详解

    工欲善其事.必先利其器,想要成为JavaWEB高手那么你不知道servlet是一个什么玩意的话,那就肯定没法玩下去,那么servlet究竟是个什么玩意?下面,仅此个人观点并通过一个小小的案例来为大家详 ...

  6. Linux下配置Apache最大连接数

    最近有博友发现我的博客经常http 503,博客负载不大,应该不会出现负载问题,很有可能就是Apache最大连接数原因,Apache默认支持150个连接.1.先要修改最大连接数,必须了解Apache的 ...

  7. Servlet的请求HttpServletRequest

    一.从容器到HttpServlet 1.web容器作了什么 web容器做的事情就是,创建Servlet实例,并完成Servlet的名称注册及URL模式的对应.在请求来到时,web容器会转发给正确的Se ...

  8. I/O流

     转自:http://www.cnblogs.com/dolphin0520/p/3791327.html 一.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的 ...

  9. Docker - 用Flannel跨主机

    试了下比较流行的几种SDN,感觉flannel还是比较好用,这里简单记录一下. 用的是virtualbox,3个机器,分别为: genesis : inet 192.168.99.103/24 brd ...

  10. mac下sublime 配置使用markdown

    Sublime Text作为一个优秀的文本编辑器,拥有很多的扩展插件.我们可以利用这些插件为Sublime Text 增加扩展的功能.mac常用编辑器强烈推荐sublime,当前出到Sublime T ...