《疯狂java讲义》读书笔记(八):网络编程
网络编程的一些基础在本篇读书笔记中较少涉及,在理解TCP和UDP的时候,最好还是有些知识储备,这学期学完了计算机网络,再看一遍书,又加深了印象。
关于网络编程这块儿,书上很多都是例子为主,在这一节的读书笔记,结合大二学的一些东西和大三学的一些专业课来写,并不是完全意义上的《疯狂java讲义》读书笔记。
在这节的读书笔记里,主要从3个方面去介绍,分别是网络基础、Java网络API以及基于TCP的网络编程,在李刚的这本书中,除了基于TCP的网络编程还讲了基于UDP的网络编程,有兴趣的读者可以自行去翻阅,这里不花篇幅去介绍了。
1.网络基础
学过计算机网络的话,对于TCP/IP协议、IP地址和端口以及域名和DNS应该都有所了解,可以直接看第二部分了。
1.1 网络类型
这部分很简单,无非是可以按照规模大小地理位置分为局域网、城域网和广域网,还有按照网络的拓扑结构可以分为星型网络、网状网络等知识内容。
1.2 TCP/IP协议
目前局域网采用的协议标准是TCP/IP协议。TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议,IMCP协议,TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等。电脑有了这些,就好像学会了外语一样,就可以和其他的计算机终端做自由的交流了。
1.3 IP地址和端口
IP地址是因特网上的主机和路由器所使用的地址,用于标识网编号的主机编号,IP地址被分为A-E 5类。A、B、C类比较常用。
端口号是应用程序和外界交流的出入口,用于表示数据交给哪个通信程序进行处理,意思就是说信息从主机A发到了目的主机B,主机B上运行着非常多的程序,而主机A的意图是想把信息发给主机B上的某一个程序,那么怎么区分这么多程序呢,就需要端口号来辨别。IP地址相当于门牌号,主机A想找到主机B,就得靠主机B的IP地址在大千世界里才能找到。
其中,我们常见的80端口号就是HTTP服务端口号,以后还会见到各种花里胡哨的端口号。
关于IP地址和端口号在计算机网络中会详细学,在这里不多说了。
1.4 域名
网络通信中,路由器使用定长且包含网络地址信息的IP地址进行路由。收发数据必须利用IP地址。IP地址不方便记忆,因此必须用更友好的、便于记忆的名字来代替数字 IP 地址"。这个名字就是域名。DNS是用于把域名解析成IP地址的。
2.Java网络API
2.1InetAddress类
该类适用于封装IP地址或域名的,没有构造方法所以不能直接创建对象。
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo {
public static void main(String[] args) {
try {
// 获取本机地址信息
InetAddress localIp = InetAddress.getLocalHost();
System.out.println("localIp.getCanonicalHostName()= "
+ localIp.getCanonicalHostName());
System.out.println("localIp.getHostAddress()= "
+ localIp.getHostAddress());
System.out.println("localIp.getHostName()= "
+ localIp.getHostName());
System.out.println("localIp.toString()= " + localIp.toString());
System.out.println("localIp.isReachable(5000)= "
+ localIp.isReachable(5000));
System.out.println("====================================");
// 获取指定域名地址信息
InetAddress baiduIp = InetAddress.getByName("www.baidu.com");
System.out.println("baiduIp.getCanonicalHostName()= "
+ baiduIp.getCanonicalHostName());
System.out.println("baiduIp.getHostAddress()= "
+ baiduIp.getHostAddress());
System.out.println("baiduIp.getHostName()= "
+ baiduIp.getHostName());
System.out.println("baiduIp.toString()= " + baiduIp.toString());
System.out.println("baiduIp.isReachable(5000)= "
+ baiduIp.isReachable(5000));
System.out.println("====================================");
// 获取指定原始IP地址信息
// InetAddress ip = InetAddress
// .getByAddress(new byte[] { 127, 0, 0, 1 });
InetAddress ip = InetAddress.getByName("127.0.0.1");
System.out.println("ip.getCanonicalHostName()= "
+ ip.getCanonicalHostName());
System.out.println("ip.getHostAddress()= " + ip.getHostAddress());
System.out.println("ip.getHostName()= " + ip.getHostName());
System.out.println("ip.toString()= " + ip.toString());
System.out.println("ip.isReachable(5000)= " + ip.isReachable(5000));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2URL类
protocal://host:port/resourceName
,这就是个URL的组成结构。
比如说http://book.mooc.cn/java-book.html
。默认端口80是可以省略的。
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo {
public static void main(String[] args) {
try {
URL mybook = new URL("https://blog.csdn.net/weixin_40992982");
System.out.println("协议protocol=" + mybook.getProtocol());
System.out.println("主机host =" + mybook.getHost());
System.out.println("端口port=" + mybook.getPort());
System.out.println("文件filename=" + mybook.getFile());
System.out.println("锚ref=" + mybook.getRef());
System.out.println("查询信息query=" + mybook.getQuery());
System.out.println("路径path=" + mybook.getPath());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
2.3URLConnection类
URLConnection代表与URL指定的数据源的动态链接。
2.4URLDecoder和URLEncoder类
涉及到普通字符串和application/x-www-form-urlencoded MIME字符串之间相互转换时需要使用URLDecoder和URLEncoder两个工具类。
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class URLDecoderDemo {
public static void main(String[] args) {
try {
// 将普通字符串转换成application/x-www-form-urlencoded字符串
String urlStr = URLEncoder.encode("Hello,这里是NayelyA", "GBK");
System.out.println(urlStr);
// 将application/x-www-form-urlencoded字符串 转换成普通字符串
String keyWord = URLDecoder.decode(
"Hello%2C%D5%E2%C0%EF%CA%C7NayelyA", "GBK");
System.out.println(keyWord);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
3.基于TCP的网络编程
java.net包中提供了网络编程所需要的类,其中基于TCP协议的网络编程主要使用两种Socket,一个是ServerSocket(服务器套接字)
还有一个是Socket(客户端套接字)
。
3.1 ServerSocket 类主要方法:
运行在服务器端,监听来自客户端的Socket连接。
Socket accept(); // 如果接受到一个客户端Socket的连接请求,该方法将会返回一个与客户端Socket对应的Socket。
ServerSocket(int port) // 用指定端口 port 来创建一个 ServerSocket ,0-65535。构造方法还有别的,在这里不全写出来了。
ServerSocket 使用完毕后应使用其 close方法关闭该 ServerSocket ,通常情况下,服务器不应该只接受一个客户端请求,所以 java 程序通常会通过循环不停调用和 accept() 方法。
使用ServerSocket进行网络通信的具体步骤。
1)根据指定的端口号来实例化一个ServerSocket对象
2)调用ServerSocket对象的accept()方法接收客户端发送的Socket对象
3)调用Socket对象的getInputStream()/getOutputStream()方法建立和客户端进行交互的IO流
4)关闭服务器端的Socket
5)回到第二步,继续监听
示例代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketDemo extends Thread {
// 声明一个ServerSocket
ServerSocket server;
// 计数
int num = 0;
public ServerSocketDemo() {
// 创建ServerSocket,用于监听28888端口是否有客户端的Socket
try {
server = new ServerSocket(28888);
} catch (IOException e) {
e.printStackTrace();
}
// 启动当前线程,即执行run()方法
this.start();
System.out.println("服务器启动...");
}
@Override
public void run() {
while (this.isAlive()) {
try {
// 接收客户端的Socket
Socket socket = server.accept();
// 将Socket对应的输入流包装成BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 读客户端发送的信息并显示
String line = br.readLine();
System.out.println(line);
// 将Socket对应的输出流包装成PrintStream
PrintStream ps = new PrintStream(socket.getOutputStream());
// 往客户端发送信息
ps.println("您是第" + (++num) + "个访问服务器的用户!");
ps.flush();
// 关闭
br.close();
ps.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new ServerSocketDemo();
}
}
3.2 Socket 类主要方法:
Socket(InetAddress/String remoteAddress, int port);
// 创建连接到指定远程主机,远程端口的 Socket,该构造器没有指定本地地址、本地端口,默认使用本地主机的默认 IP 地址,默认使用系统动态分配的端口。
Socket(InetAddress/String remoteAddress, int port, InetAddress localAddr, int localPort);
// 创建连接到指定远程主机、远程端口的 Socket, 并指定本地 IP 地址和本地端口,使用与本地主机有多个 IP 地址的情况。
InputStream getInputStream();
// 返回该 Socket 对象对应的输入流,让程序通过该输入流从Socket中取出数据。
OutputStream getOutputStream();
// 返回该 Socket 对象对应的输出流,让程序通过该输出流向Socket中输出数据。
Socket的两个构造方法都需要声明抛出IOException异常。使用Socket进行通信的步骤是:
1)根据指定IP地址和端口号创建一个Socket对象
2)调用getInputStream()或者getOutputStream()方法打开连接到Socket的输入/出流
3)关闭Socket
示例代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientSocketDemo {
public static void main(String[] args) {
try {
//创建连接到本机、端口为28888的Socket对象
Socket socket = new Socket("127.0.0.1", 28888);
// 将Socket对应的输出流包装成PrintStream
PrintStream ps = new PrintStream(socket.getOutputStream());
// 往服务器发送信息
ps.println("我是NayelyA");
ps.flush();
// 将Socket对应的输入流包装成BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 读服务器返回的信息并显示
String line = br.readLine();
System.out.println("来自服务器的数据:" + line);
// 关闭
br.close();
ps.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 Socket通信模型
图源网络。