Java进阶-⽹络编程、Socket、函数式接⼝、常⽤的函数式接⼝
1.⽹络通信协议
⽹络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进⾏通信。这就好⽐在道路中⾏驶的汽车⼀定要遵守交通规则⼀样,协议中对数据的传输格式、传输速率、传输步骤等做了统⼀规定,通信双⽅必须同时遵守,最终完成数据交换。
TCP/IP协议:传输控制协议/因特⽹互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最⼴泛的协议。它定义了计算机如何连⼊因特⽹,以及数据如何在它们之间传输的标准。它的内部包含⼀系列的⽤于处理数据通信的协议,并采⽤了4层的分层模型,每⼀层都呼叫它的下⼀层所提供的协议来完成⾃⼰的需求。
2.协议分类
包中提供了两种常见的⽹络协议的⽀持:
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是⾯向连接的通信协议,即传输数据之前,在发送端和接收端建⽴逻辑连接,然后再传输数据,它提供了两台计算机之间可靠⽆差错的数据传输。
三次握⼿:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
第⼀次握⼿,客户端向服务器端发出连接请求,等待服务器确认。
第⼆次握⼿,服务器端向客户端回送⼀个响应,通知客户端收到了连接请求。
第三次握⼿,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所⽰。
完成三次握⼿,连接建⽴后,客户端和服务器就可以开始进⾏数据传输了。由于这种⾯向连接的特性,TCP协议可以保证传输数据的安全,所以应⽤⼗分⼴泛,例如下载⽂件、浏览⽹页等。
UDP:⽤户数据报协议(User Datagram Protocol)。UDP协议是⼀个⾯向⽆连接的协议。传输数据时,不需要建⽴连接,不管对⽅端服务是否启动,直接将数据、数据源和⽬的地都封装在数据包中,直接发
送。每个数据包的⼤⼩限制在64k以内。它是不可靠协议,因为⽆连接,所以传输速度快,但是容易丢失数据。⽇常应⽤中,例如视频会议、QQ聊天等。
3.⽹络编程三要素
协议
协议:计算机⽹络通信必须遵守的规则,已经介绍过了,不再赘述。
IP地址
IP地址:指互联⽹协议地址(Internet Protocol Address),俗称IP。IP地址⽤来给⼀个⽹络中的计算机设备做唯⼀的编号。假如我们把“个⼈电脑”⽐作“⼀台电话”的话,那么“IP地址”就相当于“电话号码”
IP地址分类
IPv4:是⼀个32位的⼆进制数,通常被分为4个字节,表⽰成 d 的形式,例如 192.168.65.100 。其中a、b、c、d都是0~255之间的⼗进制整数,那么最多可以表⽰42亿个。
IPv6:由于互联⽹的蓬勃发展,IP地址的需求量愈来愈⼤,但是⽹络地址资源有限,使得IP的分配越发紧张。
为了扩⼤地址空间,拟通过IPv6重新定义地址空间,采⽤128位地址长度,每16个字节⼀组,分成8组⼗六进制数,表⽰成
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每⼀粒沙⼦编上⼀个⽹址,这样就解决了⽹络地址资源数量不够的问题。
查看本机IP地址,在控制台输⼊:
ipconfig
检查⽹络是否连通,在控制台输⼊:
ping 空格 IP地址
ping 220.181.57.216
特殊的IP地址
本机IP地址: 127.0.0.1 、 localhost 。
端⼝号
⽹络的通信,本质上是两个进程(应⽤程序)的通信。每台计算机都有很多的进程,那么在⽹络通信时,如何区分这些进程呢?
如果说IP地址可以唯⼀标识⽹络中的设备,那么端⼝号就可以唯⼀标识设备中的进程(应⽤程序)了。
端⼝号:⽤两个字节表⽰的整数,它的取值范围是0~65535。其中,0~1023之间的端⼝号⽤于⼀些知名的⽹络服务和应⽤,普通的应⽤程序需要使⽤1024以上的端⼝号。如果端⼝号被另外⼀个服务或应⽤所占⽤,会导致当前程序启动失败。
常⽤端⼝号
(1)80端⼝⽹络端⼝⽹址后⾯默认带有
(2)数据库 mysql:3306 oracle:1521
(3)Tomcat服务器:8080
利⽤协议 + IP地址 + 端⼝号三元组合,就可以标识⽹络中的进程了,那么进程间的通信就可以利⽤这个标识与其它进程进⾏交互。
4.Socket通信
成员⽅法
(1)public InputStream getInputStream() :返回此套接字的输⼊流。
如果此Scoket具有相关联的通道,则⽣成的InputStream 的所有操作也关联该通道。关闭⽣成的InputStream也将关闭相关的Socket。
(2)public OutputStream getOutputStream() :返回此套接字的输出流。
如果此Scoket具有相关联的通道,则⽣成的OutputStream 的所有操作也关联该通道。关闭⽣成的OutputStream也将关闭相关的Socket。
(3)public void close() :关闭此套接字。
⼀旦⼀个socket被关闭,它不可再使⽤。关闭此socket也将关闭相关的InputStream和OutputStream 。
(4)public void shutdownOutput() :禁⽤此套接字的输出流。任何先前写出的数据将被发送,随后终⽌输出流。
基本通信实现:
服务器端:
public class TcpServer {
public static void main(String[] args) throws IOException {
//向系统要⼀个指定的端⼝
ServerSocket server = new ServerSocket(8888);
System.out.println("开始等待客户连接........");
//获取发送请求的socket对象
Socket s1 = server.accept();
//获取⽹络字节输⼊流
InputStream inputStream = s1.getInputStream();
/
/使⽤该流读取数据
byte[] bytes = new byte[1024];
int len = ad(bytes);
System.out.println(new String(bytes,0,len));
//获取⽹络字节输出流
OutputStream outputStream = s1.getOutputStream();
outputStream.write("收到了,谢谢".getBytes());
s1.close();
server.close();
}
}
客户端:
public class TcpClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
//获取⽹络字节输出流
OutputStream outputStream = OutputStream();
//使⽤该流向服务器发送数据
outputStream.write("你好,服务器".getBytes());
//获取⼀个⽹络字节输⼊流读取服务器发送的数据
InputStream inputStream = InputStream();
byte[] bytes = new byte[1024];
int len = ad(bytes);
System.out.println(new String(bytes,0,len));
//关闭socket
socket.close();
}
}
⽂件上传下载案例:
服务端:
public class TcpServer {
public static void main(String[] args) throws IOException {
//1.获取⼀个绑定好指定端⼝的服务器socket对象
ServerSocket server = new ServerSocket(8888);
while (true) {
//2.⽤accept获取到请求的socket客户端
Socket client01 = server.accept();
/*
使⽤多线程技术,提⾼程序的效率
有⼀个客户端上传⽂件,就开启⼀个线程,完成⽂件的上传
*/
new Thread(new Runnable() {
//完成⽂件的上传
@Override
public void run() {
try {
//3.获取⽹络字节输⼊流对象
InputStream is = InputStream();
//4.判断⽂件夹是否存在,不存在就创建⼀个
File file = new File("D:\\CreatNew");
if (!ists()) {
file.mkdirs();
socket通信报文格式}
/*
⾃定义⼀个⽂件命名规则,防⽌覆盖
规则:域名+毫秒值+随机数
*/
String filename = "itcast" + System.currentTimeMillis() + new Random().nextInt(99999) + ".jpg";
//5.创建⼀个本地字节输出流
FileOutputStream fos = new FileOutputStream(file + "\\" + filename);
//6.使⽤⽹络字节输⼊流读取数据
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
//7.通过本地流写⼊硬盘
fos.write(bytes, 0, len);
}
System.out.println("2222222222222");
//8.给客户回数据
OutputStream os = OutputStream();
os.write("上传成功".getBytes());
//9.释放资源
fos.close();
client01.close();
} catch (IOException e) {
System.out.println(e);
}
}
}).start();
}
}
}
客户端:
public class TcpClient {
public static void main(String[] args) throws IOException {
//1.创建本地字节输⼊流,获取要读取的数据源
FileInputStream fis = new FileInputStream("D:\\Baseball\\cai.jpg");
//2.创建⼀个socket对象
Socket socket = new Socket("127.0.0.1", 8888);
//3.获取⽹络字节输出流对象
OutputStream os = OutputStream();
//4.通过本地流读取数据
byte[] bytes = new byte[1024];
int len = 0;
while ((len = ad(bytes)) != -1) {
//5.通过⽹络流写到服务器上去
os.write(bytes, 0, len);
}
System.out.println("1111111111");
/
/发送结束标记
socket.shutdownOutput();
//6.读取服务端返回的数据
InputStream is = InputStream();
while ((len = is.read()) != -1){
System.out.println(new String(bytes,0,len));
}
//7.释放资源
fis.close();
socket.close();
}
}
BS案例分析:
服务端:
package basicpart.day01.BS.web;
import java.io.*;
import java.ServerSocket;
import java.Socket;
public class BSserver {
public static void main(String[] args) throws IOException {
//1.创建⼀个服务器
ServerSocket server = new ServerSocket(8888);
/*
浏览器解析服务器回写的html页⾯,页⾯中如果有图⽚,那么浏览器就会单独的开启⼀个线程,读取服务器的图⽚我们就让服务器⼀直处于监听状态,客户端请求⼀次,服务器就回写⼀次
*/
while (true) {
//2.获取请求的客户端对象
Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
//3.获取⽹络字节输⼊流
InputStream is = InputStream();
//4.把⽹络字节输⼊流对象转换为字符缓存输⼊流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.把客户端请求信息的第⼀⾏读取出来 GET /BS/web/index.html HTTP/1.1
String line = br.readLine();
System.out.println(line);
//6.把读取的信息进⾏切割,只要中间的部分BS/web/index.html
String[] arr = line.split(" ");
//7.再把路径前⾯的/去掉,进⾏截取从第⼀个字母截取到最后
String htmlpath = arr[1].substring(1);
//8.创建⼀个本地字节输⼊流,构造⽅法中绑定要读取的html路径
FileInputStream fis = new FileInputStream("D:\\JA\\Part1-basic\\src\\basicpart\\day01\\" + htmlpath);
//9.使⽤socket的⽹络字节输出流对象
OutputStream os = OutputStream();
//写⼊HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
//必须要写⼊空⾏,否则浏览器不解析
os.write("\r\n".getBytes());
//10.⼀读⼀写复制⽂件,把服务器读取的html⽂件回写到客户端
int len = 0;
byte[] bytes = new byte[1024];
while ((len = ad(bytes)) != -1) {
os.write(bytes, 0, len);
}
//释放资源
fis.close();
socket.close();
} catch (IOException e) {
System.out.println(e);
}
}
}).start();
}
}
}
客户端:
⽹页输⼊:127.0.0.1:8888/BS/web/index.html
5.函数式接⼝
概念
函数式接⼝在Java中是指:有且仅有⼀个抽象⽅法的接⼝。
函数式接⼝,即适⽤于函数式编程场景的接⼝。⽽Java中的函数式编程体现就是Lambda,所以函数式接⼝就是可以适⽤于Lambda使⽤的接⼝。只有确保接⼝中有且仅有⼀个抽象⽅法,Java中的Lambda才能顺利地进⾏推导。
@FunctionalInterface
public interface MyFunctionalInterface {
void method();
}
作为⽅法的参数的使⽤:
public class Demo {
public static void show(MyFunctionalInterface myInter){
}
public static void main(String[] args) {
//调⽤show⽅法,⽅法的参数是⼀个接⼝,所以可以传递接⼝的实现类
show(new MyFunctionalInterfaceImpl());
//调⽤show⽅法,⽅法的参数是⼀个接⼝,所以我们可以传递接⼝的匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("使⽤匿名内部类的打印");
}
});
//调⽤show⽅法,⽅法的参数是⼀个函数式接⼝,所以我们可以传递lambda表达式
//()内为⽅法的参数
show(()-> System.out.println("lambada表达式打印"));
}
}
⽇志性能优化案例:延迟打印字符串
public class DelayLambda {
public static void main(String[] args) {
String msgA = "hello";
String msgB = "world";
//lambda表达式有延迟执⾏的功能
//如果等级不为1,则不会执⾏method⽅法,不会拼接字符串
showLog(1, () -> msgA + msgB);
}
public static void showLog(int level, MyFunctionalInterface myInter) {
if (level == 1) {
}
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论