15 - 网络编程
1. 概述
计算机网络:指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程的目的:传播交流信息、数据交换、通信。
达到效果需要什么:①如何准确地定位网络上的一台主机:×××.×××.×××.×××: port,定位到这个计算机上额某个资源;②找到了这个主机,如何传输数据?
javaweb -> 网页编程,B/S架构;网络编程 -> TCP/IP,C/S架构。
2. 网络通信的要素
如何实现网络的通信?
-
通信双方地址:IP+端口号
-
规则:网络通信协议(TCP/IP参考模型)
3. IP地址
IP地址:InetAddress
-
唯一定位一台网络上的计算机
-
127.0.0.1:本机localhost
-
IP地址的分类:
-
IPV4/IPV6
-
IPV4:4个字节组成(127.0.0.1),每个字节0~255,大概42亿个,2011年已用尽;
-
IPV6:128位,8个无符号整数
-
-
公网(互联网)/私网(局域网,192.168.××.××,专门给组织内部使用的)
-
-
域名:IP记忆问题
1 package com.wang.demo01; 2 3 import java.net.InetAddress; 4 import java.net.UnknownHostException; 5 6 // 测试IP 7 public class TestInetAddress { 8 public static void main(String[] args) { 9 try { 10 // 查询本机地址 11 InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1"); 12 System.out.println(inetAddress1); 13 InetAddress inetAddress2 = InetAddress.getByName("localhost"); 14 System.out.println(inetAddress2); 15 InetAddress inetAddress3 = InetAddress.getLocalHost(); 16 System.out.println(inetAddress3); 17 18 // 查询网站IP地址 19 InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com"); 20 System.out.println(inetAddress4); 21 22 // 常用方法 23 System.out.println(inetAddress4.getAddress()); 24 System.out.println(inetAddress4.getCanonicalHostName()); // 规范名字 25 System.out.println(inetAddress4.getHostAddress()); // IP 26 System.out.println(inetAddress4.getHostName()); // 域名,或者自己电脑的名字 27 } catch (UnknownHostException e) { 28 e.printStackTrace(); 29 } 30 } 31 }
4. 端口
端口表示计算机上的一个程序的进程:
-
不同的进程有不同的端口号,用来区分软件;
-
被规定在0~65535;
-
TCP+UDP: 65535*2,单个协议下,端口号不能冲突;
-
端口分类:
-
公有端口(0~1023):HTTP: 80, HTTPS: 443, FTP: 21, Telent: 23;
-
程序注册端口(1024~49151):分配给用户或者程序,Tomcat: 8080, MySQL: 3306, Oracle: 1521;
-
动态、私有端口(49152~65535):尽量不要使用
netstat -ano # 查看所有的端口 netstat -ano|findstr "5900" # 查看指定的端口 tasklist|findstr "8696" # 查看指定端口的进程 Ctrl + shift + ESC # 打开任务管理器的快捷键
-
1 package com.wang.demo01; 2 3 import java.net.InetSocketAddress; 4 5 public class TestInetSocketAddress { 6 public static void main(String[] args) { 7 InetSocketAddress socketAddress1 = new InetSocketAddress("127.0.0.1", 8080); 8 InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080); 9 System.out.println(socketAddress1); 10 System.out.println(socketAddress2); 11 12 System.out.println(socketAddress1.getAddress()); 13 System.out.println(socketAddress1.getHostName()); 14 System.out.println(socketAddress1.getPort()); 15 } 16 } 17 // 输出: 18 /127.0.0.1:8080 19 localhost/127.0.0.1:8080 20 /127.0.0.1 21 127.0.0.1 22 8080
5. 通信协议
协议:约定
网络通信协议:速率,传输码率,代码结构,传输控制……
复杂问题 -> 分层
TCP/IP协议簇:实质上是一组协议
重点:
-
TCP:用户传输协议
-
UDP:用户数据报协议
TCP和UDP对比:
-
TCP:打电话
-
连接,稳定
-
三次握手,四次挥手
-
最少需要三次以保证稳定连接:
-
-
-
客户端,服务端
-
传输完成,释放连接,效率低
-
-
UDP:发短信
-
不连接,不稳定
-
客户端,服务端:没有明确的界限
-
不管有没有准备好,都可以发送信息
-
DDoS:洪水攻击,分布式拒绝服务攻击
-
6. TCP
6.1 TCP聊天实现
服务器端:
-
建立服务的端口ServerSocket;
-
等待用户的连接 accept;
-
接收用户的消息。
1 package com.wang.demo02; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 // 服务器 10 public class TcpServerTest01 { 11 public static void main(String[] args){ 12 ServerSocket serverSocket = null; 13 Socket socket = null; 14 InputStream is = null; 15 ByteArrayOutputStream baos = null; 16 try { 17 // 1. 先创建一个服务器地址 18 serverSocket = new ServerSocket(9999); 19 // 2. 等待客户端的连接请求 20 socket = serverSocket.accept(); 21 // 3. 读取客户端的消息 22 is = socket.getInputStream(); 23 24 // 管道流 25 baos = new ByteArrayOutputStream(); 26 byte[] buffer = new byte[1024]; 27 int len; 28 while ((len=is.read(buffer))!=-1){ 29 baos.write(buffer,0,len); 30 } 31 System.out.println(baos.toString()); 32 } catch (IOException e) { 33 e.printStackTrace(); 34 }finally { 35 if (baos!=null){ 36 try { 37 baos.close(); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 } 42 if (is!=null){ 43 try { 44 is.close(); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 if (socket!=null){ 50 try { 51 socket.close(); 52 } catch (IOException e) { 53 e.printStackTrace(); 54 } 55 } 56 if (serverSocket!=null){ 57 try { 58 serverSocket.close(); 59 } catch (IOException e) { 60 e.printStackTrace(); 61 } 62 } 63 } 64 } 65 }
客户端:
-
连接服务器Socket;
-
发送消息。
1 package com.wang.demo02; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.net.InetAddress; 6 import java.net.Socket; 7 8 // 客户端 9 public class TcpClientTest01 { 10 public static void main(String[] args){ 11 Socket socket = null; 12 OutputStream os = null; 13 try { 14 // 1. 获取服务器的地址,端口号 15 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); 16 int port = 9999; 17 // 2. 创建一个socket连接 18 socket = new Socket(serverIP, port); 19 // 3. 发送消息 IO流 20 os = socket.getOutputStream(); 21 os.write("Hello, world!".getBytes()); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 }finally { 25 if (os!=null){ 26 try { 27 os.close(); 28 } catch (IOException e) { 29 e.printStackTrace(); 30 } 31 } 32 if (socket!=null){ 33 try { 34 socket.close(); 35 } catch (IOException e) { 36 e.printStackTrace(); 37 } 38 } 39 } 40 } 41 }
6.2 TCP文件上传
服务器端:
1 package com.wang.demo02; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 10 public class TcpServerTest02 { 11 public static void main(String[] args) throws Exception { 12 // 1. 创建服务 13 ServerSocket serverSocket = new ServerSocket(9000); 14 // 2. 监听客户端的连接 15 Socket socket = serverSocket.accept(); // 阻塞式监听,会一直等待客户端的连接 16 // 3. 获取输入流 17 InputStream is = socket.getInputStream(); 18 // 4. 文件输出 19 FileOutputStream fos = new FileOutputStream(new File("recieved.jpg")); 20 byte[] buffer = new byte[1024]; 21 int len; 22 while ((len=is.read(buffer))!=-1){ 23 fos.write(buffer,0,len); 24 } 25 // 通知客户端接收完毕 26 OutputStream os = socket.getOutputStream(); 27 os.write("Done!".getBytes()); 28 // 5. 关闭资源 29 fos.close(); 30 is.close(); 31 socket.close(); 32 serverSocket.close(); 33 } 34 }
客户端:
1 package com.wang.demo02; 2 3 import java.io.*; 4 import java.net.Socket; 5 6 public class TcpClientTest02 { 7 public static void main(String[] args) throws Exception { 8 // 1. 创建一个Socket连接 9 Socket socket = new Socket("127.0.0.1", 9000); 10 // 2. 创建一个输出流 11 OutputStream os = socket.getOutputStream(); 12 // 3. 读取文件 13 FileInputStream fis = new FileInputStream(new File("JungKook.jpg")); 14 // 4. 写出文件 15 byte[] buffer = new byte[1024]; 16 int len; 17 while ((len=fis.read(buffer))!=-1){ 18 os.write(buffer,0,len); 19 } 20 // 通知服务器,文件传输完毕 21 socket.shutdownOutput(); 22 // 确认服务器接收完毕,才能够断开连接 23 InputStream inputStream = socket.getInputStream(); 24 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 25 byte[] buffer2 = new byte[1024]; 26 int len2; 27 while ((len2=inputStream.read(buffer))!=-1){ 28 baos.write(buffer,0,len2); 29 } 30 System.out.println(baos.toString()); 31 // 5. 关闭资源 32 fis.close(); 33 os.close(); 34 socket.close(); 35 } 36 }
Tomcat
服务器端:
-
自定义 S
-
Tomcat服务器 S:Java后台开发
客户端:
-
自定义 C
-
浏览器 B
7. UDP
不用连接,但需要知道对方的地址
7.1 UDP消息发送
发送端:
1 package com.wang.demo03; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 7 // 不需要连接服务器 8 public class UdpClientTest01 { 9 public static void main(String[] args) throws Exception { 10 // 1. 建立一个Socket 11 DatagramSocket socket = new DatagramSocket(); 12 // 2. 创建数据包 13 String msg = "Hello, world!"; 14 InetAddress localhost = InetAddress.getByName("localhost"); 15 int port = 9090; 16 // param:数据,数据起始,数据长度,IP地址,端口 17 DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port); 18 // 3. 发送数据包 19 socket.send(packet); 20 // 4. 关闭流 21 socket.close(); 22 } 23 }
接收端:
1 package com.wang.demo03; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 6 public class UdpServerTest01 { 7 public static void main(String[] args) throws Exception { 8 // 1. 开放端口 9 DatagramSocket socket = new DatagramSocket(9090); 10 // 2. 接收数据包 11 byte[] buffer = new byte[1024]; 12 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); 13 socket.receive(packet); // 阻塞接收 14 System.out.println(packet.getAddress().getHostAddress()); 15 System.out.println(new String(packet.getData())); 16 // 3. 关闭连接 17 socket.close(); 18 } 19 }
7.2 UDP聊天实现
单工通信:一方是发送方,另一方是接收方
发送方:
1 package com.wang.udpchat; 2 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 import java.net.DatagramPacket; 6 import java.net.DatagramSocket; 7 import java.net.InetSocketAddress; 8 9 public class UdpSenderDemo01 { 10 public static void main(String[] args) throws Exception { 11 DatagramSocket socket = new DatagramSocket(8888); 12 13 // 准备发送数据,控制台读取 14 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 15 while (true){ 16 String data = reader.readLine(); 17 byte[] sendData = data.getBytes(); 18 DatagramPacket packet = new DatagramPacket(sendData, 0, sendData.length, new InetSocketAddress("localhost", 6666)); 19 socket.send(packet); 20 if (data.equals("bye")){ 21 break; 22 } 23 } 24 socket.close(); 25 } 26 }
接收方:
1 package com.wang.udpchat; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 6 public class UdpRecieverDemo01 { 7 public static void main(String[] args) throws Exception { 8 DatagramSocket socket = new DatagramSocket(6666); 9 10 while (true){ 11 // 准备接收数据 12 byte[] buffer = new byte[1024]; 13 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); 14 socket.receive(packet); // 阻塞式接收数据 15 16 // 断开连接 17 byte[] data = packet.getData(); 18 String recieveData = new String(data, 0, data.length); 19 System.out.println(recieveData); 20 21 if (recieveData.equals("bye")){ 22 break; 23 } 24 } 25 socket.close(); 26 } 27 }
半双工通信:双方都可以是发送方,也可以是接收方
发送端:
1 package com.wang.udpchat02; 2 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 import java.net.DatagramPacket; 6 import java.net.DatagramSocket; 7 import java.net.InetSocketAddress; 8 9 public class ChatSend implements Runnable{ 10 DatagramSocket socket = null; 11 BufferedReader reader = null; 12 13 private int fromPort; 14 private String toIP; 15 private int toPort; 16 17 public ChatSend(int fromPort, String toIP, int toPort) { 18 this.fromPort = fromPort; 19 this.toIP = toIP; 20 this.toPort = toPort; 21 22 try { 23 socket = new DatagramSocket(fromPort); 24 reader = new BufferedReader(new InputStreamReader(System.in)); 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } 28 } 29 30 @Override 31 public void run() { 32 while (true){ 33 try { 34 String data = reader.readLine(); 35 byte[] sendData = data.getBytes(); 36 DatagramPacket packet = new DatagramPacket(sendData, 0, sendData.length, new InetSocketAddress(this.toIP, this.toPort)); 37 socket.send(packet); 38 if (data.equals("bye")){ 39 break; 40 } 41 } catch (Exception e) { 42 e.printStackTrace(); 43 } 44 } 45 socket.close(); 46 } 47 }
接收端:
1 package com.wang.udpchat02; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 6 public class ChatRecieve implements Runnable{ 7 DatagramSocket socket = null; 8 private int port; 9 private String msgFrom; 10 11 public ChatRecieve(int port, String msgFrom) { 12 this.port = port; 13 this.msgFrom = msgFrom; 14 try { 15 socket = new DatagramSocket(port); 16 } catch (Exception e) { 17 e.printStackTrace(); 18 } 19 } 20 21 @Override 22 public void run() { 23 while (true){ 24 try { 25 // 准备接收数据 26 byte[] buffer = new byte[1024]; 27 DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); 28 socket.receive(packet); // 阻塞式接收数据 29 30 // 断开连接 31 byte[] data = packet.getData(); 32 String recieveData = new String(data, 0, data.length); 33 System.out.println(msgFrom + ": " + recieveData); 34 35 if (recieveData.equals("bye")){ 36 break; 37 } 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 } 42 socket.close(); 43 } 44 } 45 package com.wang.udpchat02; 46 47 public class ChatStudent { 48 public static void main(String[] args) { 49 // 开启两个线程 50 new Thread(new ChatSend(6666,"localhost",9999)).start(); 51 new Thread(new ChatRecieve(8888,"teacher")).start(); 52 } 53 } 54 package com.wang.udpchat02; 55 56 public class ChatTeacher { 57 public static void main(String[] args) { 58 // 开启两个线程 59 new Thread(new ChatSend(7777,"localhost",8888)).start(); 60 new Thread(new ChatRecieve(9999,"student")).start(); 61 } 62 }
8. URL
统一资源定位符:用于定位资源,定位互联网上的某个资源。组成:
协议://ip地址:端口/项目名/资源
URL解析:
1 package com.wang.urldemo; 2 3 import java.net.MalformedURLException; 4 import java.net.URL; 5 6 public class URLTest01 { 7 public static void main(String[] args) throws MalformedURLException { 8 URL url = new URL("http://localhost:8080/helloword/index.jsp?username=JungKook&password=970901"); 9 10 System.out.println(url.getProtocol()); // 协议 11 System.out.println(url.getHost()); // 主机IP 12 System.out.println(url.getPort()); // 端口 13 System.out.println(url.getPath()); // 文件 14 System.out.println(url.getFile()); // 全路径 15 System.out.println(url.getQuery()); // 参数 16 } 17 } 18 // 结果: 19 http 20 localhost 21 8080 22 /helloword/index.jsp 23 /helloword/index.jsp?username=JungKook&password=970901 24 username=JungKook&password=970901
URL网络资源下载:
1 package com.wang.urldemo; 2 3 import java.io.FileOutputStream; 4 import java.io.InputStream; 5 import java.net.HttpURLConnection; 6 import java.net.URL; 7 8 public class URLDownloadTest { 9 public static void main(String[] args) throws Exception { 10 // 1. 下载地址 11 URL url = new URL("https://c-ssl.duitang.com/uploads/item/201806/13/20180613121730_mhcxy.thumb.700_0.jpg"); 12 // 2. 连接地址 13 HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); 14 15 InputStream inputStream = urlConnection.getInputStream(); 16 FileOutputStream fos = new FileOutputStream("jk.jpg"); 17 byte[] buffer = new byte[1024]; 18 int len; 19 while ((len=inputStream.read(buffer))!=-1){ 20 fos.write(buffer,0,len); 21 } 22 fos.close(); 23 inputStream.close(); 24 urlConnection.disconnect(); // 断开连接 25 } 26 }