(十四)Core Java 网络编程(TCP客户端并发登录,自定义服务器,自定义浏览器,域名解析)-02 (111)
2018-03-23 09:18
597 查看
目录 :
12 ) . 网络编程(TCP复制文件)
13 ) . 网络编程(TCP-上传图片)
14 ) . 网络编程(TCP-客户端并发上传图片)
15 ) . 网络编程(TCP客户端并发登录)
16 ) . 网络编程(浏览器客户端-自定义服务端)
17 ). 网络编程(浏览器客户端-Tomcat服务端)
18 ).网络编程(自定义浏览器-Tomcat服务端)
19 ).网络编程(自定义图形界面浏览器-Tomcat服务端)
20 ).网络编程(URL-URLConnection )
21 ).网络编程(小知识点)
22 ).网络编程(域名解析)
十二. 网络编程(TCP复制文件)
1 ) . Demo :
/* 本文讲述 : 将客户端的文件数据上传服务端 涉及到的方法: [1] Socket() 初始化客户端的方法 [2] ServerSocket() 初始化服务端的方法 [3]BufferedReader() 初始化字符流对象的方法 [4]FileReader() 初始化文件字符流的方法 [5] PrintWriter() 初始化打印(字符,字节通用)输出的方法 [6]getOutputStream() 获取输出流的方法 [7] readLine(); 读行的方法 [8]shutdownOutput() ; 结束输出流的方法 思路 : 1.需要有客户端和服务端的端口 2.需要有源,也就是数据哪里来 3.需要有目标,数据那里去 ps: 其实也就是 这三点搞明白,其它的就是在适当场景使用适当的流对象传输就可以了 */ import java.io.*; import java.net.*; //客户端 class TextSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.217.1",1000); //初始化客户端端口 BufferedReader br=new BufferedReader(new FileReader("TcpText.java")); //初始化要上传的文件的起始端 -->读入数据 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //初始化要上传的文件的接收端 -->写出数据 String line =null; while((line=br.readLine())!=null) { pw.println(line); } s.shutdownOutput(); //标识符,关闭客户端的输出流,相当于给流中加入了一个结束标记-1 BufferedReader rbr =new BufferedReader(new InputStreamReader(s.getInputStream())); //服务端反馈数据的接收对象 String str = rbr.readLine(); System.out.println(str); br.close(); s.close(); } } //服务端 class TextServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //初始化服务端 Socket s = ss.accept(); //获取连接到服务端的客户端对象 BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //获取连接到该服务端的客户端的数据 PrintWriter pwOut=new PrintWriter(new FileWriter("copy.text"),true); //将客户端的数据写入到本地磁盘文件 String Add = s.getLocalAddress().getHostAddress(); //获取连接到该服务端的客户端的地址 System.out.println(Add+"connect........"); String line=null; while((line=br.readLine())!=null) { pwOut.println(line); } PrintWriter pw=new PrintWriter(s.getOutputStream(),true); //用来为客户端反馈数据 pw.println("上传成功"); br.close(); s.close(); ss.close(); } }
2 ) . 传数据时定义结束标记的三种方式:
2.1 第一种: 定义一个常量,而后在循环里判断标记
2.2 第二种: 定义一个时间戳,在传数据前发送一次进行判断,再在传数据后发送一次进行判断,当判断标记出现两次表明数据已到 结尾
2.3 第三种 : socket中的 shutdownOutput() 可用来标记结束流
小结 :
1. Tomcat的标识符是空行 2. 文件上传的核心就是复制文件,只是从单机本地传输的方式,转换为了网络流的方式
十三. 网络编程(TCP-上传图片)
1 ) . Demo : -->图片上传
/* 本文讲述 : 图片上传 客户端流程: 1.服务端点 2.读取客户端已有的图片数据 3.通过socket输出流将数据发给服务端 4.读取服务端反馈信息' 5.关闭资源 服务端流程: 1.服务端点 2.建立服务端存储图片数据地址 3.读取客户端发来的图片数据 4.给客户端发送反馈信息 5.关闭资源 ps:关闭资源的秘诀是 哪里有数据哪里就需要关流 */ import java.io.*; import java.net.*; //客户端 class ImgSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.217.1",1000); //客户端服务的端点 FileInputStream bis =new FileInputStream("1.jpg"); //数据的来源 OutputStreambos = s.getOutputStream(); //处理数据的兵器 int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } s.shutdownOutput(); InputStream rbis = s.getInputStream(); //处理反馈的兵器 byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } } //服务端 class ImgServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //服务端的端点 Socket s =ss.accept(); String ad = s.getInetAddress().getHostAddress() ; System.out.println(ad+"connect.........."); FileOutputStream bos =new FileOutputStream("2.jpg"); //数据的存储地 InputStreambis =s.getInputStream(); //读数据的兵器 int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } OutputStream rbos =s.getOutputStream(); //写反馈的兵器 rbos.write("上传成功".getBytes()); bis.close(); ss.close(); s.close(); } }
2 ) . 拆分后的: 服务端
import java.io.*; import java.net.*; //服务端 class Server { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); Socket s =ss.accept(); String ad = s.getInetAddress().getHostAddress() ; System.out.println(ad+"connect.........."); FileOutputStreambos =new FileOutputStream("2.jpg"); InputStream bis =s.getInputStream(); int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } OutputStream rbos =s.getOutputStream(); rbos.write("上传成功".getBytes()); bis.close(); ss.close(); s.close(); } }
3 ) . 拆分后的: 客户端
import java.io.*; import java.net.*; //客户端 class Client { public static void main(String args[]) throws Exception { Socket s =new Socket("10.1.4.25",1000); FileInputStream bis =new FileInputStream("1.jpg"); OutputStream bos =s.getOutputStream(); int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } s.shutdownOutput(); InputStream rbis = s.getInputStream(); byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } }
小结 :
1. 我们可通过以上这种方式,模拟一台计算机是服务器,一台是客户端,然后进行数据的传输与反馈
2. 想要服务端一直开启,可随时获取客户端传来的数据,直接将处理数据的代码外层加 while(true){} 即可,但需要结合多线程
十四. 网络编程(TCP-客户端并发上传图片)
1 ) . Demo: 客户端
/* 运行时 需要在java client 后边加上路径参数才可以运行 ,例如 : java Client S:\develop\JavaText */ import java.io.*; import java.net.*; //客户端 class Client { public static void main(String args[]) throws Exception { if(args.length!=1) //判断是否只传入了一张照片 { System.out.println("请选择一个jpg格式的照片"); return; } File file =new File(args[0]); //主函数传值的方式来传入路径上传图片至服务器 if(!file.getName().endsWith(".jpg")) //判断其名称的后缀是否是.jpg格式 { System.out.println("图片格式错误,请重新选择"); return; } if(file.length()>1024*1024*5) //判断其图片是否过大 { System.out.println("图片过大,请重新选择"); return; } Socket s =new Socket("192.168.152.1",1000); //实例化客户端端口,指定要连接的服务端的地址和端口号 FileInputStream bis =new FileInputStream("1.jpg"); //要传输的文件数据地址 OutputStream bos =s.getOutputStream(); //获取客户端输出流用来输出数据 int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) //先把数据读到临时容器数组buf中 { bos.write(buf,0,len); //再把数据读到客户端输出流,通过输出流读到服务端的地址端口号中 } s.shutdownOutput(); //标记符,用来标识以读完 InputStream rbis = s.getInputStream(); //获取客户端输入流,用来接收服务端的反馈 byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); //读取客户端的反馈信息 System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } }
2 ) . Demo: 服务端:
import java.io.*; import java.net.*; class UserThread implements Runnable { private Socket s; //将客户端的端口封装进线程,与线程一起创建,因为一个线程便需要一个客户端的对象 public UserThread(Socket s) { this.s=s; } public void run() { String ip = s.getInetAddress().getHostAddress(); //获取其对象IP地址 int count=1; //自定义计数器 try{ System.out.println(ip+"connect.........."); //输出该连接对象的IP地址 File file =new File(ip+"("+(count)+")"+".jpg"); //实例化一个文件对象,先判断本地是否存在,若存在则更换名称,这里更名的方式是计数器的方式 while(file.exists()) file =new File(ip+"("+(count++)+")"+".jpg"); InputStream bis =s.getInputStream(); //获取其客户端对象的输入流,对客户端的数据进行获取 FileOutputStream bos =new FileOutputStream(file);//将文件对象放入文件流中得以创建 int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) //读取客户端对象的数据 { bos.write(buf,0,len); //写入文件流中 } OutputStream rbos =s.getOutputStream(); //获取其客户端的输出流,对客户端进行反馈 rbos.write("上传成功".getBytes()); //反馈信息 bis.close(); s.close(); } catch(Exception e) { throw new RuntimeException(ip+"上传失败"); } } } //服务端 class Server { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务端端口,并自定义端口号是1000 while(true) { Socket s =ss.accept(); //获取连接到该端口号的对象 new Thread(new UserThread(s)).start(); //将该端口号的对象传入线程做相关操作并启动线程 } } }
小结 :
1. 并发上传图片的原理就是 TCP+ IO流+多线程,客户端上传,服务端通过多线程循环获取客户端的连接,并同步进行 2. 客户端是多个对象,而服务端只有一个
十五. 网络编程(TCP客户端并发登录)
1 ) . Demo:
/* 本文讲述 : 需求: 客户并发登录 描述: 客户端通过键盘录入用户名,服务端对这个用户名进行校验 若该用户存在,则再服务器显示XXX已登录,并在客户端显示XXX欢迎光临 若该用户不存在,则再服务端显示XXX尝试登录,并在客户端显示XXX,该用户不存在 最多登录三次 问题: [1] PrintWriter bwInfo =new PrintWriter(s.getOutputStream(),true); -->使用PrintWrite切记一定要带上参数true [2] BufferedReader bw =new BufferedReader(new InputStreamReader(s.getInputStream())); -->流缓冲区与流对象一定要匹配,首先是流向匹配,其次是字符/字节匹配 */ import java.io.*; import java.net.*; import java.lang.*; //客户端 class LoginSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.152.1",1000); //初始化客户端端口,并定义目标服务端的地址和端口号 BufferedReader br =new BufferedReader(new InputStreamReader(System.in)); //用来接收命令行输入的用户信息 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //用来给服务端传输用户信息 BufferedReader rbr =new BufferedReader(new InputStreamReader(s.getInputStream())); //用来读取服务端反馈回来的进度信息 for(int x=0;x<3;x++) { String line= br.readLine(); //读取命令行的用户信息 if(line==null) break; pw.println(line); //将用户信息传给服务端 String info = rbr.readLine(); //读取服务端反馈的信息 System.out.println(info); if(info.contains("欢迎")) break; } br.close(); s.close(); } } class UserThread implements Runnable { private Socket s ; //为客户端对象建立引用 UserThread(Socket s) { this.s=s; } public void run() { String ip = s.getLocalAddress().getHostName(); //获取客户端对象的主机名 System.out.println(ip+".......connect........."); try{ for(int i =0;i<3;i++) //标识只能失败三次 { BufferedReader bw =new BufferedReader(new InputStreamReader(s.getInputStream())); //用来读客户端传来的用户信息 String name =bw.readLine(); if(name==null) break; BufferedReader br =new BufferedReader(new FileReader("1.txt")); //用来读取本地磁盘存储的用户信息 String line =null; PrintWriter bwInfo =new PrintWriter(s.getOutputStream(),true); //用来给客户端进行信息反馈 boolean flag =false; while((line=br.readLine())!=null) //循环查询本地磁盘存储的用户信息与客户端传来的用户信息进行比较校验 { if(name.equals(line)) { flag=true; //定义标识符,进来说明有这个用户,则标识符变成true break; } } if(flag) //若存在则服务端输出该客户已登录并反馈进度信息 { System.out.println(ip+"已登录"); bwInfo.println(name+"欢迎光临"); } else //若不存在则服务端输出该客户在尝试登录并反馈进度信息 { System.out.println(ip+"正在尝试登录"); bwInfo.println(name+"用户名不存在"); } } s.close(); } catch(Exception e) //捕获异常,这里捕个大的 { throw new RuntimeException(ip+"校验失败"); } } } //服务端 class LoginServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //初始化服务端端口,并定义端口号为1000 while(true) //不断的接收客户端对象连接 { Socket s =ss.accept(); //获取客户端对象 new Thread(new UserThread(s)).start(); //将客户端对象放入线程 } } }
2 ) .效果展示:
客户端:
服务端:
小结 :
1. 我们可通过for循环的方式限制登录次数,通过标识符的方式标记用户信息是否正确 2. 通过IO流的方式传输数据,通过多线程的方式实现并发,通过TCP的方式实现客户端与服务端的设立
十六. 网络编程(浏览器客户端-自定义服务端)
1 ) .本文讲述 : 自定义服务器的运用, 以及远程登录服务 telnet的运用
2 ) . Demo:
/* 本文讲述 : 自定义服务端,通过浏览器访问 演示:客户端和服务端 1.客户端 : 浏览器(telnet) 2.服务端 : 自定义 */ import java.io.*; import java.net.*; import java.lang.*; //服务端 class CustomServer { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务器端口 Socket s = ss.accept(); //获取连接到服务器的对象 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //向服务器的对象反馈数据 pw.println("客户端你好,我是服务端"); //反馈数据的内容 pw.close(); s.close(); ss.close(); } }
3 ) . 图示 :
3.1 通过浏览器访问
3.2 通过TelNet客户端访问
4 ) . 如何安装telnet :
4.1 打开控制面板-->打开windows功能-->添加telnet功能-->启动telnet服务-->进入cmd进行测试是否可pin通
小结 :
1. Tomcat就是将数据格式转换并打印输出到对应端口的容器 2. telnet是windows中的远程登录命令,是标准的客户端软件
十七. 网络编程(浏览器客户端-Tomcat服务端)
1 ) . 简述 :
1.1 本质 : Tomcat服务器就是封装了serverSocket的类, 该Tomcat地址就是 你所启动的那台主机的地址 ,端口就是 8080
1.2 优势 : Tomcat服务器的好处就是可读取你自定义的资源并解析以此提供服务
1.3 功能 : 我们将自定义的html页面放到Tomcat服务器上,然后启动Tomcat服务器,就可通过 地址 +端口 + 页面 访问到我们的自定义页面
2 ) . 访问Tomcat服务器主页面方式 :
3.1 第一步 : 进入 Tomcat包的Bin目录下启动startup.bat
3.2 第二步 : 进入浏览器输入本机IP地址 + Tomcat 端口号 即可 例: http://10.1.4.45:8080/
3.3 第三步 : 拓展 : 将自定义页面放入Tomcat上发布方式 :
[1] 进入Tomcat的WebApps 目录,然后建立个人目录 myWeb ,然后将 自定义html放入
[2] 通过浏览器进行访问 : 输入本机IP地址 + Tomcat 端口号 + 个人目录+ 自定义html 即可 例:http://10.1.4.45:8080/myWeb/run.html 小结 :
1. Tomcat支持你将自定义页面放入该服务器,然后它支持你运行
2. 客户端 :浏览器 , 服务端 : Tomcat服务器 十八. 网络编程(自定义浏览器-Tomcat服务端)
1 ) . Tomcat 就是 可承载我们 页面数据的地方 ,从而让浏览器 访问
2 ) . Demo:
/* 本文讲述 :
[1] 自定义客户端,访问Tomcat服务器内存放在页面数据
[2] 自定义服务端,让浏览器访问,而后留下浏览器访问时的请求头数据 演示:客户端和服务端 方式一 : 1.客户端 : 浏览器 2.服务端 : 自定义 方式二 : 1.客户端 : 浏览器 2.服务端 : Tomcat服务器 方式三: 1.客户端 : 自定义 2.服务端 : Tomcat服务器 自定义客户端思路: 1.首先得知道客户端向服务端,发送了什么数据; 可通过 自定义服务端获取通过浏览器发来的请求数据 分析 : 我们看到浏览器来给我自己定义的客户端发了以下的请求数据,那么接下来我们自己模拟一个 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0 Accept: text/html,application/xhtml+xml,application/xml *\*;q=0.9,;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 2.自定义客户端请求头,向Tomcat服务器所要相应的页面数据 总结:说到头 就是分清客户端和服务端,就可明白 一次请求两次反馈的奥妙 */ import java.io.*; import java.net.*; import java.lang.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class CustomClient { public static void main(String args[]) throws Exception { Socket s =new Socket("10.1.4.45",8080); //实例化客户端端口以及 传入目标地址和端口号 PrintWriter bw =new PrintWriter(s.getOutputStream(),true); //加true意味着自动换行和自动刷新, 对IO的字符流而言,若不手动刷新,则会出现卡顿和数据不完整的情况 bw.println("GET /myWeb/run.html HTTP/1.1"); //获取Tomcat下webapp下的myWeb下的run.html页面内容 bw.println("Accept: */*"); bw.println("Accept-Language :zh-cn"); bw.println("Host: 10.1.4.45:1000"); bw.println("Connection: Keep-Alive"); bw.println(); bw.println(); BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //读取Tomcat服务器反馈给我的页面数据 String line=null; while((line=br.readLine())!=null) { System.out.println(line); } s.close(); } } //这个类是自定义服务端,用来获取浏览器客户端请求时发来的数据的 /* //服务端 class CustomServer { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务器端口 Socket s = ss.accept(); //获取连接到服务器的对象 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //向服务器的对象反馈数据 pw.println("客户端你好,我是服务端"); //反馈数据的内容 InputStream is =s.getInputStream(); //获取浏览器发送来的请求数据 byte[] buf =new byte[1024]; int len = is.read(buf); System.out.println(new String(buf,0,len)); //打印下浏览器的请求头数据 pw.close(); s.close(); ss.close(); } } */
3 ) . 小知识 : 关于HTTP协议的请求消息头 :
3.1 引入 : 我们可以通过自定义服务器,然后通过浏览器访问自定义服务器,在服务器中获取浏览器发给自定义服务器的信息,这些信息叫作Http的请求消息头
3.2 请求头结构: --> 其中 1-7 是 请求数据头 ,8 是 请求数据体
[1] 请求行路径
[2] 可接收的数据格式
[3] 可接收的语言
[4] 支持的编码封装形式 -->也就是将数据封装打包后传给客户端,节省流量
[5] 客户端的用户信息
[6] 服务器的地址 端口号
[7] 连接状态
------------------------
[8] 请求数据体
4 ) . 个人理解:
4.1 客户端发送请求头,而后 服务端反馈请求头以及请求体
小结 :
1. 一个主机地址可对应多个端口号,可理解为一个主机内可有多个应用程序 2. 底层网络编程的数据传输规则是TCP协议,在应用层的数据传输规则是HTTP协议
3. 响应状态吗200 表示成功
十九. 网络编程(自定义图形界面浏览器-Tomcat服务端)
1 ) . Demo:
/* 本章讲述 : 需求: 如何自定义一个浏览器,通过Tomcat作为服务器,通过GUI作为图像化视图 */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; class MyWindowDemo { private Frame f; private TextField tf; private Button but; private TextArea ta; private Dialog d; private Label lab; private Button okBut; MyWindowDemo() { init(); } //提示对话框 public void dialog() { d= new Dialog(f,"提示信息",true); // 参数是 1,关联组件 2,提示内容 3,模式 lab=new Label(); //初始化标签 d.setBounds(600,200,240,150); //设置对话框基本信息 d.setLayout(new FlowLayout()); //设置对话框布局方式 okBut = new Button("确定"); //初始化按钮 String info="您输入的路径信息"+tf.getText()+"不正确,请重新输入"; lab.setText(info); //将信息设置进标签对象 dialogEvent(); //加载事件 d.add(lab); //将标签添加进对话框组件 d.add(okBut); //将按钮添加进对话框组件 } //初始化 public void init() { f= new Frame("My Window"); //初始化框架 f.setBounds(300,100,600,500); //设置坐标及大小 : 前两个坐标,后两个大小 f.setLayout(new FlowLayout()); //设置布局方式:流式布局 tf= new TextField(60); //初始化一个文本框,长度为 60 but =new Button("转到"); //初始化一个按钮,名字为转到 ta =new TextArea(25,60); //初始化一个文本域,水平25,垂直60 f.add(tf); f.add(but); f.add(ta); //加载事件 FrameEvent(); f.setVisible(true); //使框架(窗体)显示 } //对话框上的事件 private void dialogEvent() { //对话框事件 d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); //对话框上的按钮事件 okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } }); } //访问Tomcat中页面数据的方法 private void showDir() throws IOException { ta.setText(""); String url = tf.getText(); //获取文本框数据 int index1 =url.indexOf("//")+2; //截取第二个 // 开始的坐标 int index2= url.indexOf("/",index1); //截取第二个 // 开始的坐标 之后的 第一个 / 的开始坐标 String str=url.substring(index1,index2); //截取 第二个 // 开始的坐标到 第一个 / 的开始坐标这之间的数据 String[] arr =str.split(":"); //用分隔符分隔开 String host = arr[0]; //获取数组 坐标0 的 int port =Integer.parseInt(arr[1]); //获取数组中坐标1的 String path =url.substring(index2); //活期index2之后的数据 Socket s =new Socket(host,port); //实例化客户端端口以及 传入目标地址和端口号 PrintWriter bw =new PrintWriter(s.getOutputStream(),true); //加true意味着自动换行和自动刷新, 对IO的字符流而言,若不手动刷新,则会出现卡顿和数据不完整的情况 bw.println("GET "+path+" HTTP/1.1"); //获取Tomcat下webapp下的myWeb下的run.html页面内容 bw.println("Accept: */*"); bw.println("Accept-Language :zh-cn"); bw.println("Host: 10.1.4.45:1000"); bw.println("Connection: Keep-Alive"); bw.println(); bw.println(); BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //读取Tomcat服务器反馈给我的页面数据 String line=null; while((line=br.readLine())!=null) { ta.append(line+"\r\n"); } s.close(); } //窗体事件 private void FrameEvent() { //对文本框启动回车事件 tf.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { try { if(e.getKeyCode()==KeyEvent.VK_ENTER) //判断键入值是否与回车值相同 showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //按钮点击事件 but.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //窗体的关闭事件 f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } //主方法 public static void main(String args[]) throws IOException { new MyWindowDemo(); } }
小结 :
1. 浏览器的厉害之处在于 内置的 解析引擎 , 比如 html解析引擎,css 解析引擎, JavaScript解析引擎 2. 我们可通过GUI + Socket+ IO流+ 异常 来做一个自定义浏览器
二十. 网络编程(URL-URLConnection )
1 ) . URI与URL的区别:
1.1 URI要比URL的范围大 ,比如 条形码就是 URI的范畴
2 ) . Demo : URL常用方法演示:
/* 本文讲述 : String getFile() 获取此 URL的文件名。 String getHost() 获取此 URL的主机名(如适用)。 String getPath() 获取此 URL的路径部分。 int getPort() 获取此 URL的端口号。 String getProtocol() 获取此 URL的协议名称。 String getQuery() 获取此 URL的查询部分。 总结: 这些方法的底层封装的是切割,截取等方法,以对象方法的形式出现节约了编程员的开发时间 */ import java.net.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class UrlDemo { public static void sop(Object obj) { System.out.println(obj); } public static void main(String args[]) throws Exception { URL url =new URL("https://www.baidu.com:8080/myweb/index.html?name=zs&age=12";); //自定义路径,用来测试 sop("主机名 :"+url.getHost()); sop("协议:"+url.getProtocol()); sop("主机路径:"+url.getPath()); sop("端口号"+url.getPort()); sop("文件名:"+url.getFile()); sop("参数:"+url.getQuery()); } }
3 ) . Demo: 讲述 可连接对象.可获取路径对象的方法
/* 本文讲述 : [1] URL 类 : 用来定义路径[2] openConnection(); url 用来获取其路径对象[3] URLConnection() : url的连接对象[4] getInputStream() : url的获取输入流数据对象 -->用来读取服务端数据的 */ import java.net.*; import java.io.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class UrlDemo1 { public static void sop(Object obj) { System.out.println(obj); } public static void main(String args[]) throws Exception { URL url =new URL("http://10.1.4.45:8080/myWeb/run.html";); //自定义路径,用来测试 URLConnection uc = url.openConnection(); //获取到路径的对象 InputStream is = uc.getInputStream(); //通过对象调用其读取流获取数据 byte[] buf =new byte[1024]; int len = is.read(buf); sop(new String(buf,0,len)); } }
4 ). 自定义浏览器的优化版 : 也就是使用了应用层的数据传输对象 URLConnection
/* 本章讲述 : 需求: 如何自定义一个浏览器,通过Tomcat作为服务器,通过GUI作为图像化视图 之前的版本是传输层的版本,这个是应用层的版本 ,因为使用了封装的socket对象URLConnection */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; class MyWindowDemo { private Frame f; private TextField tf; private Button but; private TextArea ta; private Dialog d; private Label lab; private Button okBut; MyWindowDemo() { init(); } //提示对话框 public void dialog() { d= new Dialog(f,"提示信息",true); // 参数是 1,关联组件 2,提示内容 3,模式 lab=new Label(); //初始化标签 d.setBounds(600,200,240,150); //设置对话框基本信息 d.setLayout(new FlowLayout()); //设置对话框布局方式 okBut = new Button("确定"); //初始化按钮 String info="您输入的路径信息"+tf.getText()+"不正确,请重新输入"; lab.setText(info); //将信息设置进标签对象 dialogEvent(); //加载事件 d.add(lab); //将标签添加进对话框组件 d.add(okBut); //将按钮添加进对话框组件 } //初始化 public void init() { f= new Frame("My Window"); //初始化框架 f.setBounds(300,100,600,500); //设置坐标及大小 : 前两个坐标,后两个大小 f.setLayout(new FlowLayout()); //设置布局方式:流式布局 tf= new TextField(60); //初始化一个文本框,长度为 60 but =new Button("转到"); //初始化一个按钮,名字为转到 ta =new TextArea(25,60); //初始化一个文本域,水平25,垂直60 f.add(tf); f.add(but); f.add(ta); //加载事件 FrameEvent(); f.setVisible(true); //使框架(窗体)显示 } //对话框上的事件 private void dialogEvent() { //对话框事件 d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); //对话框上的按钮事件 okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } }); } //访问Tomcat中页面数据的方法 private void showDir() throws IOException { ta.setText(""); //设置文本框初始内容为空 String urlGui = tf.getText(); //获取打入输出框的数据 URL url =new URL(urlGui); URLConnection uc = url.openConnection(); //获取到路径的对象 InputStream is = uc.getInputStream(); //通过对象调用其读取流获取数据 byte[] buf =new byte[1024]; int len = is.read(buf); ta.setText(new String(buf,0,len)); //向文本域中设置数据 } //窗体事件 private void FrameEvent() { //对文本框启动回车事件 tf.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { try { if(e.getKeyCode()==KeyEvent.VK_ENTER) //判断键入值是否与回车值相同 showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //按钮点击事件 but.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //窗体的关闭事件 f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } //主方法 public static void main(String args[]) throws IOException { new MyWindowDemo(); } }
4 ) . 关于 TCP/IP的数据传输 :
4.1 传输层是 Soceket 和ServerSocket 是在传输层进行数据的传输
4.2 应用层是 URL和URLConnection 用来在应用层进行数据的传输,这里的URLConnection是将底层的Socekt封装成了对象
4.3 两者比较 : URLConnection 封装后,他把Soceket的响应头给封装了,意味着用户无需看到响应信息,直接获取所需信息即可
5.关于封包和拆包:
5.1 封包封的是各个层级的标识符信息以及核心数据
5.2 拆包拆的是不需要的标识符信息,仅获取我应获取的
小结 :
1. 路径上 ? 进行分隔 , & 进行连接 2. 走传输层进行客户端与服务端之间传输数据时会携带者请求头和响应头 ; 走 应用层则 封装了Socket ,隐藏了请求头和响应头,也就是在层级传输时的封包拆包
二十一. 网络编程(小知识点)
1 ) . 在使用客户端的Socket中 InetAdress 与 SocketAdress的区别?
1.1 InetAdress底层封装的是 IP地址
1.2 SocketAdress底层封装的是 IP地址和端口号
2 ) . 在服务端使用ServerSocket其中构造函数有一个backlog是什么?
2.1 官方解释是 队列的最大长度
2.2 土话解释就是 一个台服务最多能有多少个对象同时连接,可通过这个参数设定,以保证服务器性能的稳定
小结 :
1. 服务器的性能是有限度,当达到一定的瓶颈期的时候,要不机器死机,要不就挂掉,因此解决的方式有两种 ,一定种是 换更好的服务器,第二种就是对客户端连接对象的数量限定 二十二. 网络编程(域名解析)
1 ) . 问题 : 当我们在浏览器中输入https://www.baidu.com/ 网址,那么浏览器到底经历了什么步骤?
1.1 第一步 : 指定协议
1.2 第二步 : 解析主机名
[1]先在本机的主机名与IP映射关系表中查找是否有配置,若有则直接进入该IP地址,若没有则去DNS服务上查找
[2] 去DNS域名解析服务器上查找主机名与IP的映射关系表,查找该www.baidu.com主机名对应的IP地址是多少,从而进入该路径
1.3 第三步 : 前往获取到的端口号对应的页面
ps: 当我们的访问地址直接写的是Ip时Https://1.21.4.45/而不是主机名时就不会再走域名解析表,则直接访问该IP地址
2 ) . 小知识 : https://www.baidu.com/ ,百度其中的主机名是 www.baidu.com ,而背后指定的是一个IP地址,其中是如何将主机名翻译成IP地址的呢?
2.1 解答 : 通过域名解析,也就是DNS
3 ) . http://10.1.4.45:8080 与 http://localhost:8080 为何访问的是同一个地址?
3.1 因为 10.1.4.45 与localhost 是映射关系
3.2 其实其映射表就存在本机的C:\Windows\System32\drivers\etc下的hosts中 ,可通过在这里添加其主机名与IP地址映射来改变我们访问服务的 主机名
4 ) . 关于本地域名解析的应用 :
4.1 关于提高访问网站的速度:
[1] 我们可在本地配置经常访问的网站的IP及主机名,这样可提高我们的上网速度 ,获取任意网站的IP方式 例子 : InetAddress.getByeName("www.sina.com.cn");
4.2 关于躲避收费软件的收费:
[1] 我们可在本地配置一个正确的的网址的对应的错误的IP,然后当收费软件需要去自己网站监测更新时,则走的自己配的IP,以此躲避了更新收费
4.3 关于安全的问题: 如何屏蔽掉垃圾软件
[1] 我们可通过在本地配置一个正确的网址对应的都是自己本地的IP,然后当弹出恶意网站时,自动匹配的是本地,因此访问不出去以此屏蔽
5 ). 图解: 访问百度的走的步骤 :
小结 :
1. 本机上的DNS无需配置,默认的是自己什么网卡就是哪个运营商的DNS,无论哪个运营商的DNS都可用,只是距离远,速度快,会出现超时问题,例 : 阿里DNS, 万网, IBM 的 9.9.9.9 ,Google 的 8.8.8.8 / 8.8.4.4, 诺顿DNS等
12 ) . 网络编程(TCP复制文件)
13 ) . 网络编程(TCP-上传图片)
14 ) . 网络编程(TCP-客户端并发上传图片)
15 ) . 网络编程(TCP客户端并发登录)
16 ) . 网络编程(浏览器客户端-自定义服务端)
17 ). 网络编程(浏览器客户端-Tomcat服务端)
18 ).网络编程(自定义浏览器-Tomcat服务端)
19 ).网络编程(自定义图形界面浏览器-Tomcat服务端)
20 ).网络编程(URL-URLConnection )
21 ).网络编程(小知识点)
22 ).网络编程(域名解析)
十二. 网络编程(TCP复制文件)
1 ) . Demo :
/* 本文讲述 : 将客户端的文件数据上传服务端 涉及到的方法: [1] Socket() 初始化客户端的方法 [2] ServerSocket() 初始化服务端的方法 [3]BufferedReader() 初始化字符流对象的方法 [4]FileReader() 初始化文件字符流的方法 [5] PrintWriter() 初始化打印(字符,字节通用)输出的方法 [6]getOutputStream() 获取输出流的方法 [7] readLine(); 读行的方法 [8]shutdownOutput() ; 结束输出流的方法 思路 : 1.需要有客户端和服务端的端口 2.需要有源,也就是数据哪里来 3.需要有目标,数据那里去 ps: 其实也就是 这三点搞明白,其它的就是在适当场景使用适当的流对象传输就可以了 */ import java.io.*; import java.net.*; //客户端 class TextSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.217.1",1000); //初始化客户端端口 BufferedReader br=new BufferedReader(new FileReader("TcpText.java")); //初始化要上传的文件的起始端 -->读入数据 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //初始化要上传的文件的接收端 -->写出数据 String line =null; while((line=br.readLine())!=null) { pw.println(line); } s.shutdownOutput(); //标识符,关闭客户端的输出流,相当于给流中加入了一个结束标记-1 BufferedReader rbr =new BufferedReader(new InputStreamReader(s.getInputStream())); //服务端反馈数据的接收对象 String str = rbr.readLine(); System.out.println(str); br.close(); s.close(); } } //服务端 class TextServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //初始化服务端 Socket s = ss.accept(); //获取连接到服务端的客户端对象 BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //获取连接到该服务端的客户端的数据 PrintWriter pwOut=new PrintWriter(new FileWriter("copy.text"),true); //将客户端的数据写入到本地磁盘文件 String Add = s.getLocalAddress().getHostAddress(); //获取连接到该服务端的客户端的地址 System.out.println(Add+"connect........"); String line=null; while((line=br.readLine())!=null) { pwOut.println(line); } PrintWriter pw=new PrintWriter(s.getOutputStream(),true); //用来为客户端反馈数据 pw.println("上传成功"); br.close(); s.close(); ss.close(); } }
2 ) . 传数据时定义结束标记的三种方式:
2.1 第一种: 定义一个常量,而后在循环里判断标记
2.2 第二种: 定义一个时间戳,在传数据前发送一次进行判断,再在传数据后发送一次进行判断,当判断标记出现两次表明数据已到 结尾
2.3 第三种 : socket中的 shutdownOutput() 可用来标记结束流
小结 :
1. Tomcat的标识符是空行 2. 文件上传的核心就是复制文件,只是从单机本地传输的方式,转换为了网络流的方式
十三. 网络编程(TCP-上传图片)
1 ) . Demo : -->图片上传
/* 本文讲述 : 图片上传 客户端流程: 1.服务端点 2.读取客户端已有的图片数据 3.通过socket输出流将数据发给服务端 4.读取服务端反馈信息' 5.关闭资源 服务端流程: 1.服务端点 2.建立服务端存储图片数据地址 3.读取客户端发来的图片数据 4.给客户端发送反馈信息 5.关闭资源 ps:关闭资源的秘诀是 哪里有数据哪里就需要关流 */ import java.io.*; import java.net.*; //客户端 class ImgSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.217.1",1000); //客户端服务的端点 FileInputStream bis =new FileInputStream("1.jpg"); //数据的来源 OutputStreambos = s.getOutputStream(); //处理数据的兵器 int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } s.shutdownOutput(); InputStream rbis = s.getInputStream(); //处理反馈的兵器 byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } } //服务端 class ImgServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //服务端的端点 Socket s =ss.accept(); String ad = s.getInetAddress().getHostAddress() ; System.out.println(ad+"connect.........."); FileOutputStream bos =new FileOutputStream("2.jpg"); //数据的存储地 InputStreambis =s.getInputStream(); //读数据的兵器 int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } OutputStream rbos =s.getOutputStream(); //写反馈的兵器 rbos.write("上传成功".getBytes()); bis.close(); ss.close(); s.close(); } }
2 ) . 拆分后的: 服务端
import java.io.*; import java.net.*; //服务端 class Server { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); Socket s =ss.accept(); String ad = s.getInetAddress().getHostAddress() ; System.out.println(ad+"connect.........."); FileOutputStreambos =new FileOutputStream("2.jpg"); InputStream bis =s.getInputStream(); int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } OutputStream rbos =s.getOutputStream(); rbos.write("上传成功".getBytes()); bis.close(); ss.close(); s.close(); } }
3 ) . 拆分后的: 客户端
import java.io.*; import java.net.*; //客户端 class Client { public static void main(String args[]) throws Exception { Socket s =new Socket("10.1.4.25",1000); FileInputStream bis =new FileInputStream("1.jpg"); OutputStream bos =s.getOutputStream(); int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) { bos.write(buf,0,len); } s.shutdownOutput(); InputStream rbis = s.getInputStream(); byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } }
小结 :
1. 我们可通过以上这种方式,模拟一台计算机是服务器,一台是客户端,然后进行数据的传输与反馈
2. 想要服务端一直开启,可随时获取客户端传来的数据,直接将处理数据的代码外层加 while(true){} 即可,但需要结合多线程
十四. 网络编程(TCP-客户端并发上传图片)
1 ) . Demo: 客户端
/* 运行时 需要在java client 后边加上路径参数才可以运行 ,例如 : java Client S:\develop\JavaText */ import java.io.*; import java.net.*; //客户端 class Client { public static void main(String args[]) throws Exception { if(args.length!=1) //判断是否只传入了一张照片 { System.out.println("请选择一个jpg格式的照片"); return; } File file =new File(args[0]); //主函数传值的方式来传入路径上传图片至服务器 if(!file.getName().endsWith(".jpg")) //判断其名称的后缀是否是.jpg格式 { System.out.println("图片格式错误,请重新选择"); return; } if(file.length()>1024*1024*5) //判断其图片是否过大 { System.out.println("图片过大,请重新选择"); return; } Socket s =new Socket("192.168.152.1",1000); //实例化客户端端口,指定要连接的服务端的地址和端口号 FileInputStream bis =new FileInputStream("1.jpg"); //要传输的文件数据地址 OutputStream bos =s.getOutputStream(); //获取客户端输出流用来输出数据 int len =0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) //先把数据读到临时容器数组buf中 { bos.write(buf,0,len); //再把数据读到客户端输出流,通过输出流读到服务端的地址端口号中 } s.shutdownOutput(); //标记符,用来标识以读完 InputStream rbis = s.getInputStream(); //获取客户端输入流,用来接收服务端的反馈 byte[] rbuf =new byte[1024]; int rlen = rbis.read(rbuf); //读取客户端的反馈信息 System.out.println("长度:"+rlen); System.out.println(new String(rbuf,0,rlen)); bis.close(); s.close(); } }
2 ) . Demo: 服务端:
import java.io.*; import java.net.*; class UserThread implements Runnable { private Socket s; //将客户端的端口封装进线程,与线程一起创建,因为一个线程便需要一个客户端的对象 public UserThread(Socket s) { this.s=s; } public void run() { String ip = s.getInetAddress().getHostAddress(); //获取其对象IP地址 int count=1; //自定义计数器 try{ System.out.println(ip+"connect.........."); //输出该连接对象的IP地址 File file =new File(ip+"("+(count)+")"+".jpg"); //实例化一个文件对象,先判断本地是否存在,若存在则更换名称,这里更名的方式是计数器的方式 while(file.exists()) file =new File(ip+"("+(count++)+")"+".jpg"); InputStream bis =s.getInputStream(); //获取其客户端对象的输入流,对客户端的数据进行获取 FileOutputStream bos =new FileOutputStream(file);//将文件对象放入文件流中得以创建 int len=0; byte[] buf =new byte[1024]; while((len=bis.read(buf))!=-1) //读取客户端对象的数据 { bos.write(buf,0,len); //写入文件流中 } OutputStream rbos =s.getOutputStream(); //获取其客户端的输出流,对客户端进行反馈 rbos.write("上传成功".getBytes()); //反馈信息 bis.close(); s.close(); } catch(Exception e) { throw new RuntimeException(ip+"上传失败"); } } } //服务端 class Server { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务端端口,并自定义端口号是1000 while(true) { Socket s =ss.accept(); //获取连接到该端口号的对象 new Thread(new UserThread(s)).start(); //将该端口号的对象传入线程做相关操作并启动线程 } } }
小结 :
1. 并发上传图片的原理就是 TCP+ IO流+多线程,客户端上传,服务端通过多线程循环获取客户端的连接,并同步进行 2. 客户端是多个对象,而服务端只有一个
十五. 网络编程(TCP客户端并发登录)
1 ) . Demo:
/* 本文讲述 : 需求: 客户并发登录 描述: 客户端通过键盘录入用户名,服务端对这个用户名进行校验 若该用户存在,则再服务器显示XXX已登录,并在客户端显示XXX欢迎光临 若该用户不存在,则再服务端显示XXX尝试登录,并在客户端显示XXX,该用户不存在 最多登录三次 问题: [1] PrintWriter bwInfo =new PrintWriter(s.getOutputStream(),true); -->使用PrintWrite切记一定要带上参数true [2] BufferedReader bw =new BufferedReader(new InputStreamReader(s.getInputStream())); -->流缓冲区与流对象一定要匹配,首先是流向匹配,其次是字符/字节匹配 */ import java.io.*; import java.net.*; import java.lang.*; //客户端 class LoginSocket { public static void main(String args[]) throws Exception { Socket s =new Socket("192.168.152.1",1000); //初始化客户端端口,并定义目标服务端的地址和端口号 BufferedReader br =new BufferedReader(new InputStreamReader(System.in)); //用来接收命令行输入的用户信息 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //用来给服务端传输用户信息 BufferedReader rbr =new BufferedReader(new InputStreamReader(s.getInputStream())); //用来读取服务端反馈回来的进度信息 for(int x=0;x<3;x++) { String line= br.readLine(); //读取命令行的用户信息 if(line==null) break; pw.println(line); //将用户信息传给服务端 String info = rbr.readLine(); //读取服务端反馈的信息 System.out.println(info); if(info.contains("欢迎")) break; } br.close(); s.close(); } } class UserThread implements Runnable { private Socket s ; //为客户端对象建立引用 UserThread(Socket s) { this.s=s; } public void run() { String ip = s.getLocalAddress().getHostName(); //获取客户端对象的主机名 System.out.println(ip+".......connect........."); try{ for(int i =0;i<3;i++) //标识只能失败三次 { BufferedReader bw =new BufferedReader(new InputStreamReader(s.getInputStream())); //用来读客户端传来的用户信息 String name =bw.readLine(); if(name==null) break; BufferedReader br =new BufferedReader(new FileReader("1.txt")); //用来读取本地磁盘存储的用户信息 String line =null; PrintWriter bwInfo =new PrintWriter(s.getOutputStream(),true); //用来给客户端进行信息反馈 boolean flag =false; while((line=br.readLine())!=null) //循环查询本地磁盘存储的用户信息与客户端传来的用户信息进行比较校验 { if(name.equals(line)) { flag=true; //定义标识符,进来说明有这个用户,则标识符变成true break; } } if(flag) //若存在则服务端输出该客户已登录并反馈进度信息 { System.out.println(ip+"已登录"); bwInfo.println(name+"欢迎光临"); } else //若不存在则服务端输出该客户在尝试登录并反馈进度信息 { System.out.println(ip+"正在尝试登录"); bwInfo.println(name+"用户名不存在"); } } s.close(); } catch(Exception e) //捕获异常,这里捕个大的 { throw new RuntimeException(ip+"校验失败"); } } } //服务端 class LoginServerSocket { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //初始化服务端端口,并定义端口号为1000 while(true) //不断的接收客户端对象连接 { Socket s =ss.accept(); //获取客户端对象 new Thread(new UserThread(s)).start(); //将客户端对象放入线程 } } }
2 ) .效果展示:
客户端:
服务端:
小结 :
1. 我们可通过for循环的方式限制登录次数,通过标识符的方式标记用户信息是否正确 2. 通过IO流的方式传输数据,通过多线程的方式实现并发,通过TCP的方式实现客户端与服务端的设立
十六. 网络编程(浏览器客户端-自定义服务端)
1 ) .本文讲述 : 自定义服务器的运用, 以及远程登录服务 telnet的运用
2 ) . Demo:
/* 本文讲述 : 自定义服务端,通过浏览器访问 演示:客户端和服务端 1.客户端 : 浏览器(telnet) 2.服务端 : 自定义 */ import java.io.*; import java.net.*; import java.lang.*; //服务端 class CustomServer { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务器端口 Socket s = ss.accept(); //获取连接到服务器的对象 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //向服务器的对象反馈数据 pw.println("客户端你好,我是服务端"); //反馈数据的内容 pw.close(); s.close(); ss.close(); } }
3 ) . 图示 :
3.1 通过浏览器访问
3.2 通过TelNet客户端访问
4 ) . 如何安装telnet :
4.1 打开控制面板-->打开windows功能-->添加telnet功能-->启动telnet服务-->进入cmd进行测试是否可pin通
小结 :
1. Tomcat就是将数据格式转换并打印输出到对应端口的容器 2. telnet是windows中的远程登录命令,是标准的客户端软件
十七. 网络编程(浏览器客户端-Tomcat服务端)
1 ) . 简述 :
1.1 本质 : Tomcat服务器就是封装了serverSocket的类, 该Tomcat地址就是 你所启动的那台主机的地址 ,端口就是 8080
1.2 优势 : Tomcat服务器的好处就是可读取你自定义的资源并解析以此提供服务
1.3 功能 : 我们将自定义的html页面放到Tomcat服务器上,然后启动Tomcat服务器,就可通过 地址 +端口 + 页面 访问到我们的自定义页面
2 ) . 访问Tomcat服务器主页面方式 :
3.1 第一步 : 进入 Tomcat包的Bin目录下启动startup.bat
3.2 第二步 : 进入浏览器输入本机IP地址 + Tomcat 端口号 即可 例: http://10.1.4.45:8080/
3.3 第三步 : 拓展 : 将自定义页面放入Tomcat上发布方式 :
[1] 进入Tomcat的WebApps 目录,然后建立个人目录 myWeb ,然后将 自定义html放入
[2] 通过浏览器进行访问 : 输入本机IP地址 + Tomcat 端口号 + 个人目录+ 自定义html 即可 例:http://10.1.4.45:8080/myWeb/run.html 小结 :
1. Tomcat支持你将自定义页面放入该服务器,然后它支持你运行
2. 客户端 :浏览器 , 服务端 : Tomcat服务器 十八. 网络编程(自定义浏览器-Tomcat服务端)
1 ) . Tomcat 就是 可承载我们 页面数据的地方 ,从而让浏览器 访问
2 ) . Demo:
/* 本文讲述 :
[1] 自定义客户端,访问Tomcat服务器内存放在页面数据
[2] 自定义服务端,让浏览器访问,而后留下浏览器访问时的请求头数据 演示:客户端和服务端 方式一 : 1.客户端 : 浏览器 2.服务端 : 自定义 方式二 : 1.客户端 : 浏览器 2.服务端 : Tomcat服务器 方式三: 1.客户端 : 自定义 2.服务端 : Tomcat服务器 自定义客户端思路: 1.首先得知道客户端向服务端,发送了什么数据; 可通过 自定义服务端获取通过浏览器发来的请求数据 分析 : 我们看到浏览器来给我自己定义的客户端发了以下的请求数据,那么接下来我们自己模拟一个 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0 Accept: text/html,application/xhtml+xml,application/xml *\*;q=0.9,;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1 2.自定义客户端请求头,向Tomcat服务器所要相应的页面数据 总结:说到头 就是分清客户端和服务端,就可明白 一次请求两次反馈的奥妙 */ import java.io.*; import java.net.*; import java.lang.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class CustomClient { public static void main(String args[]) throws Exception { Socket s =new Socket("10.1.4.45",8080); //实例化客户端端口以及 传入目标地址和端口号 PrintWriter bw =new PrintWriter(s.getOutputStream(),true); //加true意味着自动换行和自动刷新, 对IO的字符流而言,若不手动刷新,则会出现卡顿和数据不完整的情况 bw.println("GET /myWeb/run.html HTTP/1.1"); //获取Tomcat下webapp下的myWeb下的run.html页面内容 bw.println("Accept: */*"); bw.println("Accept-Language :zh-cn"); bw.println("Host: 10.1.4.45:1000"); bw.println("Connection: Keep-Alive"); bw.println(); bw.println(); BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //读取Tomcat服务器反馈给我的页面数据 String line=null; while((line=br.readLine())!=null) { System.out.println(line); } s.close(); } } //这个类是自定义服务端,用来获取浏览器客户端请求时发来的数据的 /* //服务端 class CustomServer { public static void main(String args[]) throws Exception { ServerSocket ss =new ServerSocket(1000); //实例化服务器端口 Socket s = ss.accept(); //获取连接到服务器的对象 PrintWriter pw =new PrintWriter(s.getOutputStream(),true); //向服务器的对象反馈数据 pw.println("客户端你好,我是服务端"); //反馈数据的内容 InputStream is =s.getInputStream(); //获取浏览器发送来的请求数据 byte[] buf =new byte[1024]; int len = is.read(buf); System.out.println(new String(buf,0,len)); //打印下浏览器的请求头数据 pw.close(); s.close(); ss.close(); } } */
3 ) . 小知识 : 关于HTTP协议的请求消息头 :
3.1 引入 : 我们可以通过自定义服务器,然后通过浏览器访问自定义服务器,在服务器中获取浏览器发给自定义服务器的信息,这些信息叫作Http的请求消息头
3.2 请求头结构: --> 其中 1-7 是 请求数据头 ,8 是 请求数据体
[1] 请求行路径
[2] 可接收的数据格式
[3] 可接收的语言
[4] 支持的编码封装形式 -->也就是将数据封装打包后传给客户端,节省流量
[5] 客户端的用户信息
[6] 服务器的地址 端口号
[7] 连接状态
------------------------
[8] 请求数据体
4 ) . 个人理解:
4.1 客户端发送请求头,而后 服务端反馈请求头以及请求体
小结 :
1. 一个主机地址可对应多个端口号,可理解为一个主机内可有多个应用程序 2. 底层网络编程的数据传输规则是TCP协议,在应用层的数据传输规则是HTTP协议
3. 响应状态吗200 表示成功
十九. 网络编程(自定义图形界面浏览器-Tomcat服务端)
1 ) . Demo:
/* 本章讲述 : 需求: 如何自定义一个浏览器,通过Tomcat作为服务器,通过GUI作为图像化视图 */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; class MyWindowDemo { private Frame f; private TextField tf; private Button but; private TextArea ta; private Dialog d; private Label lab; private Button okBut; MyWindowDemo() { init(); } //提示对话框 public void dialog() { d= new Dialog(f,"提示信息",true); // 参数是 1,关联组件 2,提示内容 3,模式 lab=new Label(); //初始化标签 d.setBounds(600,200,240,150); //设置对话框基本信息 d.setLayout(new FlowLayout()); //设置对话框布局方式 okBut = new Button("确定"); //初始化按钮 String info="您输入的路径信息"+tf.getText()+"不正确,请重新输入"; lab.setText(info); //将信息设置进标签对象 dialogEvent(); //加载事件 d.add(lab); //将标签添加进对话框组件 d.add(okBut); //将按钮添加进对话框组件 } //初始化 public void init() { f= new Frame("My Window"); //初始化框架 f.setBounds(300,100,600,500); //设置坐标及大小 : 前两个坐标,后两个大小 f.setLayout(new FlowLayout()); //设置布局方式:流式布局 tf= new TextField(60); //初始化一个文本框,长度为 60 but =new Button("转到"); //初始化一个按钮,名字为转到 ta =new TextArea(25,60); //初始化一个文本域,水平25,垂直60 f.add(tf); f.add(but); f.add(ta); //加载事件 FrameEvent(); f.setVisible(true); //使框架(窗体)显示 } //对话框上的事件 private void dialogEvent() { //对话框事件 d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); //对话框上的按钮事件 okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } }); } //访问Tomcat中页面数据的方法 private void showDir() throws IOException { ta.setText(""); String url = tf.getText(); //获取文本框数据 int index1 =url.indexOf("//")+2; //截取第二个 // 开始的坐标 int index2= url.indexOf("/",index1); //截取第二个 // 开始的坐标 之后的 第一个 / 的开始坐标 String str=url.substring(index1,index2); //截取 第二个 // 开始的坐标到 第一个 / 的开始坐标这之间的数据 String[] arr =str.split(":"); //用分隔符分隔开 String host = arr[0]; //获取数组 坐标0 的 int port =Integer.parseInt(arr[1]); //获取数组中坐标1的 String path =url.substring(index2); //活期index2之后的数据 Socket s =new Socket(host,port); //实例化客户端端口以及 传入目标地址和端口号 PrintWriter bw =new PrintWriter(s.getOutputStream(),true); //加true意味着自动换行和自动刷新, 对IO的字符流而言,若不手动刷新,则会出现卡顿和数据不完整的情况 bw.println("GET "+path+" HTTP/1.1"); //获取Tomcat下webapp下的myWeb下的run.html页面内容 bw.println("Accept: */*"); bw.println("Accept-Language :zh-cn"); bw.println("Host: 10.1.4.45:1000"); bw.println("Connection: Keep-Alive"); bw.println(); bw.println(); BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); //读取Tomcat服务器反馈给我的页面数据 String line=null; while((line=br.readLine())!=null) { ta.append(line+"\r\n"); } s.close(); } //窗体事件 private void FrameEvent() { //对文本框启动回车事件 tf.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { try { if(e.getKeyCode()==KeyEvent.VK_ENTER) //判断键入值是否与回车值相同 showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //按钮点击事件 but.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //窗体的关闭事件 f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } //主方法 public static void main(String args[]) throws IOException { new MyWindowDemo(); } }
小结 :
1. 浏览器的厉害之处在于 内置的 解析引擎 , 比如 html解析引擎,css 解析引擎, JavaScript解析引擎 2. 我们可通过GUI + Socket+ IO流+ 异常 来做一个自定义浏览器
二十. 网络编程(URL-URLConnection )
1 ) . URI与URL的区别:
1.1 URI要比URL的范围大 ,比如 条形码就是 URI的范畴
2 ) . Demo : URL常用方法演示:
/* 本文讲述 : String getFile() 获取此 URL的文件名。 String getHost() 获取此 URL的主机名(如适用)。 String getPath() 获取此 URL的路径部分。 int getPort() 获取此 URL的端口号。 String getProtocol() 获取此 URL的协议名称。 String getQuery() 获取此 URL的查询部分。 总结: 这些方法的底层封装的是切割,截取等方法,以对象方法的形式出现节约了编程员的开发时间 */ import java.net.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class UrlDemo { public static void sop(Object obj) { System.out.println(obj); } public static void main(String args[]) throws Exception { URL url =new URL("https://www.baidu.com:8080/myweb/index.html?name=zs&age=12";); //自定义路径,用来测试 sop("主机名 :"+url.getHost()); sop("协议:"+url.getProtocol()); sop("主机路径:"+url.getPath()); sop("端口号"+url.getPort()); sop("文件名:"+url.getFile()); sop("参数:"+url.getQuery()); } }
3 ) . Demo: 讲述 可连接对象.可获取路径对象的方法
/* 本文讲述 : [1] URL 类 : 用来定义路径[2] openConnection(); url 用来获取其路径对象[3] URLConnection() : url的连接对象[4] getInputStream() : url的获取输入流数据对象 -->用来读取服务端数据的 */ import java.net.*; import java.io.*; //这个类是自定义客户端,用来获取 Tomcat服务器中的页面数据的 class UrlDemo1 { public static void sop(Object obj) { System.out.println(obj); } public static void main(String args[]) throws Exception { URL url =new URL("http://10.1.4.45:8080/myWeb/run.html";); //自定义路径,用来测试 URLConnection uc = url.openConnection(); //获取到路径的对象 InputStream is = uc.getInputStream(); //通过对象调用其读取流获取数据 byte[] buf =new byte[1024]; int len = is.read(buf); sop(new String(buf,0,len)); } }
4 ). 自定义浏览器的优化版 : 也就是使用了应用层的数据传输对象 URLConnection
/* 本章讲述 : 需求: 如何自定义一个浏览器,通过Tomcat作为服务器,通过GUI作为图像化视图 之前的版本是传输层的版本,这个是应用层的版本 ,因为使用了封装的socket对象URLConnection */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; class MyWindowDemo { private Frame f; private TextField tf; private Button but; private TextArea ta; private Dialog d; private Label lab; private Button okBut; MyWindowDemo() { init(); } //提示对话框 public void dialog() { d= new Dialog(f,"提示信息",true); // 参数是 1,关联组件 2,提示内容 3,模式 lab=new Label(); //初始化标签 d.setBounds(600,200,240,150); //设置对话框基本信息 d.setLayout(new FlowLayout()); //设置对话框布局方式 okBut = new Button("确定"); //初始化按钮 String info="您输入的路径信息"+tf.getText()+"不正确,请重新输入"; lab.setText(info); //将信息设置进标签对象 dialogEvent(); //加载事件 d.add(lab); //将标签添加进对话框组件 d.add(okBut); //将按钮添加进对话框组件 } //初始化 public void init() { f= new Frame("My Window"); //初始化框架 f.setBounds(300,100,600,500); //设置坐标及大小 : 前两个坐标,后两个大小 f.setLayout(new FlowLayout()); //设置布局方式:流式布局 tf= new TextField(60); //初始化一个文本框,长度为 60 but =new Button("转到"); //初始化一个按钮,名字为转到 ta =new TextArea(25,60); //初始化一个文本域,水平25,垂直60 f.add(tf); f.add(but); f.add(ta); //加载事件 FrameEvent(); f.setVisible(true); //使框架(窗体)显示 } //对话框上的事件 private void dialogEvent() { //对话框事件 d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); //对话框上的按钮事件 okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } }); } //访问Tomcat中页面数据的方法 private void showDir() throws IOException { ta.setText(""); //设置文本框初始内容为空 String urlGui = tf.getText(); //获取打入输出框的数据 URL url =new URL(urlGui); URLConnection uc = url.openConnection(); //获取到路径的对象 InputStream is = uc.getInputStream(); //通过对象调用其读取流获取数据 byte[] buf =new byte[1024]; int len = is.read(buf); ta.setText(new String(buf,0,len)); //向文本域中设置数据 } //窗体事件 private void FrameEvent() { //对文本框启动回车事件 tf.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { try { if(e.getKeyCode()==KeyEvent.VK_ENTER) //判断键入值是否与回车值相同 showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //按钮点击事件 but.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { showDir(); } catch(IOException ioe) { throw new RuntimeException("路径异常"); } } }); //窗体的关闭事件 f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } //主方法 public static void main(String args[]) throws IOException { new MyWindowDemo(); } }
4 ) . 关于 TCP/IP的数据传输 :
4.1 传输层是 Soceket 和ServerSocket 是在传输层进行数据的传输
4.2 应用层是 URL和URLConnection 用来在应用层进行数据的传输,这里的URLConnection是将底层的Socekt封装成了对象
4.3 两者比较 : URLConnection 封装后,他把Soceket的响应头给封装了,意味着用户无需看到响应信息,直接获取所需信息即可
5.关于封包和拆包:
5.1 封包封的是各个层级的标识符信息以及核心数据
5.2 拆包拆的是不需要的标识符信息,仅获取我应获取的
小结 :
1. 路径上 ? 进行分隔 , & 进行连接 2. 走传输层进行客户端与服务端之间传输数据时会携带者请求头和响应头 ; 走 应用层则 封装了Socket ,隐藏了请求头和响应头,也就是在层级传输时的封包拆包
二十一. 网络编程(小知识点)
1 ) . 在使用客户端的Socket中 InetAdress 与 SocketAdress的区别?
1.1 InetAdress底层封装的是 IP地址
1.2 SocketAdress底层封装的是 IP地址和端口号
2 ) . 在服务端使用ServerSocket其中构造函数有一个backlog是什么?
2.1 官方解释是 队列的最大长度
2.2 土话解释就是 一个台服务最多能有多少个对象同时连接,可通过这个参数设定,以保证服务器性能的稳定
小结 :
1. 服务器的性能是有限度,当达到一定的瓶颈期的时候,要不机器死机,要不就挂掉,因此解决的方式有两种 ,一定种是 换更好的服务器,第二种就是对客户端连接对象的数量限定 二十二. 网络编程(域名解析)
1 ) . 问题 : 当我们在浏览器中输入https://www.baidu.com/ 网址,那么浏览器到底经历了什么步骤?
1.1 第一步 : 指定协议
1.2 第二步 : 解析主机名
[1]先在本机的主机名与IP映射关系表中查找是否有配置,若有则直接进入该IP地址,若没有则去DNS服务上查找
[2] 去DNS域名解析服务器上查找主机名与IP的映射关系表,查找该www.baidu.com主机名对应的IP地址是多少,从而进入该路径
1.3 第三步 : 前往获取到的端口号对应的页面
ps: 当我们的访问地址直接写的是Ip时Https://1.21.4.45/而不是主机名时就不会再走域名解析表,则直接访问该IP地址
2 ) . 小知识 : https://www.baidu.com/ ,百度其中的主机名是 www.baidu.com ,而背后指定的是一个IP地址,其中是如何将主机名翻译成IP地址的呢?
2.1 解答 : 通过域名解析,也就是DNS
3 ) . http://10.1.4.45:8080 与 http://localhost:8080 为何访问的是同一个地址?
3.1 因为 10.1.4.45 与localhost 是映射关系
3.2 其实其映射表就存在本机的C:\Windows\System32\drivers\etc下的hosts中 ,可通过在这里添加其主机名与IP地址映射来改变我们访问服务的 主机名
4 ) . 关于本地域名解析的应用 :
4.1 关于提高访问网站的速度:
[1] 我们可在本地配置经常访问的网站的IP及主机名,这样可提高我们的上网速度 ,获取任意网站的IP方式 例子 : InetAddress.getByeName("www.sina.com.cn");
4.2 关于躲避收费软件的收费:
[1] 我们可在本地配置一个正确的的网址的对应的错误的IP,然后当收费软件需要去自己网站监测更新时,则走的自己配的IP,以此躲避了更新收费
4.3 关于安全的问题: 如何屏蔽掉垃圾软件
[1] 我们可通过在本地配置一个正确的网址对应的都是自己本地的IP,然后当弹出恶意网站时,自动匹配的是本地,因此访问不出去以此屏蔽
5 ). 图解: 访问百度的走的步骤 :
小结 :
1. 本机上的DNS无需配置,默认的是自己什么网卡就是哪个运营商的DNS,无论哪个运营商的DNS都可用,只是距离远,速度快,会出现超时问题,例 : 阿里DNS, 万网, IBM 的 9.9.9.9 ,Google 的 8.8.8.8 / 8.8.4.4, 诺顿DNS等
相关文章推荐
- (十四)Core Java 网络编程(网络模型详解,UDP聊天,TCP数据传输)-01 (110)
- 黑马程序员-TCP上传图片-多线程并发上传图片-客户端并发登陆-自定义服务器
- Java基础24天--02--网络编程(TCP--客户端并发上传图片)
- Java基础-网络编程(TCP-客户端并发登录)
- 黑马程序员——TCP并发,浏览器客户端与Tomcat服务器
- 【24】网络编程2_TCP并发上传图片和登录,Tomcat服务器,自定义IE,域名解析
- Java 网络编程(TCP)实现服务器与客户端1对多的随意顺 序的聊天室
- Java基础-网络编程(浏览器客户端-自定义服务端)
- Java 并发TCP 服务器[CODE]
- 黑马程序员 Java练习-自定义图形化界面模拟浏览器访问Tomcat服务器
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- java 通过 socket 实现 服务器和客户端的通信 TCP
- 黑马程序员_Java基础_网络编程_客户端服务端数据传输,交互,客户端请求服务原理,自定义浏览器,URL统一资源定位符
- 黑马程序员 Java练习-模拟TCP客户端并发上传图片
- Java笔记6 网络编程<2>TCP、自定义图形界面浏览器、URL
- [JAVA] Tcp客户端和服务器简单代码
- java基础知识11(TCP客户端并发登陆
- Java TCP ECHO服务器客户端[CODE]
- 黑马程序员_TCP-客户端并发登录 判断用户名
- MFC网络编程TCP/IP的服务器与客户端代码