005-Java网络编程
Java网络编程
- 可以让设备中的程序与网络上其他设备中的程序进行数据交互的技术(实现网络通信)
- 基本通信架构
- 基本通信架构有两种形式:
- CS架构(Client客户端/Service服务端)、BS架构(Browser浏览器/Service服务端)
- CS架构客户端和服务端都需要程序员开发
- BS架构只有服务端需要程序员开发
- 基本通信架构有两种形式:
网络编程三要素
- IP地址:设备在网络中的地址,是设备在网络中的唯一标识
- 端口:应用程序在设备中的唯一标识
- 协议:连接和数据在网络中传输的规则
IP地址
-
IP:全称“互联网协议地址”,是分配给上网设备的唯一标识
-
目前,被广泛采用的IP地址形式有两种:IPv4和IPv6
-
IPv4
-
IPv4是Internet Protocol version 4的缩写,它使用32位地址,通常以点分十进制表示
-
-
IPv6
-
IPv6是Internet Protocol version 6的缩写,它使用128位地址,号称可以为地球上每一粒沙子编号
-
IPv6分成8段,每段每位编码成一个十六进制位表示,每段之间用冒号(:)分开,将这种方式成为冒分十六进制
-
-
IP域名(Domain Name)
- 用于在互联网上识别和定位网站的人类可读的名称
- 例如:www.baidu.com
- 用于在互联网上识别和定位网站的人类可读的名称
-
DNS域名解析(Domain Name System)
-
是互联网中用于将域名转换为对应IP地址的分布式命名系统。它充当了互联网的“电话簿”,将易记的域名映射到数字化的IP地址,使得用户可以通过域名来访问网站和其他网络资源
-
-
公网IP、内网IP
- 公网IP:是可以连接到互联网的IP地址;
- 内网IP:也叫局域网IP ,是只能组织机构内部使用的IP地址;例如,192.168.开头的就是常见的局域网地址,范围为192.168.0.0--192.168.255.255,专门为组织机构内部使用
-
本机IP
- 127.0.0.1、localhost:代表本机IP,只会寻找当前程序所在系统
-
IP常用指令
- ipconfig:查看本机IP地址
- ipconfig /all:更详细的查看ip地址
- 物理地址(MAC IP):电脑网卡的IP,不换网卡不会变
- ping IP地址:检查网络是否连通
-
Java中的InetAddress类
-
代表IP地址
-
常用方法
-
端口
- 用来标记正在计算机设备上运行的应用程序,被规定为16位的二进制,范围是0~65535
- 端口分类
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTPzhanyong 80,FTP占用21)
- 注册端口:1024~49151,分配给用户进程或某些应用程序
- 动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配
- 注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则报错
协议
-
通信协议:网络上通信的设备,实现规定的连接规则,以及传输数据的规则被称为网络通信协议
-
开放式网络互联标准:OSI网络参考模型
-
OSI网络参考模型:全球网络互联标准(理论)
-
TCP/IP网络模型:事实上的国际标准(实际)
-
-
传输层的两个通信协议
-
UDP(User Datagram Protocol):用户数据报协议
- 特点:无连接、不可靠通信
- 不识闲简历连接,数据按照包发,一包数据包含:自己的IP\端口、目的地IP、端口和数据(限制在64KB内)等
- 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不确认,故是不可靠的
- 使用场景:视频直播、语音通话
- 优点:通信效率高
-
TCP(Transmission Control Protocol):传输控制协议
-
特点:面向连接、可靠通信
-
TCP的最终目的:要保证在不可靠的信道上实现可靠的数据传输
-
TCP主要有三个步骤实现可靠传输:三次握手简历连接,传输数据进行确认,四次挥手断开连接
-
三次握手建立可靠连接
-
可靠连接:确保通信的双方收发消息都是没问题的(全双工)
-
第一次:确保服务端能收;第二次确保能发;第三次连接
-
传输数据会进行确认,以保证数据传输的可靠性
-
-
四次挥手断开连接
-
目的:确保通信的双方收发消息都已经完成
-
-
使用场景:网页、文件下载、支付
-
优缺点
- 优点:可靠性更高
- 缺点:通信效率相对不高
-
-
UDP通信
-
UDP通信的实现
-
特点:无连接、不可靠通信
-
不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP等信息封装成一个数据包,发出去就不管了
-
Java提供了一个
java.net.DatagramSocket
类来实现UDP通信 -
DatagramSocket:用于创建客户端、服务端
-
DatagramPacket:创建数据包
-
-
示例
-
客户端
package com.mycode.demo1udp1; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; public class UdpClientDemo1 { public static void main(String[] args) throws Exception { System.out.println("===客户端启动了==="); // 创建发送端对象 DatagramSocket socket = new DatagramSocket(); // 创建数据包对象,封装要发送的数据 byte[] data = "我是客户端,我用UDP发个消息,发送到服务端".getBytes(); /** * public DatagramPacket(byte[] buf, int offset, int length, * InetAddress address, int port) { * setData(buf, offset, length); * setAddress(address); * setPort(port); * } * buf: 数据 * offset: 数据的起始索引 * length: 数据的长度 * address: 数据接收方的地址 * port: 数据接收方的端口 */ DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 8080); // 让发送端对象发送数据包数据 socket.send(packet); // 用完关掉,节省通讯资源占用 socket.close(); } }
-
服务端
package com.mycode.demo1udp1; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; public class UDPServerDemo1 { public static void main(String[] args) throws Exception { // 创建服务端对象 DatagramSocket socket = new DatagramSocket(8080); // 创建数据包对象,用来接收数据 byte[] buf = new byte[1024*64]; DatagramPacket packet = new DatagramPacket(buf, buf.length); // 接收数据,将数据封装到数据包对象的字节数组中去 socket.receive(packet); // 看看数据是否收到了 int len = packet.getLength(); String data = new String(buf, 0, len); System.out.println("服务端收到数据:" + data); // 获取对方的IP对象和程序端口 InetAddress address = packet.getAddress(); int port = packet.getPort(); System.out.println("对方IP:" + address.getHostAddress()); System.out.println("对方端口:" + port); } }
-
先运行服务端
-
结果
-
-
多收多发
-
多开实例设置
- 设置后可以一个类运行多个终端
-
步骤
- 常见DatagramSocket对象并指定端口(接收端对象)
- 创建DatagramPacket对象接收数据(数据包对象)
- 使用DatagramSocket对象的receive方法传入DatagramPacket对象
- 使用while死循环不断进行第三步
-
TCP通信
-
特点:面相连接、可靠通信
-
通信是双方事先会采用“三次握手”方式简历可靠连接,实现端到端的通信;底层能保证数据成功传给服务端
-
Java提供了一个
java.net.Socket
类来实现TCP通信 -
TCP通信的实现一发一收-客户端开发
-
客户端程序就是通过java.net下的Socket类来实现的
-
服务端是通过java.net包下的ServerSocket类来实现的
-
示例
-
使用特殊字节流可以传输不同类型的内容
-
客户端
-
服务端
-
-
步骤
- 创建ServerSocket对象,注册服务端端口
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
- 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
- 释放资源:关闭socket管道
-
-
使用TCP通信实现:多发多收消息
-
客户端使用死循环 ,让用户不断输入信息
-
服务端也使用死循环控制服务端程序收完消息后,继续去接收下一个消息
-
-
同时接收多个客户端的消息
-
服务端只有一个主线程时,只能处理一个客户端的消息
-
TCP通信-支持与多个客户端同时通信
-
步骤
-
创建服务端ServerSocket对象,绑定端口号,监听客户端连接
-
while死循环
-
调用accept方法,阻塞等待客户端连接,一旦有客户端连接会返回一个Socket对象
-
把这个客户端管道交给一个独立的子线程专门负责接收这个管道的信息
-
对于上面这步,要定义一个线程类,每接一个管道要交给线程处理
- run方法不能抛异常,要try-catch
-
-
-
其他应用:B/S架构的原理
-
B/S架构原理
-
注意:服务器必须给浏览器相应HTTP协议规定的数据格式,否则浏览器不识别返回的数据
-
HTTP协议规定:响应给浏览器的数据格式必须满足如下格式
-
示例
-
注意事项:每个请求单独开一个线程在高并发的情况下容易爆炸
-
解决方法:使用线程池优化
-
示例
-
-
项目前置知识
获取时间的方案
-
JDK8之前:Date获取此刻时间
- 线程不安全,如果日期对象被修改,那么所有时间都被修改了,已淘汰
Date d = new Date(); // 格式化 // EEE代表周几,a代表上午还是下午 SimpleDataFormat sdf = new SimpleDataFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
-
JDK8开始:LocalDate 、LocalTime 、LocalDateTime
LocalDateTime的常用API
LocalDateTime now = LocalDateTime.now(); now; now.getYear(); now.getDayOfYear(); // 格式化 // EEE代表周几,a代表上午还是下午 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss EEE a"); String result2 = dtf.format(now); System.out.println(result2);
字符串的高效操作方案:StringBuilder
-
定义字符串可以用String类型,但操作字符串还是建议使用StringBuilder
-
虽然StringBuilder操作字符串便捷,但操作完后还是要用toString方法转回字符串
-
用
+
拼接字符串,如果是大量拼接,效率极低- 原因:String的对象是不可变变量,共享数据性能可以,但是修改数据性能差。在拼接时,原先的字符串不会被修改,而是会产生一个新字符串,然后该字符串名会指向新字符串。
-
StringBuilder对象是可变内容的容器,
BigDecimal
- 小数运算失真问题
-
BigDecimal用于解决浮点型运算时,出现结果失真的问题
-
构造器和常用功能
-
如果直接用构造器,必须使用那个字符串参数的构造器才能解决失真问题(转成字符串才能做切割,把整数部分和小数部分切开)
-
上面这点的优化方案:直接调用valueOf方法,内部使用的就是字符串参数的构造器,可以直接传double
-
示例
-
关于除法,需要自己设置精度,以下是保留两位,四舍五入的案例
- 还有UP(进位),FLOOR(舍弃进位)
-