12.1 网络通信要素—IP和端口号
IP地址
InetAddress
:唯一的标识 Internet 上的通信实体。本地回环地址hostAddress
为127.0.0.1
,主机名hostName
为localhost
。IPV4:4个字节组成,以点分十进制表示,如192.168.0.1。
IPV6:16个字节组成,每个整数用四个十六进制位表示,用 :分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
端口号标识正在计算机上运行的进程,端口号为 16 位的整数 0~65535。客户端应用程序的端口号是随机分配的,且同一时刻一个端口号仅可被一个程序使用。
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口 80,FTP占用端口21)
- 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。 这些是默认的,可以自行修改。
- 动态/私有端口:49152~65535
当在连接网络时输入一个主机的域名后,计算机先到本机查询目的IP地址,没有时则向域名服务器(DNS) 请求将域名转化成IP地址,取得返回值后和主机建立连接。每个域名对应唯一IP
静态方法获取InetAddress实例:
public static InetAddress getLocalHost()
public static InetAddress getByName(String host)
InetAddress常用方法:
public String getHostAddress():返回 IP 地址
public String getHostName():获取此 IP 地址的主机名
public boolean isReachable(int timeout):测试是否可以达到该地址
12.2 网络通信要素—网络协议
通信协议分层思想:同层间可以相互通信、相邻的上层可以调用下层所提供的服务。各层间互不影响。
传输层协议:传输控制协议TCP(Transmission Control Protocol)
,用户数据报协议UDP(User Datagram Protocol)
。
TCP协议:
- 传输前“三次握手”建立点对点连接
- 必须先开启接收端,否则直接连接失败
- 传输完毕需释放已建立的连接,效率低
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接。数据报大小限制在64K内
- 发送时不管对方是否准备好,接收方收到也不确认,故是不可靠的,支持广播发送
- 发送数据结束时无需释放资源,开销小,速度快
Socket:网络通信其实就是Socket通信,客户端和服务器的每次通信使用不同的套接字。
public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定 IP 主机的指定端口号。
public Socket(String host,int port)
Socket类的常用方法:
public InputStream getInputStream()
public OutputStream getOutputStream()
public InetAddress getInetAddress()如果套接字是未连接的,则返回 null。
public InetAddress getLocalAddress()获取套接字绑定的本地地址。
public int getPort()此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。
public int getLocalPort()返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。
public void close()关闭此套接字。同时关闭该套接字的 InputStream 和 OutputStream。
public void shutdownInput()禁用此套接字的输入流
public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送
12.3 TCP网络编程
客户端Socket:
1.创建 Socket:若服务器响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
2.获取 Socket 的输入/出流: getInputStream()/getOutputStream()方法获得流
3.通过输入流读取服务器传来的信息,通过输出流将信息写入线程。
4.关闭 Socket:断开客户端到服务器的连接,释放线路
Socket s = new Socket(“192.168.40.165”,9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
out.close();
s.close();
// 可以先判断流是否为null,再关闭更安全
服务器必须事先建立一个等待客户连接的ServerSocket对象。accept()方法会返回一个 Socket 对象。服务器与客户端端口要一致
服务器Socket:
1.调用 ServerSocket(int port) :绑定到指定端口上。用于监听客户端的请求。
2.调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信Socket。 该方法会持续监听,只要收到连接请求就会执行
3.调用S getOutputStream() 和 getInputStream (),开始网络数据的发送和接收。
4.关闭ServerSocket和Socket对象
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len;
while((len = in.read(buf))!=-1)
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
12.4 UDP网络编程
UDP通过数据报套接字 DatagramSocket
发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。 数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号,无须建立连接。
DatagramSocket 类的常用方法:
public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择。
public DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地址,IP 地址由内核选择。
public void close()关闭此数据报套接字。
public void send(DatagramPacket p)从此套接字发送数据报包。
public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。此方法在接收到数据报前一直阻塞。如果信息比包的长度长,该信息将被截短。
public InetAddress getLocalAddress()
public int getLocalPort()
public InetAddress getInetAddress()如果套接字未连接,则返回 null。
public int getPort()返回此套接字的端口。如果套接字未连接,则返回 -1
DatagramPacket类的常用方法:
public DatagramPacket(byte[] buf,int length)用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构用来将长度为 length 的包发送到指定主机上的指定端口号。length参数必须小于等于 buf.length。
public InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
public int getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
public int getLength()返回将要发送或接收到的数据的长度。
UDP网络通信流程:
1. 建立发送端,接收端
2. 建立数据包
3. 调用Socket的发送、接收方法
4. 关闭Socket
发送端:
DatagramSocket ds = null;
try {
ds = new DatagramSocket(); // 广播,无需指定端口号
byte[] by = "hello,atguigu.com".getBytes();
DatagramPacket dp = new DatagramPacket(by, 0, by.length, InetAddress.getByName("127.0.0.1"), 10000); // 构造指向指定进程的数据包
ds.send(dp); // 发送数据包
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds != null)
ds.close();
}
接收端:
DatagramSocket ds = null;
try {
ds = new DatagramSocket(10000); // 绑定接收端口
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by, by.length); // 构造空数据包
ds.receive(dp); // 接受
String str = new String(dp.getData(), 0, dp.getLength()); //
System.out.println(str + "--" + dp.getAddress());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds != null)
ds.close();
}
12.5 URL编程
URL(Uniform Resource Locator)
:统一资源定位符,表示Internet 上某一资源的地址,迅雷种子就是一种URL。 其用来标识一个资源,而且还指明了如何定位到这个资源。浏览器通过解析 URL 查找相应的文件或其他资源。
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
协议 主机名 端口号 文件名 片段名 参数列表
#片段名:即锚点,例如看小说,直接定位到章节
参数列表:参数名=参数值&参数名= ;键值对写法,连接的服务器可以获取相应信息
URL类的构造器都声明抛出非运行时异常,必须对其进行处理。一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取相应属性。
URL 构造函数:
public URL (String spec):通过URL地址的字符串构造URL对象
public URL(URL context, String spec):通过基 URL 和相对 URL 构造一个 URL 对象。
public URL(String protocol, String host, String file); 例如:new URL("http", "www.atguigu.com", “download. html");
获取属性的方法:
public String getProtocol( ) 获取该URL的协议名
public String getHost( ) 获取该URL的主机名
public String getPort( ) 获取该URL的端口号
public String getPath( ) 获取该URL的文件路径
public String getFile( ) 获取该URL的文件名
public String getQuery( ) 获取该URL的查询名,即参数列表
若希望输出数据,例如向服务器端的 CGI (公共网关接口)程序发送一些数据,则必须先与URL建立连接,然后才能对其进行读写。
URL netchinaren = new URL ("http://www.atguigu.com/index.shtml");
URLConnectonn u = netchinaren.openConnection(); // 通过URL对象获取连接对象
u.connect(); // 进行连接
u.getInputStream(); // 获取输入流
u.disconnect(); // 使用完成后断开连接
URI、URL和URN的区别
URI(Uniform Resource Identifier)是统一资源标识符,用来唯一的标识一个资源。而URL(uniform resource locator)是统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。而URN(Uniform Resource Name)统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com
。也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI。
12.6 Jsoup网路爬虫与解析
String realUrl = String.format(url, productId, sortType, page);
Document doc = Jsoup.connect(realUrl)
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36")
.ignoreContentType(true)
.get();
String json = getCommentJson(doc.text());
JSONObject object = new JSONObject(json);
if (flag == 0) {
commentMaxPage = object.getInt("maxPage");
}
flag++;
Log.d("MainActivity", object.toString());
JSONArray array = object.getJSONArray("comments");
for (int i = 0; i < array.length(); i++) {
JSONObject jsonObject = (JSONObject) array.get(i);
if(!jsonObject.getString("content").contains("此用户未及时填写评价内容")){
Comment comment = new Comment();
comment.setId(jsonObject.getLong("id"));
comment.setContent(jsonObject.getString("content"));
comment.setCreation_time(jsonObject.getString("creationTime"));
comment.setScore(jsonObject.getInt("score"));
comment.setProduct_color(jsonObject.getString("productColor"));
comment.setProduct_size(jsonObject.getString("productSize"));
commentList.add(comment);
}
}
Thread.sleep(((long) (Math.random() * 2 + 2)) * 1000);
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context,"程序出现异常,请重新尝试",Toast.LENGTH_LONG).show();
}
}
// 获取HTML页面的Body部分
Document doc = null;
try {
doc = Jsoup.connect(String.format("https://item.jd.com/%s.html", productId)).get();
} catch (IOException e) {
e.printStackTrace();
}
Element body = doc.body();
Elements elements = body.select("div[class=w]");
// 获取图片
for (Element element : elements) {
// 爬取商品图片
image = element.select("div[class=product-intro clearfix]")
.select("div[class=preview-wrap]")
.select("div[class=preview]")
.select("div[id=spec-n1]")
.select("img[id=spec-img]").attr("data-origin");
if (image != null && image.length() > 0) {
break;
}
}
12.7 JSON
Java没有内置JSON的解析,因此需要借助第三方类库。以下教程基于 FastJson 讲解。
在 Maven 构建的项目中,在 pom.xml 文件中加入以下依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
从 Java 变量到 JSON 格式的编码过程如下:
public void testJson() {
JSONObject object = new JSONObject();
//string
object.put("string","string");
//int
object.put("int",2);
//boolean
object.put("boolean",true);
//array
List<Integer> integers = Arrays.asList(1,2,3);
object.put("list",integers);
//null
object.put("null",null);
System.out.println(object);
}
// {"boolean":true,"string":"string","list":[1,2,3],"int":2}
从 JSON 对象到 Java 变量的解码过程如下:
public void testJson2() {
JSONObject object = JSONObject.parseObject("{\"boolean\":true,\"string\":\"string\",\"list\":[1,2,3],\"int\":2}");
//string
String s = object.getString("string");
//int
int i = object.getIntValue("int");
//boolean
boolean b = object.getBooleanValue("boolean");
//list
List<Integer> integers = JSON.parseArray(object.getJSONArray("list").toJSONString(),Integer.class);
//null
System.out.println(object.getString("null"));
}
//从字符串解析JSON对象
JSONObject obj = JSON.parseObject("{\"runoob\":\"菜鸟教程\"}");
//从字符串解析JSON数组
JSONArray arr = JSON.parseArray("[\"菜鸟教程\",\"RUNOOB\"]\n");
//将JSON对象转化为字符串
String objStr = JSON.toJSONString(obj);
//将JSON数组转化为字符串
String arrStr = JSON.toJSONString(arr);
12.8 Java解析HTML
使用Jsoup.parse(String str)即可将获取到的HTML内容解析得到一个Documend类,剩下的工作就是从Document中选择我们需要的数据了。
<html>
<div id="blog_list">
<div class="blog_title">
<a href="url1">第一篇博客</a>
</div>
<div class="blog_title">
<a href="url2">第二篇博客</a>
</div>
<div class="blog_title">
<a href="url3">第三篇博客</a>
</div>
</div>
</html>
public class DataLearnerCrawler {
public static void main(String[] args) {
List<String> titles = new ArrayList<String>();
List<String> urls = new ArrayList<String>();
String html = "<html><div id=\"blog_list\"><div class=\"blog_title\"><a href=\"url1\">第一篇博客</a></div><div class=\"blog_title\"><a href=\"url2\">第二篇博客</a></div><div class=\"blog_title\"><a href=\"url3\">第三篇博客</a></div></div></html>";
//第一步,将字符内容解析成一个Document类
Document doc = Jsoup.parse(html);
//第二步,根据我们需要得到的标签,选择提取相应标签的内容
Elements elements = doc.select("div[id=blog_list]").select("div[class=blog_title]");
for( Element element : elements ){
String title = element.text();
titles.add(title);
urls.add(element.select("a").attr("href"));
}
//输出测试
for( String title : titles ){
System.out.println(title);
}
for( String url : urls ){
System.out.println(url);
}
}
}
/*
第一篇博客
第二篇博客
第三篇博客
url1
url2
url3
*/