Java网络编程
1 - 网络编程概述
1 Java是 Internet 上的语言,它从语言级上提供了对网络应用程 序的支持,程序员能够很容易开发常见的网络应用程序。
2 Java提供的网络类库,可以实现无痛的网络连接,联网的底层 细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一 的网络编程环境
2 - 网络基础
1 计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规 模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、 共享硬件、软件、数据信息等资源。
2 网络编程的目的:
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
3 网络编程中有两个主要的问题:
①如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
②找到主机后如何可靠高效地进行数据传输
3 - 网络通信要素概述
1 IP地址和端口号
2 网络通信协议
如何实现网络中的主机互相通信?
1 通信双方地址
IP
端口号
2 一定的规则(即:网络通信协议。有两套参考模型)
OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
4 - 通信要素1:IP和端口号
1 IP 地址:InetAddress(在Java中使用InetAddress类代表IP)
✔ 唯一的标识 Internet 上的计算机(通信实体)
✔ 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
✔ P地址分类方式1:IPV4 和 IPV6
✔ IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已 经用尽。以点分十进制表示,如192.168.0.1
✔ IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示, 数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
✔ IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168. 开头的就是私有址址,范围即为192.168.0.0--192.168.255.255,专门为组织机 构内部使用
✔ 特点:不易记忆
2 端口号:标识正在计算机上运行的进程(程序)
✔ 不同的进程有不同的端口号
✔ 被规定为一个 16 位的整数 0~65535。
✔ 端口分类:
① 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口 80,FTP占用端口21,Telnet占用端口23)
② 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占 用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
③ 动态/私有端口:49152~65535
3 端口号与IP地址的组合得出一个网络套接字:Socket
4 InetAddress类
✔ Internet上的主机有两种方式表示地址:
①域名(hostName):www.baidu.com
②IP 地址(hostAddress):14.215.177.38
✔ InetAddress类主要表示IP地址,两个子类:Inet4Address、Inet6Address
✔ InetAddress 类对象含有一个 Internet 主机地址的域名和IP地址:www.baidu.com 和 14.215.177.38
✔ 域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS) 负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析
✔ InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取 InetAddress实例
1 public static InetAddress getLocalHost()
2 public static InetAddress getByName(String host)
✔ InetAddress提供了如下几个常用的方法
1 public String getHostAddress():返回 IP 地址字符串(以文本表现形式)。
2 public String getHostName():获取此 IP 地址的主机名
3 public boolean isReachable(int timeout):测试是否可以达到该地址
package com.lzh.java2; import java.net.InetAddress; import java.net.UnknownHostException; /* 1 java中使用InetAddress代表IP 2 IP的分类:IPV4和IPV6 万维网和局域网 3 域名:www.baidu.com www.cnblogs.com 4 本地回路地址:127.0.0.1 对应着:localhost 5 如何实例化InetAddress类的对象,两个静态方法: InetAddress getByName(String host) InetAddress getLocalHost() 6 两个常用方法 getHostName() getHostAddress() 7 端口号:正在计算机上运行的进程 */ public class InetAddressTest { public static void main(String[] args){ try { // File file = new File("test.txt"); InetAddress IP1 = InetAddress.getByName("192.168.3.2"); System.out.println(IP1); InetAddress IP2 = InetAddress.getByName("www.baidu.com"); System.out.println(IP2); // 获取本地IP InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost); // getHostName() System.out.println(IP2.getHostName()); // getHostAddress() System.out.println(IP2.getHostAddress()); } catch(UnknownHostException e){ e.printStackTrace(); } } }
5 - 通信要素2:网络协议
1 网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代 码结构、传输控制步骤、出错控制等制定标准。
2 问题:网络协议太复杂
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩 解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
3 通信协议分层的思想
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常 用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与 再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
4 TCP/IP协议簇
✔ 传输层协议中有两个非常重要的协议:
1 传输控制协议TCP(Transmission Control Protocol)
2 用户数据报协议UDP(User Datagram Protocol)。
✔ TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得 名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
✔ IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。
✔ TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即 物理链路层、IP层、传输层和应用层
5 TCP 和 UDP
1 TCP协议:
✔ 使用TCP协议前,须先建立TCP连接,形成传输数据通道
✔ 传输前,采用“三次握手”方式,点对点通信,是可靠的
✔ TCP协议进行通信的两个应用进程:客户端、服务端。
✔ 在连接中可进行大数据量的传输
✔ 传输完毕,需释放已建立的连接,效率低
2 UDP协议:
✔ 将数据、源、目的封装成数据包,不需要建立连接
✔ 每个数据报的大小限制在64K内
✔ 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
✔ 可以广播发送
✔ 发送数据结束时无需释放资源,开销小,速度快
6 三次握手
7 四次挥手
6 - Socket介绍
✔ 利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实 上的标准。
✔ 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标 识符套接字。
✔ 通信的两端都要有Socket,是两台机器间通信的端点。
✔ 网络通信其实就是Socket间的通信。
✔ Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
✔ 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
✔ Socket分类:
1 流套接字(stream socket):使用TCP提供可依赖的字节流服务
2 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
7 - TCP网络编程
1 基于Socket的TCP编程
✔ Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模 型如图所示
✔ 客户端Socket的工作过程包含以下四个基本的步骤:
1 创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务端的通信路线。若连接失败,则会出现异常。
2 打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream()方法获得输出流,进行数据传输
3 按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入路线的信息),通过输出流将信息写入线程
4 关闭 Socket:断开客户端到服务器的连接,释放线路
✔ 客户端创建Socket对象
1 客户端程序可以使用Socket类创建对象,创建的同时会自动向服务器方发起连 接。Socket的构造器是:
①Socket(String host,int port)throws UnknownHostException,IOException:向服务器(域名是 host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
②Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的 IP地址以及端口号port发起连接。
2 客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请
1 Socket s = new Socket(“192.168.40.165”,9999); 2 OutputStream out = s.getOutputStream(); 3 out.write(" hello".getBytes()); 4 s.close();
✔ 服务器程序的工作过程包含以下四个基本的步骤:
1 调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口 上。用于监听客户端的请求。
2 调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信 套接字对象。
3 调用 该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出 流和输入流,开始网络数据的发送和接收。
4 关闭ServerSocket和Socket对象:客户端访问结束,关闭通信套接字。
✔ 服务器建立 ServerSocket 对象
1 ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口 中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字 连接的ServerSocket对象。
2 所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象
1 ServerSocket ss = new ServerSocket(9999); 2 Socket s = ss.accept (); 3 InputStream in = s.getInputStream(); 4 byte[] buf = new byte[1024]; 5 int num = in.read(buf); 6 String str = new String(buf,0,num); System.out.println(s.getInetAddress().toString()+”:”+str); s.close(); ss.close();
8 - TCP网络编程:例题练习
1 客户端发送内容给服务端,服务端将内容打印到控制台上。
package com.lzh.java2; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /* 实现TCP网络编程: 例1:客户端发送信息给服务端,服务端将信息输出 */ public class TCPTest { @Test // 客户端 public void client() { Socket socket = null; OutputStream writer = null; try { InetAddress IP= InetAddress.getByName("127.0.0.1"); socket = new Socket(IP,8090); // 第1步:创建Socket对象,指定服务器的IP和端口号 writer = socket.getOutputStream(); // 第2步:获取一个输出流,用于输入数据 writer.write("hello server".getBytes()); // 第3步:写出数据 } catch (IOException e) { e.printStackTrace(); } finally { // 第4步:资源的回收 if(writer != null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test // 服务端 public void server() { ServerSocket server = null; Socket socket = null; InputStream reader = null; ByteArrayOutputStream byteArray = null; try { server = new ServerSocket(8090); // 第1步:创建服务端的ServerSocket对象,指明服务端的端口号 socket = server.accept(); // 第2步:调用accept()表示接受客户端的Socket reader = socket.getInputStream(); // 第3步:获取输入流,用于输出数据 byteArray = new ByteArrayOutputStream(); // 第4步:对数据进行处理 byte[] buffer = new byte[5]; int len; while((len = reader.read(buffer)) != -1){ byteArray.write(buffer,0,len); } System.out.println(byteArray.toString()); System.out.println("客户端IP:" + socket.getInetAddress().getHostName()); } catch (IOException e) { e.printStackTrace(); } finally { // 第5步:资源的回收 if(server != null){ try { server.close(); } catch (IOException e) { e.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(reader != null){ try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } if(byteArray != null){ try { byteArray.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2 客户端发送文件给服务端,服务端将文件保存在本地
package com.lzh.java2; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; /* 例2:客户端发送文件给服务端,服务端将文件保存在本地 */ public class TCPSocketTest { @Test public void client() { Socket socket = null; OutputStream writer = null; BufferedInputStream bis = null; try { socket = new Socket(InetAddress.getByName("127.0.0.1"),8089); writer = socket.getOutputStream(); bis = new BufferedInputStream(new FileInputStream(new File("me.jpg"))); byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ writer.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(writer != null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); System.out.println("发送成功!"); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server() throws IOException { // 这里异常应该使用try-catch-finally ServerSocket socket = new ServerSocket(8089); System.out.println("正在等待客户端连接..."); Socket clientSocket = socket.accept(); System.out.println("客户端已连接IP地址为:"+clientSocket.getInetAddress().getHostName()); InputStream is = clientSocket.getInputStream(); BufferedOutputStream reader = new BufferedOutputStream(new FileOutputStream(new File("new_me.jpg"))); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ reader.write(buffer,0,len); } System.out.println("接收成功"); socket.close(); clientSocket.close(); is.close(); reader.close(); } }
3 从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给 客户端。并关闭相应的连接
package com.lzh.java2; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /* 例3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给 客户端。并关闭相应的连接。 */ public class TCPSocketTest2 { @Test // 客户端 public void client() { Socket socket = null; OutputStream writer = null; BufferedInputStream bis = null; try { socket = new Socket(InetAddress.getByName("127.0.0.1"),8089); writer = socket.getOutputStream(); bis = new BufferedInputStream(new FileInputStream(new File("me.jpg"))); byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ writer.write(buffer,0,len); } // 关闭数据的输出 socket.shutdownOutput(); // 接收服务端反馈的信息并输出到控制台 InputStream is = socket.getInputStream(); ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); byte[] buf = new byte[10]; int l; while((l = is.read(buf)) != -1){ byteArray.write(buf,0,l); } System.out.println(byteArray.toString()); is.close(); byteArray.close(); } catch (IOException e) { e.printStackTrace(); } finally { if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if(writer != null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); System.out.println("发送成功!"); } catch (IOException e) { e.printStackTrace(); } } } } @Test // 服务端 public void server() throws IOException { // 这里异常应该使用try-catch-finally ServerSocket socket = new ServerSocket(8089); System.out.println("正在等待客户端连接..."); Socket clientSocket = socket.accept(); System.out.println("客户端已连接IP地址为:"+clientSocket.getInetAddress().getHostName()); InputStream is = clientSocket.getInputStream(); BufferedOutputStream reader = new BufferedOutputStream(new FileOutputStream(new File("new_me.jpg"))); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ reader.write(buffer,0,len); } System.out.println("接收成功"); // 服务端给客户端反馈信息 OutputStream os = clientSocket.getOutputStream(); os.write("你好客户端,照片已经收到".getBytes()); socket.close(); clientSocket.close(); is.close(); reader.close(); os.close(); } }
9 - UDP网络编程(了解)
UDP网络通信
✔ 类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。
✔ UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证 UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
✔ DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP 地址和端口号以及接收端的IP地址和端口号。
✔ UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和 接收方的连接。如同发快递包裹一样
UDP网络通信流 程:
1. DatagramSocket与DatagramPacket
2. 建立发送端,接收端
3. 建立数据包
4. 调用Socket的发送、接收方法
5. 关闭Socket
发送端与接收端是两个独立的运行程序
package com.lzh.java3; import org.junit.Test; import java.io.IOException; import java.net.*; /* UDP网络编程: ✔ 将数据、源、目的封装成数据包,不需要建立连接 ✔ 每个数据报的大小限制在64K内 ✔ 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的 ✔ 可以广播发送 ✔ 发送数据结束时无需释放资源,开销小,速度快 */ public class UDPSocketTest { @Test // 发送端 public void send() throws IOException { DatagramSocket socket = new DatagramSocket(); byte[] data = "hello world".getBytes(); DatagramPacket packet = new DatagramPacket(data,0,data.length, InetAddress.getLocalHost(),8080); socket.send(packet); socket.close(); } @Test // 接收端 public void receiver() throws IOException { DatagramSocket socket = new DatagramSocket(8080); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); socket.close(); } }
10 - URL编程
URL介绍
✔ URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一 资源的地址。
✔ 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate 这个资源。
✔ 通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
✔ URL的基本结构由5部分组成: <传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
1 例如: http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
2 #片段名:即锚点,例如看小说,直接定位到章节
3 参数列表格式:参数名=参数值&参数名=参数值....
URL类与类的构造器
1 为了表示URL,java.net 中实现了类 URL。我们可以通过下面的构造器来初 始化一个 URL 对象:
✔ public URL (String spec):通过一个表示URL地址的字符串可以构造一个URL对象。例 如:URL url = new URL ("http://www. atguigu.com/");
✔ public URL(URL context, String spec):通过基 URL 和相对 URL 构造一个 URL 对象。 例如:URL downloadUrl = new URL(url, “download.html")
✔ public URL(String protocol, String host, String file); 例如:new URL("http", "www.atguigu.com", “download. html");
✔ public URL(String protocol, String host, int port, String file); 例如: URL gamelan = new URL("http", "www.atguigu.com", 80, “download.html");
2 URL类的构造器都声明抛出非运行时异常,必须要对这一异常进行处理,通 常是用 try-catch 语句进行捕获
URL常用方法
一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的 方法来获取这些属性:
✔ public String getProtocol( ) 获取该URL的协议名
✔ public String getHost( ) 获取该URL的主机名
✔ public String getPort( ) 获取该URL的端口号
✔ public String getPath( ) 获取该URL的文件路径
✔ public String getFile( ) 获取该URL的文件名
✔ public String getQuery( ) 获取该URL的查询名
package com.lzh.java3; import java.net.MalformedURLException; import java.net.URL; /* URL网络编程: 1 URL:统一资源定位符,对应着某一资源地址 2 URL格式:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表 */ public class URLTest { public static void main(String[] args) { try { URL url = new URL("https://www.bilibili.com/video/BV1zE41197bw?p=753"); // public String getProtocol() 获取该URL的协议名 System.out.println(url.getProtocol()); // public String getHost() 获取该URL的主机名 System.out.println(url.getHost()); // public String getPort() 获取该URL的端口号 System.out.println(url.getPort()); // public String getPath() 获取该URL的文件路径 System.out.println(url.getPath()); // public String getFile() 获取该URL的文件名 System.out.println(url.getFile()); // public String getQuery() 获取该URL的查询名 System.out.println(url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); } } }