外围概述:在互联网时代,咱们所利用的大部分软件都是基于网络为根底的,那么软件之间是如何通过网络通信的呢?为什么咱们通过浏览器输出网址就能够看到网页?本篇咱们将会学习网络编程,让咱们可能从编程角度更底层的更好的了解软件之间的通信流程。
第一章:网络编程根底
1.1-软件结构(理解)
C/S构造
C/S构造 :全称为Client/Server构造,是指客户端和服务器构造。常见程序有QQ、迅雷等软件。
B/S构造
B/S构造:全称为Browser/Server构造,是指浏览器和服务器构造。常见浏览器有谷歌、火狐等。
两种架构各有劣势,然而无论哪种架构,都离不开网络的反对。网络编程,就是在肯定的协定下,实现两台计算机的通信的程序。
1.2-网络通信协定(理解)
网络通信协定:通信协议是对计算机必须恪守的规定,只有恪守这些规定,计算机之间能力进行通信。这就 好比在路线中行驶的汽车肯定要恪守交通规则一样,协定中对数据的传输格局、传输速率、传输步骤等做了对立规定,通信单方必须同时恪守,最终实现数据交换。
TCP/IP协定: 传输控制协议/因特网互联协定( Transmission Control Protocol/Internet Protocol),是Internet最根本、最宽泛的协定。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的规范。它的外部蕴含一系列的用于解决数据通信的协定,并采纳了4层的分层模型,每一层都呼叫它的下一层所提供的协定来实现本人的需要。
1.3-协定分类(理解)
通信的协定还是比较复杂的, java.net 包中蕴含的类和接口,它们提供低层次的通信细节。咱们能够间接应用这些类和接口,来专一于网络程序开发,而不必思考通信的细节。
TCP
TCP:传输控制协议 (Transmission Control Protocol)。TCP协定是面向连贯
的通信协议,即传输数据之前,在发送端和接收端建设逻辑连贯,而后再传输数据,它提供了两台计算机之间牢靠无差错的数据传输。
三次握手:TCP协定中,在发送数据的筹备阶段,客户端与服务器之间的三次交互,以保障连贯的牢靠。
- 第一次握手,客户端向服务器端收回连贯申请,期待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,告诉客户端收到了连贯申请。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连贯。整个交互过程如下图所示。
实现三次握手,连贯建设后,客户端和服务器就能够开始进行数据传输了。因为这种面向连贯的个性,TCP协定能够保障传输数据的平安,所以利用非常宽泛,例如下载文件、浏览网页等。
UDP
用户数据报协定(User Datagram Protocol)。UDP协定是一个面向无连贯
的协定。传输数据时,不须要建设连贯,不论对方端服务是否启动,间接将数据、数据源和目的地都封装在数据包中,间接发送。每个数据包的大小限度在64k以内。它是不牢靠协定,因为无连贯,所以传输速度快,然而容易失落数据。日常利用中,例如视频会议、QQ聊天等。
1.4-网络编程三要素(理解)
协定
协定:计算机网络通信必须恪守的规定,曾经介绍过了,不再赘述。
IP地址
什么是IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做惟一的编号。如果咱们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
IP地址的分类:
- IPv4:是一个32位的二进制数,通常被分为4个字节,示意成 a.b.c.d 的模式,例如 192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多能够示意42亿个。
- IPv6:因为互联网的蓬勃发展,IP地址的需求量愈来愈大,然而网络地址资源无限,使得IP的调配越发缓和。有材料显示,寰球IPv4地址在2011年2月调配结束。 为了扩充地址空间,拟通过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以上的端口号。如果端口号被另外一个服务或利用所占用,会导致以后程序启动失败。
利用 协定 + IP地址 + 端口号 三元组合,就能够标识网络中的过程了,那么过程间的通信就能够利用这个标识与其它过程进行交互。
第二章:TCP通信程序
2.1-TCP通信程序介绍(理解)
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格辨别为客户端(Client)与服务端(Server)。
两端通信时步骤:
- 服务端程序,须要当时启动,期待客户端的连贯。
- 客户端被动连贯服务器端,连贯胜利能力通信。服务端不能够被动连贯客户端。
在Java中,提供了两个类用于实现TCP通信程序:
- 客户端:
java.net.Socket
类示意。创立Socket
对象,向服务端收回连贯申请,服务端响应申请,两者建设连贯开始通信。 - 服务端:
java.net.ServerSocket
类示意。创立ServerSocket
对象,相当于开启一个服务,并期待客户端的连贯。
2.2-Socket类(重要)
Socket 类:该类实现客户端套接字,套接字指的是两台设施之间通信的端点。
构造方法
public Socket(String host, int port)
:创立套接字对象并将其连贯到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
回送地址(127.x.x.x) 是本机回送地址(Loopback Address),次要用于网络软件测试以及本地机过程间通信,无论什么程序,一旦应用回送地址发送数据,立刻返回,不进行任何网络传输。
Socket client = new Socket("127.0.0.1", 6666);
成员办法
public InputStream getInputStream()
: 返回此套接字的输出流。- 如果此Scoket具备相关联的通道,则生成的InputStream 的所有操作也关联该通道。
- 敞开生成的InputStream也将敞开相干的Socket。
public OutputStream getOutputStream()
: 返回此套接字的输入流。- 如果此Scoket具备相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
- 敞开生成的OutputStream也将敞开相干的Socket。
public void close()
:敞开此套接字。- 一旦一个socket被敞开,它不可再应用。
- 敞开此socket也将敞开相干的InputStream和OutputStream 。
public void shutdownOutput()
: 禁用此套接字的输入流。- 任何先前写出的数据将被发送,随后终止输入流
2.3-ServerSocket类(重要)
ServerSocket 类:这个类实现了服务器套接字,该对象期待通过网络的申请。
构造方法
构造方法:public ServerSocket(int port)
:应用该构造方法在创立ServerSocket对象时,就能够将其绑定到一个指 定的端口号上,参数port就是端口号。
ServerSocket server = new ServerSocket(6666);
成员办法
成员办法:public Socket accept()
:侦听并承受连贯,返回一个新的Socket对象,用于和客户端实现通信。该办法 会始终阻塞直到建设连贯。
2.4-简略的TCP网络程序(重要)
TCP通信剖析
- 【服务端】启动,创立ServerSocket对象,期待连贯。
- 【客户端】启动,创立Socket对象,申请连贯。
- 【服务端】接管连贯,调用accept办法,并返回一个Socket对象。
- 【客户端】Socket对象,获取OutputStream,向服务端写出数据。
- 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。
- 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
- 【客户端】Scoket对象,获取InputStream,解析回写数据。
- 【客户端】开释资源,断开连接
客户端向服务端发送数据
客户端代码
public class ClientTCP { public static void main(String[] args) throws Exception { System.out.println("客户端 发送数据"); // 1.创立 Socket ( ip , port ) , 确定连贯到哪里. Socket client = new Socket("localhost", 6666); // 2.获取流对象 . 输入流 OutputStream os = client.getOutputStream(); // 3.写出数据. os.write("你好么? tcp ,我来了".getBytes()); // 4. 敞开资源 . os.close(); client.close(); }}
服务端代码
public class ServerTCP { public static void main(String[] args) throws IOException { System.out.println("服务端启动 , 期待连贯 .... "); // 1.创立 ServerSocket对象,绑定端口,开始期待连贯 ServerSocket ss = new ServerSocket(6666); // 2.接管连贯 accept 办法, 返回 socket 对象. Socket server = ss.accept(); // 3.通过socket 获取输出流 InputStream is = server.getInputStream(); // 4.一次性读取数据 // 4.1 创立字节数组 byte[] b = new byte[1024]; // 4.2 据读取到字节数组中. int len = is.read(b); // 4.3 解析数组,打印字符串信息 String msg = new String(b, 0, len); System.out.println(msg); //5.敞开资源. is.close(); server.close(); }}
服务端向客户端回写数据
服务端代码
public class ClientTCP { public static void main(String[] args) throws Exception { System.out.println("客户端 发送数据"); // 1.创立 Socket ( ip , port ) , 确定连贯到哪里. Socket client = new Socket("localhost", 6666); // 2.通过Scoket,获取输入流对象 OutputStream os = client.getOutputStream(); // 3.写出数据. os.write("你好么? tcp ,我来了".getBytes()); // ==============解析回写========================= // 4. 通过Scoket,获取 输出流对象 InputStream in = client.getInputStream(); // 5. 读取数据数据 byte[] b = new byte[100]; int len = in.read(b); System.out.println(new String(b, 0, len)); // 6. 敞开资源 . in.close(); os.close(); client.close(); }}
客户端代码
public class ServerTCP { public static void main(String[] args) throws IOException { System.out.println("服务端启动 , 期待连贯 .... "); // 1.创立 ServerSocket对象,绑定端口,开始期待连贯 ServerSocket ss = new ServerSocket(6666); // 2.接管连贯 accept 办法, 返回 socket 对象. Socket server = ss.accept(); // 3.通过socket 获取输出流 InputStream is = server.getInputStream(); // 4.一次性读取数据 // 4.1 创立字节数组 byte[] b = new byte[1024]; // 4.2 据读取到字节数组中. int len = is.read(b); // 4.3 解析数组,打印字符串信息 String msg = new String(b, 0, len); System.out.println(msg); // =================回写数据======================= // 5. 通过 socket 获取输入流 OutputStream out = server.getOutputStream(); // 6. 回写数据 out.write("我很好,谢谢你".getBytes()); // 7.敞开资源. out.close(); is.close(); server.close(); }}
第三章:文件上传案例
3.1-文件上传剖析
- 【客户端】输出流,从硬盘读取文件数据到程序中。
- 【客户端】输入流,写出文件数据到服务端。
- 【服务端】输出流,读取文件数据到服务端程序。
- 【服务端】输入流,写出文件数据到服务器硬盘中。
- 【服务端】获取输入流,回写数据。
- 【客户端】获取输出流,解析回写数据。
3.2-客户端程序实现
public static void main(String[] args) throws IOException { // 1.创立流对象 // 1.1 创立输出流,读取本地文件 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg")); // 1.2 创立输入流,写到服务端 Socket socket = new Socket("localhost", 6666); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); //2.写出数据. byte[] b = new byte[1024 * 8 ]; int len ; while (( len = bis.read(b))!=-1) { bos.write(b, 0, len); } // 敞开输入流,告诉服务端,写出数据结束 socket.shutdownOutput(); System.out.println("文件发送结束"); // 3. =====解析回写============ InputStream in = socket.getInputStream(); byte[] back = new byte[20]; in.read(back); System.out.println(new String(back)); in.close(); // 4.开释资源 socket.close(); bis.close();}
3.3-文件上传单线程服务器实现
public static void main(String[] args) throws IOException { System.out.println("服务器 启动..... "); // 1. 创立服务端ServerSocket ServerSocket serverSocket = new ServerSocket(6666); // 2. 循环接管,建设连贯 Socket accept = serverSocket.accept(); /* *3. socket对象进行读写操作 */ try { //3.1 获取输出流对象 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //3.2 创立输入流对象, 保留到本地 . FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg"); BufferedOutputStream bos = new BufferedOutputStream(fis); // 3.3 读写数据 byte[] b = new byte[1024 * 8]; int len; while ((len = bis.read(b)) != -1) { bos.write(b, 0, len); } // 4.=======信息回写=========================== System.out.println("back ........"); OutputStream out = accept.getOutputStream(); out.write("上传胜利".getBytes()); out.close(); //================================ //5. 敞开 资源 bos.close(); bis.close(); accept.close(); System.out.println("文件上传已保留"); } catch (IOException e) { e.printStackTrace(); } }}
3.4-文件上传多线程服务器实现
文件上传的案例中,服务器只能为客户端服务器一次,之后服务器端程序就会完结。而咱们必须做到让服务器程序不能完结,时时刻刻都要为客户端服务。而且同时能够为多个客户端提供服务器,做到一个客户端就要开启一个信新的线程。
public static void main(String[] args) throws IOException{ System.out.println("服务器 启动..... "); // 1. 创立服务端ServerSocket ServerSocket serverSocket = new ServerSocket(6666); // 2. 循环接管,建设连贯 while (true) { Socket accept = serverSocket.accept(); /* 3. socket对象交给子线程解决,进行读写操作 Runnable接口中,只有一个run办法,应用lambda表达式简化格局 */ new Thread(() -> { try{ //3.1 获取输出流对象 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //3.2 创立输入流对象, 保留到本地 . FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg"); BufferedOutputStream bos = new BufferedOutputStream(fis); // 3.3 读写数据 byte[] b = new byte[1024 * 8]; int len; while ((len = bis.read(b)) != -1) { bos.write(b, 0, len); } // 4.=======信息回写=========================== System.out.println("back ........"); OutputStream out = accept.getOutputStream(); out.write("上传胜利".getBytes()); out.close(); //================================ //5. 敞开 资源 bos.close(); bis.close(); accept.close(); System.out.println("文件上传已保留"); } catch (IOException e) { e.printStackTrace(); } }).start(); }}
3.5-文件上传服务器实现优化
频繁的创立线程会减少系统资源的开销,能够利用线程池进行再次优化。
public static void main(String[] args) throws IOException{ System.out.println("服务器 启动..... "); ServerSocket serverSocket = new ServerSocket(6666); //创立10个线程的线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); while (true) { Socket accept = serverSocket.accept(); //提交线程执行的工作 executorService.submit(()->{ try{ BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg"); BufferedOutputStream bos = new BufferedOutputStream(fis); byte[] b = new byte[1024 * 8]; int len; while ((len = bis.read(b)) != -1) { bos.write(b, 0, len); } System.out.println("back ........"); OutputStream out = accept.getOutputStream(); out.write("上传胜利".getBytes()); out.close(); bos.close(); bis.close(); accept.close(); System.out.println("文件上传已保留"); } catch (IOException e) { e.printStackTrace(); } }); }}
第四章:模仿B/S服务器
模仿网站服务器,应用浏览器拜访本人编写的服务端程序,查看网页成果。
4.1-案例剖析
我的项目中有一个web我的项目,咱们写服务端代码,而后通过浏览器输出地址127.0.0.1:8888/web/index.html
筹备页面数据,web文件夹。
- 蕴含网页html文件
- 蕴含图片
- 蕴含css样式表
- 咱们模仿服务器端,ServerSocket类监听端口,应用浏览器拜访,查看网页成果
4.2-HTTP协定
案例中需应用浏览器查看成果,浏览器和服务器之间是遵循HTTP协定的,咱们先对HTTP协定进行简略的介绍,在前期JavaWeb相干篇幅中再详解解释。
- HTTP协定,称为超文本传输协定。
- 规定了客户端浏览器和服务器之间的协定。
- HTTP协定是TCP网络通信模型中应用层的协定。
- 客户端浏览器被动向服务器发动申请,服务器收到后进行响应。
客户端申请
- 客户端在申请的信息的第一行中,携带了客户端想要申请的资源门路。
服务器端响应
- 响应中必须告知客户端响应的后果。
- 200状态码示意响应胜利。
- Content-Type:text/html 告知浏览器响应的内容是文本/网页内容。
4.3-代码实现
代码
public class WebSever { public static void main(String[] args) throws IOException { // 创立ServerSocket对象 ServerSocket server = new ServerSocket(8888); while (true){ new Thread(new Runnable() { @Override public void run() { try{ // 获取socket对象 Socket socket = server.accept(); // 读取接管的内容 BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = reader.readLine(); // GET /web/index.html HTTP/1.1 String path = line.split(" ")[1].substring(1); // 创立本地字节输出流 FileInputStream fis = new FileInputStream("day09_Socket\\" +path); // 创立网络输入流 OutputStream os = socket.getOutputStream(); // 写入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()); int len = 0; byte[]bts = new byte[1024]; while((len=fis.read(bts))!=-1){ os.write(bts,0,len); } // 敞开资源 os.close(); fis.close(); reader.close(); socket.close(); } catch (IOException e){ e.printStackTrace(); } } }).start(); } }}
成果
浏览器输出地址127.0.0.1:8888/web/index.html
拜访