1. 网络编程入门
1.1 网络编程概述
1.2 网络编程三要素
1.3 IP地址
在命令提示符中使用
1.4 InetAddress的使用
代码示例:
public class InetAddressDemo { // InetAddress // 此类表示Internet协议(IP) 地址 public static void main(String[] args) throws UnknownHostException { // public static InetAddress getByName (String host): // 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址,推荐是使用IP地址 //InetAddress address = InetAddress.getByName("WxW"); InetAddress address = InetAddress.getByName("192.168.0.105"); // public String getHostName ():获取此IP地址的主机名 String hostName = address.getHostName(); System.out.println(hostName); // public String getHostAddress (): 返回文本显示中的IP地址字符串 String hostAddress = address.getHostAddress(); System.out.println(hostAddress); } }
运行结果:
代码示例:
public class InetAddressTest { public static void main(String[] args) throws UnknownHostException { System.out.println(InetAddress.getLocalHost());//LAPTOP-VEKHRDDR/192.168.240.1 System.out.println(InetAddress.getLocalHost().getHostName());//LAPTOP-VEKHRDDR System.out.println(InetAddress.getLocalHost().getHostAddress());//192.168.240.1 System.out.println(InetAddress.getByName("192.168.3.11"));//输出:/192.168.3.11 System.out.println(InetAddress.getByName("192.168.3.11").getHostName());//LAPTOP-VEKHRDDR System.out.println(InetAddress.getByName("192.168.3.11").getHostAddress());//192.168.3.11 System.out.println(InetAddress.getByName("LAPTOP-VEKHRDDR"));//LAPTOP-VEKHRDDR/192.168.240.1 System.out.println(InetAddress.getByName("LAPTOP-VEKHRDDR").getHostName());//LAPTOP-VEKHRDDR System.out.println(InetAddress.getByName("LAPTOP-VEKHRDDR").getHostAddress());//192.168.240.1 } }
运行结果:
1.5 端口
1.6 协议
三次握手
2. UDP通信程序
2.1 UDP通信原理
2.2 UDP发送数据
代码示例:
创建发送端
public class SendDemo { public static void main(String[] args) throws IOException { // 1:创建发送端的Socket对象(DatagramSocket) // DatagramSocket ()构造数据报套接字并将其绑定到本地主机上的任何可用端口 DatagramSocket ds = new DatagramSocket(); // 2:创建数据,并把数据打包 // DatagramPacket (byte[] buf, int Length, InetAddress address, int port) // 构造一个数据包,发送长度为length的数据 包到指定主机上的指定端口号。 byte[] bys = "hello,udp,我来了".getBytes(); int length = bys.length; InetAddress destAddress = InetAddress.getByName("192.168.0.105"); int port = 10086; DatagramPacket dp = new DatagramPacket(bys, length, destAddress, port); // 上面的步骤也可以写成这样 // DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.0.105"), 10086); // 3:调用DatagramSocket对象的方法发送数据 // void send (DatagramPacket p)从此套接字发送数据报包 ds.send(dp); // 4:关闭发送端 ds.close(); } }
运行结果:
运行后并没有输出结果,因为还没有写接收端
2.3 UDP接收数据
代码示例:
创建接受端public class ReceiveDemo { public static void main(String[] args) throws IOException {
// 1:创建接收端的Socket对象(DatagramSocket ) // DatagramSocket (int port)构造数据报套接字并将其绑定到本地主机上的指定端口 DatagramSocket ds = new DatagramSocket(10086); // 2:创建一个数据包,用于接收数据 // DatagramPacket (byte[] buf, int length) 构造一个 DatagramPacket用于 接收长度为Length数据包 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys, bys.length); // 3:调用DatagramSocket对象的方法接收数据 ds.receive(dp); // 4:解析数据包,并把数据在控制台显示 // byte[] getData() 返回数据缓仲区 byte[] datas = dp.getData(); // int getLength () 返回要发送的数据的长度或接收到的数据的长度 int len = dp.getLength(); String dataString = new String(datas, 0, len); // 这样写是为了让输出的数据正好而不多余。 System.out.println("数据是:" + dataString); // 上面的步骤也可以这样写 // System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
String ip = dp.getAddress().getHostAddress();
System.out.println("收到"+ip+"发来的消息");
// 5:关闭接收端 ds.close(); } }
运行结果:
运行时要先运行接收端再运行发送端,这样就就可以在接收端的控制台上看见接收到的数据了。
2.4 UDP通信程序练习
代码示例:
UDP发送端
/* UDP发送数据: 数据来自于键盘录入,直到输入的数据是886,发送数据结束 */ public class SendDemo { public static void main(String[] args) throws IOException { // 创建发送端的Socket对象(DatagramSocket ) DatagramSocket ds = new DatagramSocket(); // 自己封装键盘录入数据 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line = br.readLine()) != null) { // 输入的数据的886,发送数据接收 if ("886".equals(line)) { break; } // 创建数据,并把数据打包 byte[] bys = line.getBytes(); DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.0.105"), 12345); // 调用DatagramSocket对象发送程序 ds.send(dp); } // 关闭发送端 ds.close(); } }
UDP接收端
/* UDP接收数据: 因为接收端不知道发送端什么时候停止发送,故采用死循环接收 */ public class ReceiveDemo { public static void main(String[] args) throws IOException { // 创建接收端的Socket对象(DatagramSocket) DatagramSocket ds = new DatagramSocket(12345); while (true) { // 创建一个数据包,用于接收数据 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys, bys.length); // 调用DatagramSocket对象的方法接收数据 ds.receive(dp); String data = new String(dp.getData(), 0, dp.getLength());
// 解析数据包,并把数据在控制台显示
System.out.println("数据是:" + data);
}
// 因为是采用死循环所以不需要关闭接收端
}
}
运行结果:
输入886结束接收
还可以开启多个发送数据窗口,每个窗口都可以发送数据,互不影响。
3. TCP通信程序
3.1 TCP通信原理
3.2 TCP发送数据
代码示例:
//TCP发送数据 public class ClientDemo { public static void main(String[] args) throws IOException { // 1:创建客户端的Socket对象(Socket) // Socket (InetAddress address, int port)创建流套接字并将其连接到指定IP地址的指定端口号 // Socket s = new Socket(InetAddress.getByName("192.168.0.105"), 1000); // Socket (String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号 Socket s = new Socket("192.168.0.105", 1000);// 这两个方法做的是同一件事 // 2:获取输出流,写数据 // OutputStream getOutputStream () 返回此套接字的输出流 OutputStream os = s.getOutputStream(); os.write("Hello,tcp,我来了".getBytes()); // 3:释放资源 s.close(); } }
3.3 TCP接收数据
代码示例:
//TCP接收数据 public class ServerDemo { public static void main(String[] args) throws IOException { // 1:创建服务器端的Socket对象(ServerSocket) // ServerSocket (int port)创建绑定到指定端口的服务器套接字 ServerSocket ss = new ServerSocket(10000); // Socket accept() 侦听要连接到此套接字并接受它 Socket s = ss.accept(); // 2:获取输入流,读数据,并把数据显示在控制台 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len; while ((len = is.read(bys)) != -1) { System.out.println("数据是:" + new String(bys, 0, len)); } // 3:释放资源 ss.close(); } }
运行结果:
先开启接收端再开启发送端
3.4 TCP通信程序练习
3.4.1 练习1:发送数据接收反馈,接受数据发出反馈
代码示例:
创建客户端:
/* * 客户端:发送数据,接收服务器反馈 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端的Socket对象(Socket) Socket s = new Socket(" ", 1000); // 获取输出流,写数据 OutputStream os = s.getOutputStream(); os.write("hello,tcp,我来了".getBytes()); // 接收服务器反馈 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys, 0, len); System.out.println("客户端:" + data); // 释放资源 s.close(); } }
创建服务器端:
/* * 服务器:接收数据,给出反馈 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务器端的Socket对象(ServerSocket) ServerSocket ss = new ServerSocket(1000); // 监听客户端连接,返回一个Socket对象 Socket s = ss.accept(); // 获取输入流,读数据,并把数据显示在控制台 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys, 0, len); System.out.println("服务器:" + data); // 给出反馈 OutputStream os = s.getOutputStream(); os.write("数据已收到".getBytes()); // 释放资源 ss.close(); } }
运行结果:
先打开服务器,然后等待客户端连接。
3.4.2 练习2:用键盘录入数据
代码示例:
创建客户端:
/* * 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户点Socket对象 Socket s = new Socket("192.168.0.105", 10000); // 数据来自于键盘录入,直到输入的数据是886,发送数据结束 // 使用自己包装的键盘录入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 封装输出流对象,将输出流封装成缓存流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null) { if ("886".equals(line)) { break; } // 获取输出流对象 // OutputStream os = s.getOutputStream(); // os.write(line.getBytes()); bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 s.close(); } }
创建服务器端:
/* * 服务器:接收到的数据在控制台输出 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务器Socket对象 ServerSocket ss = new ServerSocket(10000); // 监听客户端的连接,返回一个对应的Socket对象 Socket s = ss.accept(); // 获取输入流,将输出流封装成缓存流 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line; while ((line = br.readLine()) != null) { System.out.println(line); } // 释放资源 ss.close(); } }
运行结果:
先运行服务器端再运行客户端,在客户端输入数据发送到服务器端。
3.4.3 练习3:将键盘录入的数据写入文本文件
代码示例:
创建客户端:
客户端和练习2是一样的
/* * 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户点Socket对象 Socket s = new Socket("192.168.0.105", 10000); // 数据来自于键盘录入,直到输入的数据是886,发送数据结束 // 使用自己包装的键盘录入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 封装输出流对象 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null) { if ("886".equals(line)) { break; } // 获取输出流对象 // OutputStream os = s.getOutputStream(); // os.write(line.getBytes()); bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 s.close(); } }
创建服务端:
/* * 服务器:接收到的数据写入文本文件 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端Socket对象 ServerSocket ss = new ServerSocket(10000); // 监听客户端,返回一个对应的Socket对象 Socket s = ss.accept(); // 接收数据 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); // 把数据写入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("s.text")); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 bw.close(); ss.close(); } }
运行结果:
先运行服务器再运行客户端,再客户端写数据传入服务器再写入文本文件。
3.4.4 练习4:数据来自文本,发送到服务器再写入文本(上传接收)
代码示例:
被复制文件是练习3的s.text
创建客户端:
/* * 客户端:数据来自于文本文件 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket s = new Socket("192.168.0.105", 10000); // 封装文本文件的数据 BufferedReader br = new BufferedReader(new FileReader("s.text")); // 封装输出流写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 br.close(); s.close(); } }
创建服务器:
服务器和练习3是一样的
/* * 服务器:接收到的数据写入文本文件 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端Socket对象 ServerSocket ss = new ServerSocket(10000); // 监听客户端,返回一个对应的Socket对象 Socket s = ss.accept(); // 接收数据 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); // 把数据写入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.text")); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 bw.close(); ss.close(); } }
运行结果:
先运行服务器端再运行客户端。
3.4.5 练习5:在练习4的基础上加上服务器反馈
按照常规的方法会出现问题
代码示例:
接收服务器的反馈最重要的是在客户端加入结束标签shutdownOutput(),这样才能让服务器端知道你已经结束输出数据了。
创建客户端:
/* * 客户端:数据来自于文本文件 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket s = new Socket("192.168.0.105", 10000); // 封装文本文件的数据 BufferedReader br = new BufferedReader(new FileReader("s.text")); // 封装输出流写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 定义结束标签 // void shutdownOutput() 禁用此套接字的输出流。 s.shutdownOutput(); // 接收反馈 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); System.out.println("服务器的反馈:" + data); // 释放资源 br.close(); s.close(); } }
创建服务端:
/* * 服务器:接收到的数据写入文本文件 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端Socket对象 ServerSocket ss = new ServerSocket(10000); // 监听客户端,返回一个对应的Socket对象 Socket s = ss.accept(); // 接收数据 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); // 把数据写入文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.text")); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 给出反馈 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("文件上传成功!"); bwServer.newLine(); bwServer.flush(); // 释放资源 bw.close(); ss.close(); } }
运行结果:
3.4.6 练习6:利用多线程使服务器接收多个客户端的文件
代码示例:
创建客户端类:
/* * 客户端:数据来自于文本文件 */ public class ClientDemo { public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket s = new Socket("192.168.0.105", 10000); // 封装文本文件的数据 BufferedReader br = new BufferedReader(new FileReader("s.text")); // 封装输出流写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 定义结束标签 // void shutdownOutput() 禁用此套接字的输出流。 s.shutdownOutput(); // 接收反馈 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); System.out.println("服务器的反馈:" + data); // 释放资源 br.close(); s.close(); } }
创建服务器类:
/* * 服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程 */ public class ServerDemo { public static void main(String[] args) throws IOException { // 创建服务端Socket对象 ServerSocket ss = new ServerSocket(10000); while (true) { // 监听客户端,返回一个对应的Socket对象 Socket s = ss.accept(); // 为每一个客户端开启一个线程 new Thread(new ServerTherad(s)).start(); } } }
创建线程类:
public class ServerTherad implements Runnable { private Socket s; public ServerTherad(Socket s) { this.s = s; } @Override public void run() { try { // 接收数据 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); // 把数据写入文本文件 // 解决命名冲突问题 int count = 0; File file = new File("Copy[" + count + "].text"); while (file.exists()) { count++; file = new File("Copy[" + count + "].text"); } BufferedWriter bw = new BufferedWriter(new FileWriter(file)); String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 给出反馈 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("文件上传成功!"); bwServer.newLine(); bwServer.flush(); // 释放资源 bw.close(); } catch (IOException e) { e.printStackTrace(); } } }
运行结果:
先运行服务器再运行客户端,不过这次可以运行多次客户端,可以复制对个文件夹,而且不会重名。
第一次
第二次