今天内容
1.UDP发送端可以不断键盘录入数据,接收端不断展示数据,发送可以自定义结束条件 (课堂练习)
发送端
import javafx.scene.layout.BackgroundFill;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;
/**
-
@author 高圆圆
-
@date 2022/11/28 9:58
-
需求:
-
UDP发送端可以不断键盘录入数据,接收端不断展示数据,
-
发送端可以自定义结束条件 (课堂练习) */ public class UdpSend {
public static void main(String[] args) { //UDP发送端可以不断键盘录入数据
//创建发送端的socket DatagramSocket ds = null ; try { ds = new DatagramSocket() ; //键盘录入可以使用BufferedReader--->读一行 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ; String line = null ; //一次读一行内容 System.out.println("请您输一个数据"); while((line=br.readLine())!=null){ if("over".equals(line)){ break; } //创建数据报包,将数据存储在数据包中 DatagramPacket dp = new DatagramPacket(line.getBytes() ,line.getBytes().length, InetAddress.getByName("10.35.162.121"), 6666) ; //发送数据报包 ds.send(dp) ; } } catch (IOException e) { e.printStackTrace(); }finally { if(ds!=null){ ds.close() ; } }}}
接收端
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket;
/**
-
@author 高圆圆
-
@date 2022/11/28 10:09
-
接收端不断展示数据------->接收端模拟真实,不需要关闭 */ public class UdpReceive { public static void main(String[] args) {
try { //创建接收端的Socket对象 DatagramSocket ds = new DatagramSocket(6666) ; while(true){ //创建一个接收容器 //自定义一个字节数组缓冲区 byte[] buffer = new byte[1024] ; int bufferLength = buffer.length ; DatagramPacket dp = new DatagramPacket(buffer,bufferLength) ; //接收数据容器 ds.receive(dp) ; //解析接收容器中真实内容 byte[] bytes = dp.getData(); int length = dp.getLength(); //每次0开始读取字节数---转成String String receiveMsg = new String(bytes,0,length) ; //获取ip地址字符串 String ip = dp.getAddress().getHostAddress() ; //展示数据 System.out.println("data from-->"+ip+",conent is :"+receiveMsg); } } catch (IOException e) { e.printStackTrace(); }}}
2.TCP客户端和服务器端的代码实现
服务器端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Tcp_server { public static void main(String[] args) throws IOException { //创建服务端对象 ServerSocket ss=new ServerSocket(8888); System.out.println("服务器正在等待客户端连接请求..."); //监听客户端的连接 Socket socket=ss.accept(); System.out.println("客户端已连接"); //获取监听到的客户端的通道内的字节输入流对象,读数据 InputStream inputStream=socket.getInputStream(); //一次读取一个字节数组 byte[] bytes=new byte[1024]; int length=inputStream.read(bytes); //转换String String s=new String(bytes,0,length); //获取ip地址对象,同时打印出ip地址字符串形式 String ip=socket.getInetAddress().getHostAddress(); System.out.println("data from--->"+ip+"content is--->"+s); //服务器端反馈给客户端数据 //服务器可以获取监听客户端通道内字节输出流,写数据 OutputStream outputStream=socket.getOutputStream(); outputStream.write("已收到数据".getBytes()); //释放服务器端的资源 ss.close(); } }
客户端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class Tcp_client { public static void main(String[] args) throws IOException { //创建客户端对象,指定ip和端口 Socket socket=new Socket("127.0.0.1",8888); //获取客户端通道内容字节输出流对象,写数据 OutputStream outputStream=socket.getOutputStream(); outputStream.write("纵使苍天陨落,契约必须完成".getBytes()); //客户端获取通道字节输入流对象,读服务器的反馈的数据 InputStream inputStream=socket.getInputStream(); byte[] bytes=new byte[1024]; int length=inputStream.read(bytes); String s=new String(bytes,0,length); System.out.println(s); //释放资源 outputStream.close(); } }
3.TCP三次握手的原理--->SYNC+ACK
SYNC同步序列编号 ACK:消息确认字符(当客户端发送数据到服务器端,服务器需要 确认并反馈)
4.TCP的应用
4.1 TCP方式,客户端可以不断键盘录入数据,服务器端不断展示数据
//客户端 import java.io.*; import java.net.Socket;
//TCP客户端不断键盘录入数据,服务器端不断展示数据在控制台 public class Test5_client { public static void main(String[] args) throws IOException { //创建TCP客户端对象 Socket socket=new Socket("127.0.0.1",5555); //将录入的数据转为字符流输入对象 BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in)); //客户端输出的字节流转为字符流,通道内的字节流封装成字符流 BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //录入一行,输出一行 String s=null; while ((s=bufferedReader.readLine())!=null){ bufferedWriter.write(s); bufferedWriter.newLine(); bufferedWriter.flush(); } socket.shutdownOutput(); InputStream inputStream=socket.getInputStream(); byte[] bytes=new byte[1024]; int len=inputStream.read(bytes); System.out.println("录入结束"); //关闭资源 socket.close(); bufferedReader.close(); } } //服务端 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;
public class Test5_server { public static void main(String[] args) throws IOException { //创建服务端对象 ServerSocket serverSocket=new ServerSocket(5555); //监听客户端连接 Socket socket=serverSocket.accept(); //将读入的字节流输入数据封装成字符流,将通道内的字节流输入转为字节流输入 BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream())); //将读入的字符流数据输出到控制台 String line=null; while ((line=bufferedReader.readLine())!=null){ System.out.println(line); } serverSocket.close(); } }
4.2 TCP客户端的读取文本文件(将文本文件写入通道中)_,
服务器端复制文本文件到指定路径中(将文本文件写入指定文件中)
客户端
import java.io.*; import java.net.Socket;
/**
-
@author 高圆圆
-
@date 2022/11/28 14:27
-
需求:
-
TCP客户端的文本文件(当前项目下的UdpReceive.java文件)
-
分析:TCP创建字符缓冲输入流--->读UdpReceive.java文件 一次读一行
-
TCP客户端获取通道字节输入流--->封装字符缓冲输出流--->写一行---发给服务器端 * * */
public class TcpClientTest { public static void main(String[] args) throws IOException { //创建客户端的Socket对象 Socket s = new Socket("10.35.162.121",2222) ; //创建字符缓冲输入流对象 BufferedReader br = new BufferedReader( new FileReader("UdpReceive.java")) ;
//获取客户端通道的字节输出流---->包装成BufferedWriter:字符缓冲输出流 BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(s.getOutputStream())) ; //每次从.java文件读取一行,给通道流的流中写一行 String line = null ; while((line=br.readLine())!=null){ bw.write(line) ; bw.newLine(); bw.flush(); } System.out.println("文件读完,发送过去了..."); //释放资源 br.close(); s.close(); }}
服务端
import java.io.*; import java.net.ServerSocket; import java.net.Socket;
/**
-
@author 高圆圆
-
@date 2022/11/28 14:27
-
TCP服务器端将复制客户端的文件内容,复制到D://2211//day27_code_resource//Copy.java里面
-
分析:
-
TCP获取监听客户端的字节输入流--->封装字符缓冲输入流--->一读取一行
-
创建字符缓冲输出流--->写一行D://2211//day27_code_resource//copy.java到这个文件中 */
public class TcpServerTest { public static void main(String[] args) throws IOException { //创建服务器端的Socket对象 ServerSocket ss = new ServerSocket(2222) ; //监听客户端连接 Socket s = ss.accept(); //获取监听客户端所在的通道内字节输入流对象---->包装成字符缓冲输入流 BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream())) ;
//将监听客户端的通道内的字节流(已经被包装了字符缓冲输入流)的内容----通过的字符缓冲输出流写入到文件中 BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\EE_2211\\day27_code_resource\\copy.java")) ; //一次读取一行,写一行到文件中 String line = null ; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } System.out.println("复制完毕"); //释放资源 bw.close(); ss.close(); }}
-
4.3 TCP客户端读取文本文件,服务器端复制文本文件,现在服务器端反馈给客户端,"已经复制完毕"
服务端
import java.io.*; import java.net.ServerSocket; import java.net.Socket;
/**
-
@author 高圆圆
-
@date 2022/11/28 14:57 */
public class Tcp_ServerTest2 { public static void main(String[] args) throws IOException { //创建服务器端的Socket对象 ServerSocket ss = new ServerSocket(2222) ; //监听客户端连接 Socket s = ss.accept(); //获取监听客户端所在的通道内字节输入流对象---->包装成字符缓冲输入流 BufferedReader br = new BufferedReader( new InputStreamReader(s.getInputStream())) ;
//将监听客户端的通道内的字节流(已经被包装了字符缓冲输入流)的内容----通过的字符缓冲输出流写入到文件中 BufferedWriter bw = new BufferedWriter( new FileWriter("D:\\EE_2211\\day27_code_resource\\copy.java")) ; //一次读取一行,写一行到文件中 String line = null ; while((line=br.readLine())!=null){//阻塞式,null也是表示客户端的通道内的字节输入流读完了 /* if("over".equals(line)){ break; }*/ bw.write(line); bw.newLine(); bw.flush(); } //加入反馈,服务器端反馈给客户端数据 //获取字节输出流,写 OutputStream out = s.getOutputStream(); out.write("hello,文件复制完毕".getBytes()); out.flush(); //释放资源 bw.close(); ss.close(); }}
客户端
import java.io.*; import java.net.Socket;
-
发现问题:
-
两个端都出现了互相等待了
-
因为客户端的文本文件读完,是null作为结束条件,但是服务器端不断的从通道内的输入流去读数据,
-
两端的通道不知道文件是否完了(服务器端不知道客户端的文件完没完),等待着写数据过来,
-
解决方案:
-
通知服务器端,别等了,文件读完了
-
1)自定义结束条件 ,服务器端读到自定义结束条件,就反馈!
-
弊端:如果文件第一句话恰好是自定义的结束条件,就不好
-
2)推荐:在客户端这边有个终止通道内的流 没有数据写过去了!禁止输出流输出!
-
public void shutdownOutput() throws IOException
public class Tcp_ClientTest2 { public static void main(String[] args) throws IOException { //创建客户端的Socket对象 Socket s = new Socket("10.35.162.121",2222) ; //创建字符缓冲输入流对象 BufferedReader br = new BufferedReader( new FileReader("UdpReceive.java")) ;
//获取客户端通道的字节输出流---->包装成BufferedWriter:字符缓冲输出流 BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(s.getOutputStream())) ; //每次从.java文件读取一行,给通道流的流中写一行 String line = null ; while((line=br.readLine())!=null){//阻塞式方法.一直等待为null文件完毕 bw.write(line) ; bw.newLine(); bw.flush(); } //写一个数据 //通道内的流写一句
/* bw.write("over"); bw.newLine(); bw.flush();*/
//方案2: public void shutdownOutput() throws IOException s.shutdownOutput();
//客户端要读取服务器反馈数据 //获取通道内的字节输入流 InputStream inputStream = s.getInputStream(); //读取一个字节数组 byte[] bytes = new byte[1024] ; int len = inputStream.read(bytes); System.out.println("客户端接收到了反馈数据:"+new String(bytes,0,len));
//释放资源 br.close(); s.close(); }
}
5.反射(前期了解,二阶段学习Servlet服务器端程序开发,都是反射)
咱们之前写代码:类名 对象名 = new 类名() ;考虑运行阶段 Runtime 反射里面研究的类的编译阶段: Class---->编译的过程 1.反射中如何获取类字节码文件对象 2.反射中是如何通过构造器创建对象呢? 3.通过反射创建对象之后,在去给成员变量赋值 4.通过反射创建对象之后,调用类的成员方法..
'什么是反射?
-
编译某个类的时候---->获取这个类的字节码文件,然后去加载,调用里面的成员方法,访问成员变量,
-
通过构造方法创建对象! *
-
编译过程:
-
对这个类的属性/成员/构造其进行校验---->类加载器(负责类的加载过程)
-
BoostrapClassLoader:启动类加载器,负责java核心库,rt.jar *
-
如何获取一个类的字节码文件?
-
java.lang.Class:代表正在运行的java类或者接口,通过静态方法
-
public static Class forName(String classname):
-
参数:代表就是当前类或者接口的全限定名称 (包名.类名)
-
String---->全限定名称 java.lang.String
-
Person---->自定义的---->全限定名称:com.qf.reflect_06.Person
2.通过反射方式获取类的字节码文件之后,创建当前类对象!
-
之前的写法:考虑运行的代码 类名 对象名 = new 类名() ;
-
1)获取类的字节码文件对象
-
2)获取类的Constructor所在的构造器对象
-
3)通过它创建当前类实例
2)获取指定的构造方法所在的Constructor类对象** //public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取指定的公共的构造方法所在Constructor实例,参数--->参数类型的Class字节码文件对象 //参数是String----->String.class----结果 java.lang.String**
3)通过它创建当前类实例** //public T newInstance(Object... initargs):参数就给构造函数中参数进行实际赋值
2)获取构造器Constructor类对象--->带两个参数的
//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //参数里面是参数类型的字节码文件对象
AccessibleObject类提供Field(成员变量的类对象)/Method(成员方法类对象)/Constructor(构造器类对象)的基类 //提供功能:取消Java语言访问检查,暴力访问 //public void setAccessible(boolean flag) true:就是抑制Java语言访问检查功能
public class ReflectDemo2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取当前类字节码文件 Class clazz=Class.forName("Reflect.Person"); //创建对象,获取当前类的构造函数 Constructor con=clazz.getConstructor(); //创建对象,获取当前类的有参构造器 Constructor con1=clazz.getDeclaredConstructor(String.class,int.class); System.out.println(con); con1.setAccessible(true); Object obj=con.newInstance("达达利亚",20); System.out.println(obj); } }