这里是Java网络编程
,Java Socket编程
相关的学习手记
。这里按照官方的Java 8 Toturial
教程的Custom Networking学习路径
,对相关的一些内容进行解读(并不完全,如果有错请联系我,谢谢^ _ ^),同时在学习的过程中加入个人的理解与对代码运行的思考。
下面是整个专栏的文章链接,用于快速的导航。
0
. 整个系列文章介绍1
. Java网络编程假定你需要一些基本的网络知识。2
. Java操作URL;java.net.URL,java.net.URLConnection3
. Java Socket编程;关于Socket的使用; java.net.Socket ,java.net.ServerSocket4
. Java UDP Datagrams数据报编程;Java单播,多播编程;java.net.DatagramPacket,java.net.DatagramSocket,java.net.MulticastSocket5
. 使用Java访问系统(电脑,机器)的网络信息,如网卡信息,网络状态等;java.net.NetworkInterface6
. 在Java客户端上操作,管理Cookie;7
. 在1~6节的编程实践中,我遇到的一些问题;
摘要及说明
原文链接:Working with URLs这里会根据具体情况进行相关内容的省略。
注意:文章有Java 8 IO相关API的使用,不过也不复杂
- URL的定义(definition )
- 如何创建和解析URL(create and parse a URL)
- 如何打开一个URL连接(open a connection to a URL)
- 如何读,写一个连接(read from and write to a connection)
URL
很简单,这里做简单介绍。
URL(Uniform Resource Locator),统一资源定位,互联网上资源的引用/链接。
组成部分:
- 协议(Protocol),如http
- 资源名,协议后面的
资源名又由很多部分组成,而主机名,端口号,文件名是常见元素
简单例子:https://docs.oracle.com/javase/tutorial/networking/urls/index.html
- 协议名:https
- 主机名:docs.oracle.com
- 端口号:这里默认为80。即https://docs.oracle.com:80/javase/tutorial/networking/urls/index.html
- 文件名:/javase/tutorial/networking/urls/index.html。其中包含路径与文件名。
使用URL类——创建
创建URL类,有几种构造器,最简单的当然是通过字符串String来创建
URL(String spec)
——创建字符串表示的URL对象URL url = new URL("http://baidu.com");
URL(URL baseURL, String relativeURL)
——相对URL
例如下两个URL,可表示为
- http://example.com/pages/page1.html
- http://example.com/pages/page2.html
URL myURL = new URL("http://example.com/pages/");
URL page1URL = new URL(myURL, "page1.html");
URL page2URL = new URL(myURL, "page2.html");
还有其它构造器,通过制定特定的URL成分来构造URL对象:
URL(String protocol, String host, int port, String file);
URL(String protocol, String host, String file);
除此之外官方的API中还有如下的构造器,暂时不详解,列出:
URL(String protocol, String host, int port, String file, URLStreamHandler handler)
URL(URL context, String spec, URLStreamHandler handler)
注意点:
-
每次new URL时都需要捕获异常。如下是一个简单的例子:
try { URL url = new URL("http://example.com/"); } catch (MalformedURLException e) { e.printStackTrace(); }
-
URL是"write-once"对象
。也就是说,一旦你创建了一个URL对象,你就不能改变它的任何属性,如protocol(协议), host name(主机名), filename(文件名/路径), port number(端口号)。
使用URL类——解析
我们能使用类似getXXX方法来获取URL对象的信息。这些信息对应于我们之前在构造器的一些参数。
URL对象对象的所有get方法:
尝试下面的代码,通过下面的代码,对于URL的各种字段都能有一个清晰的了解:
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.net.*;
import java.io.*;
public class ParseURL {
public static void main(String[] args) throws Exception {
URL aURL = new URL("http://example.com:80/docs/books/tutorial"
+ "/index.html?name=networking#DOWNLOADING");
System.out.println("protocol = " + aURL.getProtocol());
System.out.println("authority = " + aURL.getAuthority());
System.out.println("host = " + aURL.getHost());
System.out.println("port = " + aURL.getPort());
System.out.println("path = " + aURL.getPath());
System.out.println("query = " + aURL.getQuery());
System.out.println("filename = " + aURL.getFile());
System.out.println("ref = " + aURL.getRef());
}
}
/**输出结果
protocol = http
authority = example.com:80
host = example.com
port = 80
path = /docs/books/tutorial/index.html
query = name=networking
filename = /docs/books/tutorial/index.html?name=networking
ref = DOWNLOADING
**/
使用URL类——直接从URL读数据
在成功创建URL对象后,可以直接调用URL的openStream() 方法获取 java.io.InputStream 对象,也就是说我们能很简单地直接读取URL的内容。
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.net.*;
import java.io.*;
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL("https://www.oracle.com/index.html");
//URL oracle = new URL("http://www.oracle.com/");
BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
很容易知道,上面的打印结果为网站的html代码。值得注意的是:官方教程第一行代码为:
URL oracle = new URL("http://www.oracle.com/");
我运行之后,发现什么也没有输出来。于是我打开浏览器,输入该网址http://www.oracle.com/
。浏览器重定位到https://www.oracle.com/index.html
网站。所以我把上面例子中的URL改为此链接,运行成功,输出html代码。
具体原因我没有分析,我猜应该是重定向的问题,而url的openStream并不能追踪重定向,所以返回空?
当然这是个人猜想,具体如果我找到原因,再贴出来。各位有谁清楚的话,可以留言或者@我,
canliture@outlook.com哦,谢谢 ^ _ ^。
使用URL类——创建连接
在成功创建URl对象之后,我们可以调用URL对象的
openConnection
方法来获得一个URLConnection
对象。或者它的特定协议的子类对象,例如java.net.HttpURLConnection
。
在我们使用
URLConnection
连接之前,我们可能会设置参数或者一些请求属性。当调用URLConnection.connect
方法的时候,
会初始化一条通信链路,下面是一个代码实例:
try {
URL myURL = new URL("http://example.com/");
URLConnection myURLConnection = myURL.openConnection();
myURLConnection.connect();
}
catch (MalformedURLException e) {
// new URL() failed
// ...
}
catch (IOException e) {
// openConnection() failed
// ...
}
代码中,通过调用openConnection()
方法,能够返回一个新创建的URLConnection对象。
我们并不总是需要显示地调用connect方法来建立连接,像getInputStream, getOutputStream,
等操作在必要的时候会隐式地执行连接。例如上面的代码并不需要openConnection
,却能读出请求内容,代码可以查看下一小结的代码示例(从URLConnection读)
在成功地连接之后,我们能使用URLConnection
对象来执行一些操作,像读,写等。下面的内容会有讲解。
URLConnection类——读写
URLConnection
类包含许多让你使用URL来在网络间通信的方法。URLConnection
是一个以HTTP为中心的类,意思就是说,它的许多方法只工作在HTTP形式的URL下。然而,大多数的URL协议能让你从连接中读写,这部分就讲解了读写这两部分。
从URLConnection读
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.net.*;
import java.io.*;
public class URLConnectionReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL("https://www.oracle.com/index.html");
URLConnection yc = oracle.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(
yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
注意这里的代码示例与上面的使用URL类——直接从URL读数据代码的对比。这里是显式地获取得以URLConnection
连接然后从连接中获取input Stream。其中通过调用getInputStream
,连接时隐式地建立。
直接从URL获取内容
,或者通过URLConnection
来读取内容,在实现功能上没有差别。但是URLConnection
用处更大,因为它还能在从URL读取内容的同时进行其它的操作,如写操作。
向URLConnection写
这里的“写”,HTTP中一般指请求参数
这里官方给了一个对HTML与服务器通信的简单例子:
许多HTMl页面包含表单(form)——文本域,或者其它的需要发给服务器端的填写字段。在你点击提交按钮表单的按钮时,浏览器会写入数据到URL。在另一端的服务器收到数据并处理,然后返回响应,通常是以一个新的页面的形式返回。
在许多情况下,提交表单的方式叫做”HTTP POST“,POST数据给服务端。这种方法叫做”POST to A URL“。服务端识别此POST请求,并读取客户端发来的数据。
Java 程序和服务端交互通常通过如下几个步骤:
- 1,
创建URL类的对象
(Create a URL)- 2,
从URL中获得一个URLConnection
(Retrieve the URLConnection object)- 3,
设置在此URLConnection连接能够进行输出(写)操作{ connection.setDoOutput(true); }
(Set output capability on the URLConnection)- 4,
打开URL资源的连接
(Open a connection to the resource)- 5,
从资源连接中获取一个输出流
(Get an output stream from the connection)- 6,
写内容到输出流中
(Write to the output stream)- 7,
关闭输出流
(Close the output stream.)
下面是一个简单的实践程序代码:
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
public class Main {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: java Reverse "
+ "http://<location of your servlet/script>"
+ " string_to_reverse");
System.exit(1);
}
String stringToReverse = URLEncoder.encode(args[1], "UTF-8");
URL url = new URL(args[0]);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
out.write("string=" + stringToReverse);
out.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(
connection.getInputStream() ) );
String decodedString;
while ((decodedString = in.readLine()) != null) {
System.out.println(decodedString);
}
in.close();
}
}
程序用法很简单,要以命令行的形式运行,因为要命令行参数来作为执行参数。下面以百度网址为例,运行例子。
- 假设代码文件为Main.java
- 查看程序代码容易知道,运行的时候,我们需要加一个运行参数“string_to_reverse”
- D:\src>javac Main.java
- D:\src>java Main http://www.baidu.com/ string_to_reverse
对于咱们中文用户来说,查看输出,有坑啊!!乱码!!
查询资料。知道,我们的输出流需要设置编码,代码改进如下:
/**
.....
out.close();
**/
BufferedReader in = new BufferedReader(
new InputStreamReader(
connection.getInputStream(), "UTF-8") );
/**
String decodedString;
.....
**/