Java网络基础
计算机网络
将分布在不同地理区域的计算机,通过一些外部网络设备以及内部的网络协议,连接成一个网络系统;通俗的理解为2台或以上的计算机协同工作,例如:文件传输,数据传输。计算机网络的核心目的是实现:信息共享
网络分类
根据网络规模以及用途分为:
- 局域网(0-10km,通过网络设备有线连接)
- 城域网(0-100km,交通信号,视频监控)
- 广域网(因特网:互联网)
根据网络的工作模式分为:
- 专有服务器(只提供专一某一种服务器,如:云主机,数据库专有服务器,静态资源文件的专有服务)
- 客户机服务器模式(c/s架构)
- 对等式网络(peer to peer)
网络模型与协议
计算机网络之间实现通信需要两种设备支持:
- 硬件设备:(网卡,网关:交换机,路由器)
- 软件设备:(网络通信协议:TCP/IP、UDP)
网络协议
网络协议就是计算器网络设备相互之间通信的标准(暗号),在互联网标准组织的推动下,将网络协议分为两种:
- TCP/IP : 传输控制协议/ip协议
传输控制协议,是一个安全可靠的互联网协议,需要通信的主机之间需要先建立正确的链接,才能够进行通信 - UDP:用户数据报协议
是一个不安全的网络协议,不需要双方之间建立联系,也不保证信息传输的有序性(有可能后发消息先到),传输效率比TCP/IP更高.没有专门服务器和客户端,只有发送端和接收端。
IP
Internet Protocol(因特网协议),主机之间通信的唯一标识,每台计算机都已个唯一的ip地址;ip又划分为IPv4和IPv6
- IPv4由4个字节构成的一段地址(每个字节最大不能超过255,范围0~255,最大取值只能到40+亿个)
- IPv6由16个字节构成
1字节=8个二进制位
4字节=32位
16字节=128位
ip地址需要确保在同一个网络中不可重复,一旦重复则会出现:ip冲突
ip地址通常分为5类:
A. (1.0.0.0 到127.0.0.0)
B. (128.1.0.0–191.254.0.0)
C. (192.0.1.0–223.255.254.0) 民用**
D. (224.0.0.0到239.255.255.255) 广播
E.(240.0.0.0到255.255.255.254,255.255.255.255)
端口(门牌号)
端口(port)是主机中应用程序对外的沟通的唯一标识;ip是主机的标识,端是应用的标识;因此如果需要准确的寻找到主机中的应用,则需要同时提供ip和端口。
端口分为TCP/IP、UDP
取值范围从 0 ~ 65535之间;但是由于0~1023之间的端口密集绑定了一些已有的服务,所以应该避免在创建自定义服务时使用这些端口;自定义的服务建议选择范围:
- 1024~49151之间
InetAddress类
IndetAddress是位于java.net包中提供的用于表示ip地址和主机的类,常用方法:
- getLocalhost() 获取本地主机
- getByName() 根据提供的主机名或者ip获取InetAddress对象
- getHostAddress() 获取主机地址
- getHostName() 获取主机名称
基于TCP/IP的Socket通信
TCP/IP协议是一种面向流的全双工通信协议,有着服务器客户端的概念,必须保证通信双方建立稳定安全的连接才能够进行数据传输;
Socket(套接字),实际上就是由IP地址跟端口号的结合,通过Socket对象可以实现两台主机之间的通信;Socket分为服务端Socket(java.net.ServerSocket),以及客户端Socket(java.net.Socket)
示例代码:
/**
* 服务器
* @author mrchai
*
*/
public class Server {
public static void main(String[] args) throws IOException {
//创建服务
// ServerSocket server = new ServerSocket();
// //将服务绑定到指定的主机以及指定端口
// server.bind(new InetSocketAddress(8888));
//占据指定的端口创建一个服务(创办一家银行:还未开业)
ServerSocket server = new ServerSocket(8888);
System.out.println("服务已开启,等待连接...");
while(true) {
//开启监听
Socket s = server.accept();
InetAddress ip = s.getInetAddress();
System.out.println(ip.getHostName()+"==="+ip.getHostAddress());
//获取基于Socket的输出流
OutputStream os = s.getOutputStream();
//将节点流包装打印流
PrintWriter out = new PrintWriter(os);
out.println("欢迎使用SOFTEEM服务器!!!");
out.flush();
s.close();
}
}
}
/**
* 客户端
* @author mrchai
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//连接到指定地址,指定端口的服务
Socket s = new Socket("192.168.7.101",8888);
//从socket中获取输入流
InputStream is = s.getInputStream();
//将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(is);
//将字符节点流包装为缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
//读取一行文本
String msg = br.readLine();
System.out.println("服务端:"+msg);
}
}
基于UDP的Socket通信
UDP协议不是一种基于稳定连接的协议,是一种面向数据报包的通信协议,不需要通信双方建立稳定的连接,也没有所谓服务端和客户的概念,数据报包在传输的时候不保证一定及时到达,也不能保证数据报包的到达顺序,但是UDP协议传输效率要远高于TCP/IP,比如直播,视频会议。
DatagramSocket&DatagramPacket
DatagramSocket类是一个基于UDP通信协议的套接字,使用该类可以实现一个无连接的通信通道;需要实现在该通道通信,我们还需要另一个类的辅助:DatagramPacket(数据报包),该类用于将数据打包成数据报包,然后通过DatagramSocket完成数据报包的发送(send)与接收(receive)
示例代码:
/**
* 发送方
* @author mrchai
*
*/
public class Sender {
public static void main(String[] args) throws IOException {
//创建一个数据报的网络通道(创建物流公司)
DatagramSocket ds = new DatagramSocket();
//准备需要传输的数据(货物)
String msg = "天王盖地虎,小鸡炖蘑菇!!!";
//将需要发送的数据打包成数据报包(打包货物)
DatagramPacket packet = new DatagramPacket(
msg.getBytes(), //需要被发送的数据的字节数组
msg.getBytes().length, //发送的数据长度(字节数组长度)
InetAddress.getByName("localhost"), //接收方的ip
1025 //接收方的端口
);
//发送数据报包(投递包裹)
ds.send(packet);
//关闭通道
ds.close();
}
}
/**
* 接收方
* @author mrchai
*
*/
public class Receiver {
public static void main(String[] args) throws IOException {
//创建一个数据报的网络通道,绑定到指定端口
DatagramSocket ds = new DatagramSocket(1025);
//声明字节数组,用于存储接收的数据
byte[] b = new byte[1024];
//准备数据报包(空包)
DatagramPacket packet = new DatagramPacket(b, b.length);
//通过循环可以不断接收发送到当前地址和端口的数据报包
while(true) {
//接收数据到数据报包中
ds.receive(packet);
//接受到的数据(字节数组)实际长度
int len = packet.getLength();
//将字节数组转换为字符串
String s = new String(b, 0, len);
//获取发送方的ip地址
String ip = packet.getAddress().getHostAddress();
System.out.println("收到来自【"+ip+"】消息:"+s);
}
}
}
UDP数据广播
java.net包中提供了用于实现UDP数据广播的Socket类:java.net.MulticastSocket,该类从DatagramSocket继承而来,可以实现广播消息的发送:
/**
* 广播发送方
* @author mrchai
*
*/
public class BoradcastSender {
public static void main(String[] args) throws IOException {
//创建广播通道
MulticastSocket ms = new MulticastSocket();
//获取广播地址(D类地址)
InetAddress addr = InetAddress.getByName("228.5.6.7");
//将通道加入组播地址
ms.joinGroup(addr);
String msg = "*************";
//将消息打包成数据报包
DatagramPacket dp = new DatagramPacket(
msg.getBytes(),
msg.getBytes().length,
addr,
5555);
//发送
ms.send(dp);
ms.close();
}
}
/**
*广播接收方
* @author mrchai
*
*/
public class BroadcastReceiver {
public static void main(String[] args) throws IOException {
//创建广播通道,并绑定端口5555
MulticastSocket ms = new MulticastSocket(5555);
//获取广播地址(D类地址)
InetAddress addr = InetAddress.getByName("228.5.6.7");
//将通道加入组播地址
ms.joinGroup(addr);
//声明字节缓冲区
byte[] b = new byte[1024];
//基于字节缓冲区构建数据报包(空包)
DatagramPacket dp = new DatagramPacket(b, b.length);
System.out.println("准备接收通知...");
while(true) {
//接收数据到数据报包中
ms.receive(dp);
//获取读取到的数据报长度
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String s = new String(b,0,len);
System.out.println("接收到来自"+ip+"的通知消息:"+s);
}
}
}
Http协议
URL 类
URL(统一资源定位器),一般用于表示一个网络地址(本地网,外部网络),通过该地址可以定位到网络中的资源。
URL的案例:
- http://www.softeem.com:80/sales/home.html?page=1
- ftp://119.231.8.19:23
- jdbc:mysql://192.168.0.1:3306/test?user=root&password=123456
一个URL地址通常由以下几个部分构成:
- 协议(双方约定的通信标准: http:// ftp:// jdbc:mysql://)
- 主机地址或域名(资源所在服务器地址:softeem.com 119.231.8.9 192.168.0.1)
- 端口(服务器中指定服务对外数据交换接口,唯一:80 3306)
- 请求资源(服务器根地址中资源所在的相对位置:/sales/home.html test)
- 查询路径(?之后的内容:page=1 user=root&password=123456)
URL类中提供的部分方法:
//根据提供的url地址创建一个URL对象
URL url = new URL("http://www.softeem.com:80/doc/index.html?offset=5&limit=10");
//获取当前地址所表示的协议
String protocol = url.getProtocol();
System.out.println(protocol);
//获取主机地址
String host = url.getHost();
System.out.println(host);
//获取端口号
int port = url.getPort();
System.out.println(port);
//获取该URL请求的所有内容包括查询路径
String file = url.getFile();
System.out.println(file);
//获取URL的请求路径部分(不含查询路径)
String path = url.getPath();
System.out.println(path);
//获取查询部分
String query = url.getQuery();
System.out.println(query);
Http概述
Http(Hyper Text Transfer Protocol)超文本传输协议(not only text);是一个基于TCP/IP的应用层协议,一般用于WWW服务器进行数据传输的一种协议,http是一种请求响应协议,即由客户端通过http协议发起请求,由http服务器提供响应;http是一种无状态的短链接协议。
由于HTTP协议是基于TCP/IP的,因此客户端和服务端之间通信都是面向字节流通信机制
Http请求的构成
一个http请求过程通常由两个部分组成:
- 请求头(客户端请求到服务器时传输一些配置信息以及书资源)
- 响应头(服务端响应客户端时传递到客户端的状态信息)
请求头
请求体 | 是否必选 |
---|---|
GET/POST [请求资源的URL路径] HTTP/[HTTP版本] | 是 |
Host: [URL主机] | 是 |
User-Agent: [请求类型唯一标识] | 否 |
Accept: [设置服务器返回的数据类型] | 否 |
Accept-Language:[设置服务器返回的语言] | 否 |
Accept-Encoding: [设置服务器返回的压缩编码] | 否 |
Accept-Charset: [设置服务器返回的文字编码] | 否 |
\r\n | 是 |
Post内容 | 否 |
请求头中常见的请求方式有
- POST
- GET
- DELETE
- PUT
- HEAD
响应头
回复体 | 是否必须返回 |
---|---|
HTTP/[HTTP版本] [HTTP状态码] | 是 |
Date: [服务器时间] | 否 |
Server: [服务器类型] | 否 |
Content-Type: [返回的数据流内容类型] | 否 |
Content-Length:[返回的数据流内容长度] | 否 |
Content-Encoding:[返回的数据流压缩编码] | 否 |
Accept-Charset: [设置服务器返回的文字编码] | 否 |
\r\n | 是 |
回复体内容 | 否 |
http响应头中比较值得关注的是响应的状态码。
- 1XX 消息
- 2XX 响应成功
- 200 服务端响应成功
- 3XX 重定向,请求被重新定向到其他目标
- 4XX 来自客户端的请求错误
- 404 Not Found 请求的资源不存在
- 405 请求和响应的方式不一致(客户端发送get请求,但是服务端使用post接收)
- 5XX 服务器错误
- 500 服务器内部错误(程序异常)
JSON数据格式
HttpURLConnection
HttpURLConnection用于获取一个http连接,是一个http客户端工具类,HttpURLConnection从URLConnection继承而来,通过该类可以读取服务端响应的数据(文本,视频,音频,图片等资源)。
public class HttpConnDemo {
public String loadData(String url) {
//Callable&FutureTask
return null;
}
//练习:将以下地址表示的音乐文件下载到本地
//http://music.softeem.top/musics/1592383934192.mp3
public static void main(String[] args) {
// new Thread() {
// public void run() {
// };
// }.start();
//使用线程启动网络请求
new Thread(()->{
try {
// String path = "http://192.168.7.141:9090/easyblog/index.html";
String path = "http://music.softeem.top/list";
//根据提供的url地址创建一个url对象
URL url = new URL(path);
//打开连接(强转为HttpURLConnection)
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//打开连接
// conn.connect();
//设置请求方式(不设置默认使用GET)
conn.setRequestMethod("GET");
//获取服务端的响应码
int code = conn.getResponseCode();
//避免魔法值
if(code == HttpURLConnection.HTTP_OK) {
//从连接中获取输入流
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
while((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
}
//断开连接
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}) .start();
}
}
基于Http协议下载远程文件
/**
* 文件下载器
* @author mrchai
*/
public class FileDownloader {
public void download(String url,File dir) throws MalformedURLException, FileNotFoundException {
URL urlObj = new URL(url);
//获取文件名称
String path = urlObj.getPath(); // musics/1592383934192.mp3
int index = path.lastIndexOf("/");// 6
String fname = path.substring(index+1); // 1592383934192.mp3
//根据文件名组合目录获取本地文件的输出流
OutputStream os = new FileOutputStream(new File(dir,fname));
System.out.println("开始下载...");
//启动线程读写
new Thread(()-> {
HttpURLConnection conn = null;
InputStream is = null;
try {
//打开连接
conn = (HttpURLConnection)urlObj.openConnection();
conn.setRequestMethod("GET");
//获取响应状态码
int stateCode = conn.getResponseCode();
if(stateCode == HttpURLConnection.HTTP_OK) {
//获取连接中输入流
is = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
System.out.println("下载完成!!!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(os != null)os.close();
if(is != null)is.close();
if(conn != null)conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) throws MalformedURLException, FileNotFoundException {
// String url = "http://music.softeem.top/musics/1592383934192.mp3";
// String url = "http://192.168.7.141:9090/easyblog/mp3/WeAreYoung.mp3";
String url="https://vdept.bdstatic.com/366e32794a7965496b53655453614276/4b5a314d42746252/dff065116b75d4ef9129d7746e9ec38095b1d32cfaae43e648f2d7f1c3ee084e52394855eb32d106c6775955cf6abb2def3c701150f1ed387791e632bf442002.mp4?auth_key=1595566524-0-0-8e9d876380f34a9366e5b88aea39c832";
new FileDownloader().download(url,new File("d:/tempfile"));
}
}
JSON数据格式
JSON(JavaScript Object Notation)是一种与语言平台无关的数据交换格式,有着比XML更为轻量级的数据表现形式,是目前大多数互联网应用程序之间的数据交换格式的首选。
JSON数据格式从结构上分为三种类型:
- 标量
- 数组
- 对象
标量
标量即一个具体的值:数值,字符串,布尔等;如:10、softeem、true 都是标量
数组
也称之为序列,序列即一系列数据(类型一致)的集合,数组使用“[]”包裹,内部元素之间使用“,”隔开,比如:数值数组,字符串数组,复杂的对象数组
[2,4,6,8,0]
["kobe","curry","wade"]
[
{
"name":"黑龙江",
"citys":["哈尔滨","大庆"]
},
{
"name":"*",
"citys":["台北","*"]
}
]
对象
也称之为映射(Map),所谓映射即一个对象,包含在一对”{}“之间,内部的元素以键值对为结构组织,键和值之间使用“:”分隔,元素之间以“,”隔开
{
"id":10,
"name":"softeem",
"type":"normal",
"flag":true,
"langs":["java","c++","php"],
"group":{
id:1,
gname:"vip1"
}
}
JSON支持的数据类型
JSON数据格式支持的值类型:
- String 字符串
- Number 数值
- Array 数组
- Object 对象
- true/false 布尔值
- null 空值
JSON与Java对象相互转换
json作为一种数据交换格式,就避免不了与Java之间的相互转换,因此对于Json字符串和Java之间的转换,目前有一些开源的可选方案:
- FastJSON:阿里巴巴提供
- Gson:Google提供
- Jackson
- json-lib
FastJSON使用
fastJSON是由阿里巴巴开源的JSON库,号称全世界最快的json插件,常用方法:
- toJSONString(Object) 将一个Java对象转换为Json字符串
- parseObject(String json,Class<T> clazz) 将一个json字符串转换为Java对象
- parseArray(String json,Class<T> clazz) 将一个json字符串转换为Java集合