读书笔记-Java网络编程安全教程-微课版
作者:互联网
Java网络编程安全教程 清华大学出版社 ISBN-9787302482833
仅供参考, 自建索引, 以备后查
https://docs.oracle.com/javase/6/docs/api/index.html
https://docs.oracle.com/javase/7/docs/api/index.html
https://docs.oracle.com/javase/8/docs/api/index.html
=================================================================
一: 简介概述 IO流
java.io.* java.nio.* java.nio.channels.*
InputStream OutputStream 字节流
Reader Writer 字符流
InputStreamReader OutputStreamWriter 字节流与字符流转换
FileInputStream/FileOutputStream 二进制流
BufferedInputStream/BufferedOutputStream 二进制缓冲流
DataInputStream/DataOutputStream 数据流
HTTP/SMTP/POP3/FTP/Telnet/DHCP 应用层+表示层+会话层= *应用层
TCP/IP *传输层 + *网络层
MAC地址 数据链路层+物理层= *网络接口层
SOCKET是操作系统开放给程序员的网络编程接口
传输层以下协议的技术实现是由操作系统完成的,即网络服务由操作系统提供
SOCK_STREAM流式套接字 SOCK_DGRAM数据报套接字 SOCK_RAW原始套接字
通信端口 0~65535
线程: 新生new 等待系统分配CPU执行start 执行run 阻塞wait/sleep/join/yield 消亡throw/interrept/stop
ServerSocket/Socket DatagramPacket/DatagramSocket
=================================================================
二: ServerSocket多线程与Client通信
ServerSocket ss = new ServerSocket();
ss.bind(..);
while (true) {
Socket client = ss.accept();
new ClientSocketHandlerThread(client).start();
}
Class ClientSocketHandlerThread implements Runnable {
ClientSocketHandlerThread(Socket s) {
// TODO
}
public void run() {
// TODO
}
}
SwingWorker<T,V> implements Runnable, Future<T>, RunnableFuture<T>
doInBackground() publish() process() done() get()
=================================================================
三: ServerSocket多线程与Client通信+线程池
最佳线程池大小 = Runtime.getRuntime().availableProcessors() * 2 + 1
int MAX = Runtime.getRuntime().availableProcessors() * 2 + 1;
ExecutorService fixedPool = Executors.newFixThreadPool(MAX);
ServerSocket ss = new ServerSocket();
ss.bind(..);
while (true) {
Socket client = ss.accept();
/// 在线程池中执行
fixedPool.exec( new ClientSocketHandlerThread(client) );
}
fixedPool.shutdown(); /// 等待线程池中所有线程执行完毕,此时已经不能再向线程池添加线程
if ( ! fixedPoo.awaitTermination(60, TimeUnit.SECONDS) ) {
fixedPool.shutdownNow(); /// 如果60S还没结束则强制关闭
}
/ 下面是API文档的示例
The following method shuts down an ExecutorService in two phases,
first by calling shutdown to reject incoming tasks, and then calling
shutdownNow, if necessary, to cancel any lingering tasks:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
Executor
void execute(..) 无参数返回
ExecutorService
Future<?> submit(..) 返回Future对象
T invokeAny(Collection<?>...) 执行一组任务,返回其中一个结果,通常情况下是最先执行完毕的那个
List<Future<?>> invokeAll(Collection<?>...) 返回所有任务的Future对象
更多线程、线程池参考 Java并发编程从入门到精通
=================================================================
四: 非阻塞I/ O NIO:(1)New I/O (2)Non-Blocking I/O
ServerSocket/Socket ServerSocketChannel/SocketChannel
DatagramSocket/DatagramPacket DatagramChannel
Buffer ByteBuffer/CharBuffer/DoubleBuffer/FloatBuffer/IntBuffer/LongBuffer/ShortBuffer
allocate() [ˈæləkeɪt] read() write() clear() flip() [flɪp] wrap() [ræp] limit()
capacity() [kəˈpæsəti] reset() rewind() [ˌriːˈwaɪnd]
Selector
SelectionKey
OP_ACCEPT=16 OP_CONNECT=8 OP_READ=1 OP_WRITE=4
支持多模式 OP_READ | OP_WRITE (位与运算 参考 Linux 权限 124)
自定义协议类 <===============
/// 服务端代码,启动监听
Selector st = Selector.open();
ServerSocketChannel listen = ServerSocketChannel.open();
listen.socket().bind(..);
listen.configureBlocking(false);
listen.register(st, SelectionKey.OP_ACCEPT);
/// register() 第三个参数 If the att argument is not null then
/// the key's attachment will have been set to that value
/// listen.register(st, SelectionKey.OP_ACCEPT, new SelfDefinedProtocol());
while (true) {
int nKeys = st.select();
if (nKeys == 0)
continue; ///
Set<SelectionKey> keys = st.selectedKeys();
keys.forEach(k->{
// TODO
});
}
/// Client端连接
SocketChannel sc = SocketChannel.open();
sc.connect(..);
Charset charset = Charset.forName("UTF-8");
ByteBuffer outBuff = ByteBuffer.wrap("要发送的内容".getBytes(charset));
sc.write(outBuff);
ByteBuffer inBuff = ByteBuffer.allocate(2048);
sc.read(inBuff);
inBuff.flip(); // postion=0
String content = charset.decode(inBuff).toString();
// TODO
IO流:阻塞机制; 多线程占用系统及CPU资源;
readLine() 能确定当前文本行被读完
NIO:读到数据处理,否则立即返回; 采用轮询机制,单线程处理大量并发连接;
缓冲区需要处理数据被分割的情况
NIO支持 分散(scatter) / 聚集(gather) 工作模式
SocketChannel sc = ..;
ByteBuffer header = ByteBuffer.allocate(60);
ByteBuffer body = ByteBuffer.allocate(2048);
ByteBuffer[] gather = { header, body };
sc.read(gather); /// 分散模式 scatter 把通道数据读到多个缓存中
sc.write(gather); /// 聚集模式 gather 多个数据写入到通道中
其它相关 <============= public abstract int interestOps() public abstract int readyOps() |
interestOps() 返回值即 register() 第二个参数
int interestKey = selectionKey.interestOps();
boolean isAccept = interestSet & SelectioinKey.OP_ACCEPT != 0;
// interestSet & SelectionKey.OP_CONNECT;
// interestSet & SelectionKey.OP_READ
// interestSet & SelectionKey.OP_WRITE
/// 或者
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.bind(null);
Selector selector = Selector.open();
channel.register(selector, 29);
while (true) {
int nSize = selector.select();
if (nSize == 0) {
continue;
}
selector.selectedKeys().forEach(key -> {
if (key.isAcceptable()) {
// TODO
}
else if (key.isConnectable()) {
// TODO
}
else if (key.isReadable()) {
// TODO
}
else if (key.isWritable()) {
// TODO
}
});
}
=================================================================
五: UDP
DatagramPacket 构造函数(接收、发送) 及 方法
DatagramSocket 构造函数 及 方法 receive(..) send(..)
对象序列化与反序列化
/// 对象序列化为字节数组
public static <T extends Serializable> byte[] objectTo(T obj) {
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
ObjectOutputStream objOutput = new ObjectOutputStream(output);
objOutput.writeObject(obj);
return output.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/// 字节数组反序列化为对象
public static Object toObject(byte[] bytes) {
try {
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bin);
return oin.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/// 发送数据
public static void sendTo(Serializable data, InetAddress remote, int port) {
DatagramSocket socket = null;
try {
byte[] bytes = objectTo(data);
socket = new DatagramSocket();
socket.setSoTimeout(3000);
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, remote, port);
socket.send(packet);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null)
socket.close();
}
}
UDP: DNS服务、DHCP服务、网络音视频点播
=================================================================
六: TCP传输文件
/// 选择文件
public static File getFile() {
JFileChooser jfc = new JFileChooser();
jfc.setDialogTitle("选择文件");
jfc.setApproveButtonText("确认选择");
if (jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
return jfc.getSelectedFile();
}
return null;
}
SwingWorker<T,V>
T 在 doInBackground() 与 get() 方法中做返回类型
V 在 publish() 与 process() 方法中做要处理的数据类型
调用 execute() 方法,会自动启动线程执行 doInBackground() 方法,类似于
Thread类的 start() 方法会自动调用 run() 方法
事件调度线程 EDT Event Dispatch Thread
在 SwingWorker doInBackground() 执行完毕会调用 SwingWorker done() 方法
可以使用 isDone() 来检测是否执行完毕 也可以调用 get() 方法获取结果
若 doInBackground() 方法调用 publish(),则 EDT 会调用 SwingWorker process() 来更新UI
当前线程调用 SwingWorker execute() 方法立即返回,工作线程执行 doInBackground() ,
可以在其内部调用 publish() 更新进度,EDT(一般是主线程)调用后续方法 process() done(),
且可以监听 SwingWorker 的 PropertyChangeListener 事件
UDP 与 TCP 可以同时使用同一个端口且互不影响
=================================================================
七: SSL/TLS
SSL3.1 == TLS1.1 SSL3.2 == TLS1.1 SSL3.3 == TLS1.2
在 应用层 与 TCP传输层 之间加入 SSL/TLS协议层
常用加密算法
DES对称密钥 3DES Blowfish RC5
AES分组对称密钥 AES-128 AES-192 AES-256
RSA非对称密码算法 运算速度比对称密码算法慢
哈希摘要算法 MD5 Message Digest Algorithm 消息摘要算法
SHA-1(160位) SHA-256(256位) Secure Hash Algorithm 安全哈希算法
SSL/TLS协议基本工作原理
密钥生成工具 keytool x.509证书
keytool -genkeypair RSA 生成RSA非对称密钥
keytool -exportcert 从RSA非对称密钥中导出公钥
keytool -importcert 导入公钥到信任的密钥库中
JAVA 摘要算法使用 及 生成AES对称密钥
/// hashType in [ "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512" ]
public static String getHash(String plainText, String hashType) {
try {
MessageDigest md = MessageDigest.getInstance(hashType);
byte[] bytes = md.digest(plainText.getBytes("UTF-8"));
return DatatypeConverter.printHexBinary(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/// 生成256位AES对称密钥
public static SecretKey newKey() {
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
return kg.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/// 向指定ServerSocket发送文件 读取需要按照以下顺序
/// long(文件长度)
/// 文件byte[]
/// int(AES对称密钥长度)
/// 密钥byte[]
/// int(文件摘要长度)
/// 文件摘要byte[]
public void sendToWithSSL(String filePath, InetAddress remote, int port) throws Exception {
/// 本机私钥
KeyStore privateKS = getKeyStore("/config/client.keystore", "pwd567");
KeyManagerFactory privateKF = KeyManagerFactory.getInstance("SunX509");
privateKF.init(privateKS, "pwd567".toCharArray());
/// 其它机器发行的公钥
KeyStore publicTKS = getKeyStore("/config/public.keystore", "899psw");
TrustManagerFactory publicKF = TrustManagerFactory.getInstance("SunX509");
publicKF.init(publicTKS);
/// SSL/TLS上下文
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(privateKF.getKeyManagers(), publicKF.getTrustManagers(), null);
///
/// 创建SSL环境下的Socket
Socket socket = ctx.getSocketFactory().createSocket(remote, port);
DataOutputStream sendOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
File file = new File(filePath);
sendOut.writeLong(file.length()); /// 文件长度
sendOut.flush();
/// 读取文件以便写到SOCKET中,并在完成后生成摘要信息
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
DigestInputStream digestIn = new DigestInputStream(new BufferedInputStream(new FileInputStream(file)), sha256);
///
byte[] buf = new byte[2048];
int temp = 0;
while ( (temp = digestIn.read(buf)) != -1) {
if (temp == 0)
continue;
sendOut.write(buf, 0, temp);
sendOut.flush();
}
digestIn.close();
/// sendOut.close(); /// 此处还要关闭,之后还要写密钥信息
/// 从输入流中获取文件摘要信息
byte[] fileDigest = digestIn.getMessageDigest().digest();
//String digestText = DatatypeConverter.printHexBinary(fileDigest);
/// 加密文件摘要 当前机器私钥加密,需要用发行的公钥解密
Key privateKey = privateKS.getKey("my_key", "pwd567".toCharArray());
byte[] signature = doCipher("RSA/ECB/PKCS1Padding", privateKey, fileDigest);
//String signatureText = DatatypeConverter.printHexBinary(signature);
/// 我方公钥是公开发行,任何人均可使用公钥对其解密 所以对加密后的文件摘要再次做加密处理
Key randomKey = newKey(); /// 随机对称密钥
byte[] signatureAES = doCipher("AES", randomKey, signature);
/// 再用对方的公钥把 使用的随机对称密钥 加密,对方用私钥可以解密 之后可以对文件摘要进行解密
Key publicKey = publicTKS.getCertificate("server").getPublicKey();
byte[] encryptedKey = doCipher("RSA/ECB/PKCS1Padding", publicKey, randomKey.getEncoded());
/// 把密钥等发送
sendOut.writeInt(encryptedKey.length);
sendOut.flush();
sendOut.write(encryptedKey);
sendOut.flush();
sendOut.writeInt(signatureAES.length);
sendOut.flush();
sendOut.write(signatureAES);
sendOut.flush();
sendOut.close();
}
/// 生成256位AES对称密钥
public static SecretKey newKey() {
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
return kg.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/// 加密数据
private byte[] doCipher(String type, Key key, byte[] original) throws Exception {
Cipher cipher = Cipher.getInstance(type);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(original);
}
/// 加载密钥库
private KeyStore getKeyStore(String path, String pwd) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
InputStream stream = this.getClass().getResourceAsStream(path);
ks.load(stream, pwd.toCharArray());
return ks;
}
=================================================================
八: 网络抓包
Pcap4J 通过 JNA 对 libpcap(UNIX/Linux), WinPcap, Npcap 封装调用
WireShark 抓包工具基于 libpcap WinPcap
数据传输中: 应用层数据 到达 传输层 会被加上 协议端口号,
到达 网络层 会被加上 IP头信息,设置源地址及目标地址,
到达 网络链路层, IP地址映射为 MAC地址
到达 物理层, 数据为 二进制
数据包解析协议: 帧头部MAC --- 网络层状况IP --- 传输层头部PORT --- 数据包
帧状况包含收发双方MAC地址
TCP头部20-60字节,无附加信息时20字节
IP状况20-60字节,无附加信息时20字节
=================================================================
九: 邮件
SMTP: Simple Mail Transfer Protocol 简单邮件传输协议 默认端口25
ESMTP: Extension Simple Mail Transfer Protocol 加入安全认证功能
交互过程中 2** 表示命令执行成功 3** 等待用户输入 5** 命令执行失败
ehlo <name> 用于替代 helo
auth **** 回应SMTP服务器的验证方式
e.g. auth login ENTER
{Base64编码的用户名} ENTER
{Base64编码的密码} ENTER
mail from:<abc@qq.com>
rcpt to:<def@163.com>
data SMTP服务器等待用户输入邮件内容
邮件内容输入结束标志是 回车换行 输入 . 再回车换行
quit 结束退出
POP3/IMAP: 用于获取邮件列表数据
Post Office Protocol Version 3 端口110
Internet Mail Access Protocol Version4
JavaMail库 javax.mail.Message javax.mail.Transport javax.mail.Store javax.mail.Session
=================================================================
十: WebSocket
javax.websocket.server
@ServerEndpoint 标记类为 WebSocket 服务端
@PathParam 用于动态参数,URI中及方法参数中均可
@OnOpen 连接成功时触发的方法
@OnMessage 服务端收到客户端的消息触发 查看源码示例给出标记方法的参数类型
@OnClose @OnError
在 pom.xml 加入库引用,可查看上述注解源码
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
WebSocket 握手阶段的 HTTP 头信息
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ****** ///BASE64随机值
Sec-WebSocket-Version: ** ///WebSocket版本号
握手阶段响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ********
JS WebSocket 发送代码示例
// TODO
服务端 @ServerEndpoint 标记代码示例
// TODO
=================================================================
十一: Nodejs Socket.IO MongoDB HTML5语音
Nodejs 是 Javascript 在服务端的运行环境,采用谷歌 V8 引擎,内核由c++编写,主要
技术特点是事件驱动和非阻塞I/O 其在命令行模式下使用 REPL 和 .js 文件
REPL: Read, Evaluate, Print, Loop 输入 求值 输出 循环
模块加载 require('核心模块或第三方模块名称') require('自定义模块路径')
npm: node package manage
# npm -install -g *** # npm install cnpm -g # npm/cnpm install -g supervisor
package.json 模块、项目配置文件,必不可少 用 npm init 来生成
MongoDB: 基于分布式文件在售,在非关系数据库中最低关系型数据库。
BJSON: Binary JSON nodejs中可使用Mongoose来操作MongoDB
SQL | MongoDB | Description |
database | database | |
table | collection | 表===集合 |
row | document | 行===文档 |
column | field | 列===属性 |
index | index | |
table joins | 是否支持关联查询 | |
primary key | primary key | MongoDB自动把id设置为主键 |
HTML5 语音 window.AudioContext AudioNode window.URL.createObjectURL(..)
function isMeidaEnable() {
return !!(navigator.getUserMedia
||navigator.webkitGetUserMedia
||navigator.mozGetUserMedia
||navigator.msGetUserMedia);
}
if (isMediaEnable()) {
console.log('Enabled');
}
=================================================================
十二: HttpClient 正则表达式
URL: new URL(..).openStream()
HttpClient: HttpClients.createDefault().execute(..)
/// 准备参数
List<NameValuePair> paramList = new ArrayList<>();
for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
paramList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
/// 设置参数到请求参数
UrlEncodedFormEntity paramEntity = new UrlEncodedFormEntity(paramList, Consts.UTF_8);
/// HttpGet HttpPost 相当于在地址栏输入URL
/// HttpGet httpGet = new HttpGet(EntityUtils.toString(paramEntity));
HttpPost httpPost = new HttpPost(“http://…”);
/// 设置超时等参数 此处采用默认配置
RequestConfig requestConfig = RequestConfig.DEFAULT;
httpPost.setConfig(requestConfig);
httpPost.setEntity(paramEntity);
/// HttpClient 相当于一个浏览器
CloseableHttpClient httpclient = HttpClients.createDefault();
/// 浏览器发送请求
CloseableHttpResponse response = httpclient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity resultEntity = response.getEntity();
String jsonText = EntityUtils.toString(resultEntity);
// TODO jsonText 是请求返回的结果
EntityUtils.consume(resultEntity); /// 确认是否还有内容并关闭
response.close();
httpclient.close();
}
else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY
|| statusCode == HttpStatus.SC_MOVED_TEMPORARILY
|| statusCode == HttpStatus.SC_SEE_OTHER
|| statusCode == HttpStatus.SC_TEMPORARY_REDIRECT ) {
/// 重定向地址头部
Header header = httpPost.getFirstHeader(“location”);
String redirectURL = header.getValue();
// TODO
}
SSL/TLS HTTPS Hyper Text Transfer Protocol over Secure Socket Layer
与 非 SSL/TLS 主要区别在于 HttpClient 的获取
public HttpClient sslHttpClient() throws Exception {
SSLContext context = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
/// 忽略验证直接通过
return true;
}
}).build();
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(context);
return HttpClients.custom().setSSLSocketFactory(factory).build();
}
正则表达式处理文本
public void analysis(String html) {
Pattern p = Pattern.compile("<tr>.*?</tr>");
Matcher m = p.matcher(html);
while (m.find()) {
String item = m.group();
// TODO item 是匹配的字符
}
}
HtmlParser Jsoup 解析HTML文档
Gson 处理 JSON 文本
=================================================================
十三: Android仿QQ UDP协议通信 Adapter设计模式
android.app.Activity
android.app.Service
android.view.View
android.content.BroadcastReceiver
android.content.ContentProvider
Activity 之间对象传递 需要使用 Serializable / Parcelable
Parcelable 是 Android 提供的轻量级高效内存序列化机制
Android Studio 可以安装 Parcelable 插件自动生成相应代码
设计模式之 Adapter ArrayAdapter, BaseAdapter
AndroidManifest.xml [ˈmænɪfest] Android全局配置文件,位于项目根目录,自行查阅
Android应用分为四个层次:
Linux内核层: 管理CPU、RAM以及为硬件提供驱动支持
系统运行层: 系统核心运行库和Android虚拟机
应用程序框架: Android SDK 面向APP程序设计者、开发者
用户应用程序: 发行的各类APP,QQ、Wechat等
Java 虚拟机把 .java 编译为 .class
而 Android 虚拟机 在此基础上继续,把 各个分散的 .class 压缩到同一个文件中(自行查阅)
再被编译为能被 Dalvik ART 执行的 dex 文件
最后一步都相同,转换为命令交由操作系统运行
=================================================================
十四: Android新闻客户端 OkHttp/Volley请求框架 Glide图片加载
LBS: Location Based Service
Volley.newRequestQueue(..).add(new ***Request(..)) 执行HTTP请求
<uses-permission android:name="android.permission.INTERNET" />
Fragment Activity
FragmentTransaction ft = getSupportFragmentManger().beginTransaction();
class **Adapter extends BaseAdapter {
public View getView(int position, View view, ViewGroup parent) {
if (view == null) { /// 加入判断,可以复用,不用浪费资源再创建
view = LayoutInflater.from(context).inflate(view_id)
}
view.findViewById(id_id);
}
}
URL 发送 HTTP 请求
URL url = new URL(...);
HttpUrlConnection con = (HttpUrlConnection) url.openConnection();
BufferedReader reader = new BufferedReader(
new InputStreamReader(con.getInputStream()));
StringBuilder buf = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
buf.append(buf + "\n");
}
reader.close();
=================================================================
十五: XMPP Openfire Smack Android实时通信APP
XMPP: Extensible Messaging and Presence Protocol
Smack: 客户端类库
Openfire: 基于XMPP协议,采用Java语言开发的实时协作服务器 下载
build.gradle 引入 smack 库
compile 'org.igniterealtime.smack:smack-android-extensions:4.1.9'
compile 'org.igniterealtime.smack:smack-tcp:4.1.9'
AsyncTask
onPreExecute() UI主线程调用,显示进度等UI
doInBackground() 后台异步线程执行
onPostExecute() UI主线程调用,更新UI
getStatus()
calcel() 异步线程取消执行销毁
EventBus
@Subscribe(ThreadMode = ThreadMode.MAIN) 订阅消息
implements StanzaListener/ChatMessageListener
集成百度定位API
标签:return,String,读书笔记,Java,微课,new,byte,null,public 来源: https://blog.csdn.net/u011225581/article/details/112384717