1 网络编程概述(了解)
计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程
就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。
2 网络编程三要素之IP概述(掌握)
每个设备在网络中的唯一标识
每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。
ipconfig:查看本机IP192.168.12.42
ping:测试连接192.168.40.62
本地回路地址:127.0.0.1 255.255.255.255是广播地址
IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。
IPv6:8组,每组4个16进制数。
1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f
1a2b::aaaa:0000:0000:0000:aabb:1f2f
1a2b:0000:aaaa::aabb:1f2f
1a2b:0000:aaaa::0000:aabb:1f2f
1a2b:0000:aaaa:0000::aabb:1f2f
3 网络编程三要素之端口号概述(掌握)
每个程序在设备上的唯一标识
每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。
端口号范围从0-65535
编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。
常用端口
mysql: 3306
oracle: 1521
web: 80
tomcat: 8080
QQ: 4000
feiQ: 2425
4 网络编程三要素协议(掌握)
为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
UDP 面向无连接,数据不安全,速度快。不区分客户端与服务端
(想象一下发短信,被发送人的手机即使没开机短信也能发出去,可是这个时候被发送人是接收不到信息的)
TCP 面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据
(想象一下打电话,被发送人的手机必须开机,而且你在接通电话之后才会和他聊天)
5 Socket套接字概述:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO流传输。
Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。
6 UDP传输(了解)
6.1 发送Send
创建DatagramSocket, 随机端口号
创建DatagramPacket, 指定数据, 长度, 地址, 端口
使用DatagramSocket发送DatagramPacket
关闭DatagramSocket
6.2.接收Receive
创建DatagramSocket, 指定端口号
创建DatagramPacket, 指定数组, 长度
使用DatagramSocket接收DatagramPacket
关闭DatagramSocket
从DatagramPacket中获取数据
6.3 接收方获取ip和端口号
String ip = packet.getAddress().getHostAddress();
int port = packet.getPort();
6.4 我们假设电脑之间的数据传输是两个国家间的货物往来
首先建立发货方
import java.net.*; public class Demo1_Send { public static void main(String[] args) throws Exception{ // 1 创建Socket创建一个码头 DatagramSocket socket = new DatagramSocket(); // 2 发送的数据 既货物 String str = "我是货物"; // 3 创建Packet 相当于集装箱 把货物放在里面 DatagramPacket packet = new DatagramPacket(str.getBytes(),str.getBytes().length,InetAddress.getByName("127.0.0.1"),6616); // 4 把货发出去 socket.send(packet); // 5 发完货关闭码头 (其实底层是IO流 用完要关闭流) socket.close(); } }
然后建立收货方
import java.net.DatagramPacket; import java.net.DatagramSocket; public class Demo1_Receive { public static void main(String[] args)throws Exception { // 1 创建socket 相当于创建一个码头 DatagramSocket socket = new DatagramSocket(6616); // 2 创建Packet 相当于集装箱 DatagramPacket packet = new DatagramPacket(new byte[1024],1024); // 3 接收数据 等于把货装到自己的集装箱里 socket.receive(packet); // 4 获取数据 (这里是获取全部数据 包括空的)拿到一整个集装箱 byte[] arr = packet.getData(); // 获取有效的字节个数 获取具体的货物 int len = packet.getLength(); System.out.println(new String(arr,0,len)); // 拿到数据关闭码头 (底层是IO流 关闭流) socket.close(); } }
6.5 UDP传输优化 发送方 数据从键盘录入
public class Demo2_Send { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); // 1 创建Socket创建一个码头 DatagramSocket socket = new DatagramSocket(); while (true) { // 2 发送的数据 既货物 String str = sc.nextLine(); if (str.equals("quit")) { break; } else { // 3 创建Packet 相当于集装箱 把货物放在里面 DatagramPacket packet = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6616); // 4 把货发出去 socket.send(packet); } } // 5 发完货关闭码头 (其实底层是IO流 用完要关闭流) socket.close(); } }
接收方 不间断的接收数据
import java.net.DatagramPacket; import java.net.DatagramSocket; public class Demo2_Receive { public static void main(String[] args) throws Exception { // 1 创建socket 相当于创建一个码头 DatagramSocket socket = new DatagramSocket(6616); // 2 创建Packet 相当于集装箱 DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); while (true) { // 3 接收数据 等于把货装到自己的集装箱里 socket.receive(packet); // 4 获取数据 (这里是获取全部数据 包括空的)拿到一整个集装箱 byte[] arr = packet.getData(); // 获取有效的字节个数 获取具体的货物 int len = packet.getLength(); String ip = packet.getAddress().getHostAddress(); int port = packet.getPort(); System.out.println(ip + ":" + port + ":" + new String(arr, 0, len)); } /* // 拿到数据关闭码头 (底层是IO流 关闭流) socket.close();*/ } }
7 TCP协议(掌握)
1.客户端
创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器
调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
输入流可以读取服务端输出流写出的数据
输出流可以写出数据到服务端的输入流
import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 12345); InputStream is = socket.getInputStream(); // 获取客户端输入流 OutputStream os = socket.getOutputStream(); // 获取客户端的输出流 byte[] arr = new byte[1024]; int len = is.read(arr); // 读取服务器发过来的数据 System.out.println(new String(arr, 0, len)); // 将数据转换成字符串并打印 os.write("已经收到".getBytes()); //客户端向服务器写数据 socket.close(); // 关闭客户端 } }
2.服务端
创建ServerSocket(需要指定端口号)
调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
输入流可以读取客户端输出流写出的数据
输出流可以写出数据到客户端的输入流
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /* 2.服务端 * 创建ServerSocket(需要指定端口号) * 调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket * 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流 * 输入流可以读取客户端输出流写出的数据 * 输出流可以写出数据到客户端的输入流 */ public class Server { public static void main(String[] args) throws IOException { ServerSocket servr = new ServerSocket(12345); Socket socket = servr.accept(); // 接受客户端的请求 InputStream is = socket.getInputStream(); // 获取客户端输入流 OutputStream os = socket.getOutputStream(); // 获取客户端输出流 os.write("测试测试".getBytes());// 服务器向客户端写出数据 byte[] arrs = new byte[1024]; int len = is.read(arrs); // 读取客户端发过来的数据并打印 System.out.println(new String(arrs, 0, len)); // 将数据转换成字符串并打印 // 注意 因为是服务器端所以不用关闭 } }
7.1 TCP优化
客户端
输入流:直接使用带缓冲的缓冲字符输入流(不用创建字节数组去存储读取到的数据了)
输出流:使用PrintStream 打印流 输出字符串后会自动换行
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; public class Demo1_Client { public static void main(String[] args) throws Exception{ Socket socket = new Socket("127.0.0.1",12345); System.out.println("客户端启动"); // 将字节流包装成字符流输入 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // PrintStream中有写出字符串后自动换行的方法 看成字符输出流就好 PrintStream ps = new PrintStream(socket.getOutputStream()); ps.println("客户端说:Hello"); System.out.println(br.readLine()); // 关闭客户端 socket.close(); } }
服务端
设置死循环 监听请求 每次有新请求进来都新建一个匿名线程去处理
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class Demo2_Server { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(12345); System.out.println("服务端启动"); // 死循环 服务端不断监听请求 while (true) { //接收客户端请求 final Socket socket = server.accept(); // 每次有新的请求进来 都新建一个匿名线程去处理请求 new Thread() { @Override public void run() { try { // 将字节输入流包装成字符输入流 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节输出流 包装成字符打印流 该流写出字符串后会自动换行 PrintStream ps = new PrintStream(socket.getOutputStream()); // 读取客户端发过来的请求 System.out.println(br.readLine()); // 发送回客户端 ps.println("服务器端说:嘿 man"); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } } }
小练习 1(需求:客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串)
直接上源码吧 注释写得很清楚
建立客户端
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner; public class Test_Send { // 客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串 public static void main(String[] args) throws Exception { // 建立客户端 指定ip与端口号 Socket socket = new Socket("127.0.0.1", 12345); System.out.println("客户端启动"); // 建立输入输出流 对socket里面的流进行包装 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintStream ps = new PrintStream(socket.getOutputStream()); // 建立键盘读取对象 Scanner sc = new Scanner(System.in); // 发送键盘录入的字符串 ps.println(sc.nextLine()); // 接收 返回的字符串 System.out.println(br.readLine()); } }
建立服务端
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class Test_Receive { public static void main(String[] args) throws IOException { // 建立服务端 ServerSocket server = new ServerSocket(12345); System.out.println("服务端启动 绑定12345端口"); // 死循环不断监听请求 while (true) { // 接收客户端请求 注意要被匿名内部类调用的成员变量得是final的 final Socket socket = server.accept(); // 每次有新请求进来 都新建一个匿名线程去处理请求 new Thread() { @Override public void run() { try { // 创建输入输出流 对socket里面的流进行包装 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintStream ps = new PrintStream(socket.getOutputStream()); // 读取客户端发过来的数据 String line = br.readLine(); //链式编程 反转字符串 1 创建 StringBuilder(line)类 2 利用其reverse()方法将字符串反转 // 3 再使用toString方法返回字符串 line = new StringBuilder(line).reverse().toString(); // 将反转的字符串写出 ps.println(line); // 关闭socket 里面的流会自动关闭 socket.close(); } catch (IOException e) { } } }.start(); } } }
小练习 2 上传文件到服务器
// 1(客户端).提示输入要上传的文件路径, 验证路径是否存在以及是否是文件夹
// 2(客户端).发送文件名到服务端
// 3(服务端)建立多线程的服务器
// 4(服务端)读取文件名
// 5(服务端)判断文件是否存在, 将结果发回客户端
// 6 (客户端)接收结果, 如果存在给予提示, 程序直接退出
// 7 (客户端)如果不存在, 定义FileInputStream读取文件, 写出到网络
// 8 (服务端).定义FileOutputStream, 从网络读取数据, 存储到本地
直接上源码
客户端
import java.io.*; import java.net.Socket; import java.util.Scanner; public class UpdateClient { public static void main(String[] args) throws Exception { // 1 提示输入要上传的文件路径,验证路径是否存在以及是否是文件夹 File file = getFile(); // 2 发送文件名到客户端 Socket socket = new Socket("127.0.0.1", 12345); // 创建输入输出流,将文件名发送至客户端 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintStream ps = new PrintStream(socket.getOutputStream()); ps.println(file.getName()); // 6 提示结果,如果存在给予提示,程序直接退出 String result = br.readLine(); if (result.equals("存在")) { System.out.println("您上传的文件已经存在,请不要重复上传"); socket.close(); return; } // 7 如果不存在,定义FileInputStream读取文件,写出到网络 FileInputStream fis = new FileInputStream(file); byte[] arr = new byte[8192]; int len; while ((len = fis.read(arr)) != -1) { ps.write(arr, 0, len); } fis.close(); socket.close(); } // 获取一个文件路径 private static File getFile() { Scanner sc = new Scanner(System.in); System.out.println("请输入一个文件路径"); while (true) { File file = new File(sc.nextLine()); if (!file.exists()) { System.out.println("你录入的文件路径不存在请重新输入"); } else if (file.isDirectory()) { System.out.println("你录入的是文件夹路径,请输入一个文件路径"); } else { return file; } } } }
服务端
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class UpdateServer { public static void main(String[] args) throws IOException { // 3 创建多线程服务器 ServerSocket server = new ServerSocket(12345); System.out.println("服务器启动,绑定12345"); // 4 读取文件名 while (true) { final Socket socket = server.accept(); // 接收请求 new Thread() { public void run() { try { InputStream is = socket.getInputStream(); // 创建输入输出流 BufferedReader br = new BufferedReader(new InputStreamReader(is)); PrintStream ps = new PrintStream(socket.getOutputStream()); // 获取文件名 String fileName = br.readLine(); // 5 判断文件是否存在,将结果返回客户端 File dir = new File("update"); dir.mkdir(); // 创建文件夹 File file = new File(dir, fileName); // 封装成File对象 if (file.exists()) { // 如果服务器已经存在这个文件 将结果返回客户端 ps.println("存在"); socket.close(); // 关闭socket return; } else { ps.println("文件不存在"); } // 8 定义FileOutputStream 从网络读取数据,存储到本地 FileOutputStream fos = new FileOutputStream(file); byte[] arr = new byte[8192]; int len; while ((len = is.read(arr)) != -1) { fos.write(arr, 0, len); } fos.close(); socket.close(); } catch (IOException e) { } } }.start(); } } }