1.1 概述Java 是 Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的 网络应用程序。Java 提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机 安装系统里,由JVM 进行控制。并且 Java 实现了一个跨平台的网络库, 程序员面对的是一个统一的网络 编程环境。计算机网络:把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大,功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件,软件,数据信息等资源。网络编程的目的:直接或间接地通过网络协议与其他计算机实现数据交换,进行通讯。网络编程中有两个主要的问题:1.如何准确的定位网络上的一台或多台主机,定位主机上的特定的应用2. 找到主机后如何可靠高效地进行数据传输网络编程 ! = 网页编程( web 开发)B/S 架构 和 C/S 架构1.2 网络通信两个要素如何实现网络中的主机互相通信?通信双方的地址 :
- IP
- 端口号
一定的规则:(即,网络通信协议,有两套参考模型)
小总结:1. 网络编程中有两个主要的问题:
- 如何准确的定位网络上一台或多台主机;定位主机上的特定的应用
- 找到主机后如何可靠高效的进行数据传输
2. 网络编程中的两个要素:
- ip 和 端口号
- 提供网络通信协议。 TCP/IP参考模型(应用层,传输层,网络层,物理+数据链路层),
1.3 、 Inet Adderssip 地址: Inet Adderss
- 唯一的标识 internet 上的计算机 ( 通信实体 )
- 本地回环地址(hostAddress):127.0.0.1 主机名 ( hostName ):localhost
- IP地址分类方式一 : IPV4 IPV6
- IPV4:4个字节组成,4个0~255。大概42亿个, 30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
- IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间。用冒号 隔开,如:2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b
- IP地址分类方式2:公网地址(万维网使用) 和 私有地址(局域网使用)。
- 192.168.开头的就是私有地址,范围即为 192.168.0.0 ~ 192.168.255.255,专门为组织机构内部使用
- 【查看 JDK 帮助文档=> InetAddress类,代表IP】特点:不便于记忆,我们常使用域名来访问:www.baidu.com
- https://blog.kuangstudy.com/ => DNS 域名解析(150.109.117.44)=> 现在本机的hosts文件,
- 判断是否有输入的域名地址,没有的话,再通过DNS服务器,找主机。
- hosts文件地址: c:\windows\system32\drivers\etc\hosts
import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; //测试IP //由于没有构造器,不能new出来。只能利用静态方法 public class TestInetAddress { public static void main(String[] args) { try { //查询本机地址 InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1"); System.out.println(inetAddress1); InetAddress inetAddress3 = InetAddress.getByName("localhost"); System.out.println(inetAddress3); InetAddress inetAddress4 = InetAddress.getLocalHost(); System.out.println(inetAddress4); //查询网站ip地址 InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com"); System.out.println(inetAddress2); //常用方法 System.out.println(Arrays.toString(inetAddress2.getAddress())); //返回一个数组 System.out.println(inetAddress2.getCanonicalHostName()); //规范的名字 System.out.println(inetAddress2.getHostAddress()); //ip System.out.println(inetAddress2.getHostName()); //域名,或者自己电脑的名字 } catch (UnknownHostException e) { e.printStackTrace(); } } }
1.4 、端口号端口号标识正在计算机上运行的进程(程序)不同的进程有不同的端口号,用来区分软件被规定为一个16 位的整数 0~65535
- TCP 和 UDP 各有 65535个端口,单个协议下端口不能冲突
- 端口分类:
- 公认端口: 0~1023。被预先定义的服务通信占用端口。
- HTTP 默认端口 : 80
- HTTPS 默认端口:443
- FTP 默认端口: 21
- Telnet 默认端口:23
- 注册端口:1024~49151、分配给用户进程或应用程序。
- tomcat 默认端口:8080
- Mysql 默认端口:3306
- Oracle 默认端口:1521
- 动态、私有端口:49152~65535
netstat -ano #查看所有的端口 netstat -ano|findstr "5900" #查看指定的端口 tasklist|findstr "8696" #查看指定端口的进程
//端口 public class TestInetSocketAddress { public static void main(String[] args) { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080); InetSocketAddress socketAddress2 = new InetSocketAddress("localhost",8080); System.out.println(socketAddress); System.out.println(socketAddress2); System.out.println(socketAddress.getAddress()); System.out.println(socketAddress.getHostName()); //地址 System.out.println(socketAddress.getPort()); //端口 }
找到电脑上特定端口,或有其对应的处理程序,才能收到发出的程序
InetSocketAddress IP 地址及端口 InetAddress IP 地址
1.5 网络通信协议网络通信协议: 计算机网络中实现通信必须有一些约定,即通信协议,对速率,传输代码,代码结构,传输控制步骤, 出错控制等制定标准。问题:网路协议太复杂?计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?通信协议分层的思想在制定协议时,把复杂成份分解成一些简单的成份,再将他们复合起来。最常用的复合方式是层次方式,即同层间可以通信,上一层调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开
发和扩展。
TCP/IP 协议簇传输层协议中有两个非常重要的协议:
- 用户传输协议 TCP (Transmission Control Protocol)
- 用户数据报协议(User Datagram Protocol)
- Tcp/IP 以其两个主要协议: 传输控制协议:TCP,和网络互联协议:IP,而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
- IP(Internet Protocol)协议是网络层的主要协议,支持网间互联的数据通信。
- TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层,IP层,传输层和应用层
TCP 和 UDP 对比TCP 协议
- 使用TCP协议前,必须建立TCP连接,形成传输数据通道;
- 传输前,采用 ‘ 三次握手 ’ 方式,点对点通信,是可靠的。
- TCP协议进行通信的两个应用进程:客户端,服务端。
- 在连接中可进行大数据量的传输
- 传输完毕,需要释放已建立的连接,效率低
- 举例:打电话
2 、 TCP 网络编程2.1 、案例一需求:客户端发送信息给服务端,服务端将数据显示在控制台上。
服务端import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; //服务端 public class TcpServerDemo01 { public static void main(String[] args) { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1.建立一个地址 serverSocket = new ServerSocket(9999); int i = 0; while (true){ //2.等待客户端连接过来 System.out.println("等待客户端连接"); socket = serverSocket.accept(); //3.读取客户端的信息 i++; is =socket.getInputStream(); System.out.println("读取信息成功"+i); baos = new ByteArrayOutputStream(); //创建一个接收数据的byte[]数组,及数组的有效长度len byte[] buffer = new byte[1024]; int len; while ((len=is.read(buffer))!=-1){ baos.write(buffer,0,len); } System.out.println(baos.toString()); } } /*//一种方法 //创建一个接收数据的byte[]数组,及数组的有效长度len byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer))!=-1){ String msg = new String(buffer,0,len); System.out.println(msg); }*/ //管道流获得信息 catch (IOException e) { e.printStackTrace(); }finally { //关闭资源,先开后关,后开先关。 if (baos!=null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket!=null){ try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; //客户端 public class TcpClientDemo01 { public static void main(String[] args) { Socket socket = null; OutputStream os = null; try { //1.要知道服务器的地址,和端口号 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); int port = 9999; System.out.println("客户端连接成功"); //2.创建一个 socket 连接 socket = new Socket(serverIP,port); //3.发送消息 IO 流 os = socket.getOutputStream(); os.write("你好,欢迎学习狂神说Java".getBytes()); System.out.println("已发送"); } catch (IOException e) { e.printStackTrace(); } //保证代码的严谨性 if (socket!= null){try { socket.close(); } catch (IOException e) { e.printStackTrace(); }} if (os!=null){try { os.close(); } catch (IOException e) { e.printStackTrace(); }} } }
ServerSocket 建立服务的端口 ServerSocket.accept 阻塞监听等待连接 Socket 创建连接 .getInputStream IO输入流 .getOutputStream IO输出流 ByteArrayOutputStream byte类型数组管道输出流
2.2 、案例二需求:客户端发送文件给服务器,服务端将文件保存在本地。我们需要准备一个图片,放在项目目录下。
服务端:客户端:import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class TcpClientDemo02 { public static void main(String[] args) throws IOException { //1.创建服务 ServerSocket serverSocket = new ServerSocket(9000); //2.监听客户端的连接 System.out.println("等待连接"); Socket socket = serverSocket.accept(); //阻塞式监听,会一直等待客户端连接 //3.获取输入流 InputStream is = socket.getInputStream(); //文件输出 FileOutputStream fos = new FileOutputStream(new File("666.jpg")); byte[] buffer = new byte[1024]; int len; while ((len=is.read(buffer))!=-1){ fos.write(buffer,0,len); } /* 客户端传输完了,服务器接收完 通知客户端我接收完毕了 */ //服务器端接收完毕,并回复信息 OutputStream os = socket.getOutputStream(); os.write("我接收完毕,你可以断开".getBytes()); //关闭资源 os.close(); fos.close(); is.close(); socket.close(); serverSocket.close(); } }
import java.io.*; import java.net.InetAddress; import java.net.Socket; public class TcpServerDemo02 { public static void main(String[] args) throws Exception { //1.创建一个Socket连接 // Socket(主机--端口号) //InetAddress.getByNam(主机--IP地址) Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000); //2.创建一个输出流 OutputStream os = socket.getOutputStream(); //3.读取文件 FileInputStream fis = new FileInputStream(new File("321.jpg")); //4.写出文件 byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer))!=-1){ os.write(buffer,0,len); } /* 传输完后,通知服务器 确定服务器接收完毕,才能断开连接 */ //客户端已经传输完毕 socket.shutdownOutput(); //接收服务端完毕信息 InputStream inputStream = socket.getInputStream(); //由于收到的式String byte[]数组,使用byte输出管道流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer2 = new byte[1024]; int len2; while((len2 = inputStream.read(buffer))!=-1){ baos.write(buffer,0,len2); } System.out.println(baos.toString()); //关闭资源 baos.close(); inputStream.close(); fis.close(); os.close(); socket.close(); } }
2.5 Tomcat 服务器客户端 :自定义 、浏览器服务端 :自定义 、Tomcat服务器1. 将提供的 tomcat 解压2. 运行 bin 目录下的启动文件3. 测试访问 localhost:80803 、 UDP 网络编程3.1 、说明
- DatagramSocket 和 DatagramPacket 两个类实现了基于UDP协议的网络程序。
- UDP 数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不确定什么时候可以抵达。
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
- UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接。如同发快递包裹一样。
- 发送方:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UdpClientDemo01 { public static void main(String[] args) throws Exception { //1.建立一个socket DatagramSocket socket = new DatagramSocket(8080); //1.2建立一个能发送的包 //发送的数据,地址 String msg = "你好啊,服务器!"; InetAddress localhost = InetAddress.getByName("localhost"); int port = 9090; //包的内容:数据,数据长度,要发送给谁 DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port); //2.发送包 socket.send(packet); //关闭流 socket.close(); } }
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UdpServerDemo02 { public static void main(String[] args) throws Exception { //开放段 //等待客户端的连接 DatagramSocket socket = new DatagramSocket(9090); //接收数据包 byte[] buffer = new byte[1024]; //接收临时区域 DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); //阻塞接收 socket.receive(packet); System.out.println(packet.getAddress().getHostAddress()); System.out.println(new String(packet.getData(),0,packet.getLength())); //关闭流 socket.close(); } }
两端可以相互发送消息:
DatagramSocket 数据包端口 DatagramPacket 数据包 .send 发送 .receive 阻塞接收
持续发送:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; public class UdpSenderDemo01 { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888); while(true){ //准备数据:控制台读取 System.in BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //发包内数据 String data = reader.readLine(); byte[] datas = data.getBytes(); DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",6666)); //发送包 socket.send(packet); if (data.equals("bye")){ break; } } //关闭流 socket.close(); } }
持续接收:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UdpReceiveDemo01 { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(6666); while(true){ //准备接收包裹 byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container,0,container.length); //阻塞式接收 socket.receive(packet); //断开连接 //将接收包转换为 String 格式 byte[] data = packet.getData(); String receiveData = new String(data,0,data.length); System.out.println(receiveData); if (receiveData.equals("bye")){ break; } } socket.close(); } }
BufferedReader 缓存区读取 InputStreamReader 输入流读取 .readLine 读取的一行内容
多线程发送:
发送方:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; public class TalkSend implements Runnable{ DatagramSocket socket = null; BufferedReader reader = null; private int fromPort; private String toIP; private int toPort; public TalkSend(int fromPort, String toIP, int toPort) { this.fromPort = fromPort; this.toIP = toIP; this.toPort = toPort; try { socket = new DatagramSocket(fromPort); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { try { while(true){ //准备数据:控制台读取 System.in BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //发包内数据 String data = reader.readLine(); byte[] datas = data.getBytes(); DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIP,this.toPort)); //发送包 socket.send(packet); if (data.equals("bye")){ break; } } } catch (Exception e) { e.printStackTrace(); } //关闭流 socket.close(); } }
接收方:
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class TalkReceive implements Runnable{ DatagramSocket socket =null; private int port; private String msgfrom; public TalkReceive(int port, String msgfrom) { this.port = port; this.msgfrom = msgfrom; try { socket = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { try { while(true){ //准备接收包裹 byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container,0,container.length); //阻塞式接收 socket.receive(packet); //断开连接 //将接收包转换为 String 格式 byte[] data = packet.getData(); String receiveData = new String(data,0,data.length); System.out.println(msgfrom+":"+receiveData); if (receiveData.equals("bye")){ break; } } } catch (Exception e) { e.printStackTrace(); } socket.close(); } }
4 URL 编程4.1 url 类
- URL (Uniform Resource Locator): 统一资源定位符,它表示 internet 上某一资源的地址。
- 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate:定位这个资源。
- 通过URL 我们可以访问Internet上的各种网络资源,比如最常见的 www,ftp站点。浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源。
- URL 的 基本结构由 5部分组成:
- 例如:http://localhost:8080/helloworld/index.jsp#a?username=kuangshen&password=1
- 23
- 片段名,即锚链接,比如我们去一些小说网站,可以直接定位到某个章节位置
- 参数列表格式 : 参数名=参数值 & 参数名=参数
package chat; import java.net.MalformedURLException; import java.net.URL; public class DRLDemo01 { public static void main(String[] args) throws MalformedURLException { URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=kuangshen&password==123"); //协议http System.out.println(url.getProtocol()); //主机ip,localhost System.out.println(url.getHost()); //端口,8080 System.out.println(url.getPort()); //文件,/helloworld/index.jsp System.out.println(url.getPath()); //全路径,/helloworld/index.jsp?username=kuangshen&password==123 System.out.println(url.getFile()); //参数,username=kuangshen&password==123 System.out.println(url.getQuery()); } }