java基础学习_网络编程_day26总结
============================================================================= ============================================================================= 涉及到的知识点有: 1:网络编程(理解) (1)网络编程的概述 (2)网络参考模型 (3)网络通信的三要素 A:IP地址 B:端口 C:通信协议 (4)Socket机制 (5)UDP协议发送和接收数据(掌握) (6)TCP协议发送和接收数据(掌握) (7)案例: A:UDP a:最基本的UDP协议发送和接收数据 b:把发送数据改进为键盘录入 c:一个简易聊天小程序并用多线程改进 B:TCP a:最基本的TCP协议发送和接收数据 b:服务器给出反馈 c:客户端键盘录入,服务器输出控制台(字符流) d:客户端键盘录入,服务器写到文本文件(字符流) e:客户端读取文本文件,服务器写到文本文件/输出控制台(字符流) f:上传图片(字节流) 注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。 g:多线程改进上传文件 ============================================================================= ============================================================================= 1:网络编程(理解) (1)网络编程的概述 网络编程:用Java语言实现计算机间数据的信息传递和资源共享。 (2)网络参考模型 OSI参考模型(Open System Interconnection:开放系统互连) TCP/IP参考模型
-------------------------------------- (3)网络通信的三要素 A:IP地址 a:点分十进制 b:IP地址的组成 c:IP地址的分类 d:两个DOS命令 e:InetAddress类(为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress供我们使用) InetAddress类的成员方法:(注意:该类中没有构造方法) public static InetAddress getByName(String host) 根据主机名或者IP地址的字符串表示得到IP地址对象 B:端口 每个程序都会至少有一个逻辑端口。 是应用程序的标识。 范围:0-65535。其中0-1024不建议使用。 C:通信协议 UDP:数据打包,有限制,不连接,效率高,不可靠。 TCP:建立数据通道,无限制,效率低,可靠。 -------------------------------------- (4)Socket机制 A:通信两端都有Socket对象。 B:所有的通信都是通过Socket间的IO进行操作的。 C:网络通信其实就是Socket间的通信。
-------------------------------------- (5)UDP协议发送和接收数据(掌握) 发送: A:创建发送端Socket对象(DatagramSocket) B:创建数据,并把数据打包(DatagramPacket) C:调用Socket对象的发送方法发送数据报包 D:释放资源 接收: A:创建接收端Socket对象,并指定端口号(DatagramSocket) B:创建一个数据包(接收容器)(DatagramPacket) C:调用Socket对象的接收方法接收数据 D:解析数据包,并显示在控制台 E:释放资源 Exception in thread "main" java.net.BindException: Address already in use: Cannot bind 多次启动接收端出现异常。端口被占用。
-------------------------------------- (6)TCP协议发送和接收数据(掌握) 发送: A:创建发送端Socket对象,并明确要连接的服务器(Socket) A步骤如果创建对象成功,就说明连接通道已建立成功了。 B:调用Socket对象获取输出流对象,写数据(OutputStream) C:释放资源 接收: A:创建接收端Socket对象,并指定端口(ServerSocket) B:监听客户端连接,返回一个对应的Socket对象 C:获取输入流对象,读取数据显示在控制台(InputStream) D:释放资源 Exception in thread "main" java.net.ConnectException: Connection refused: connect 连接被拒绝。TCP协议一定要先开服务器。 因为TCP保证数据一定被收到,所以接收端一定要先开启。
-------------------------------------- (7)案例: A:UDP a:最基本的UDP协议发送和接收数据
基本版本:
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_02; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 import java.net.InetAddress; 7 /* 8 * UDP协议发送数据: 9 * A:创建发送端Socket对象(DatagramSocket) 10 * B:创建数据,并把数据打包(DatagramPacket) 11 * C:调用Socket对象的发送方法发送数据报包 12 * D:释放资源 13 * 14 * DatagramSocket类:数据报套接字类,此类表示用来发送和接收数据报包的套接字。 15 */ 16 public class SendDemo { 17 public static void main(String[] args) throws IOException { 18 // 创建发送端Socket对象(DatagramSocket) 19 // DatagramSocket类的构造方法:public DatagramSocket() 20 DatagramSocket ds = new DatagramSocket(); 21 22 // 创建数据,并把数据打包(DatagramPacket) 23 // DatagramPacket类的构造方法:public DatagramPacket(byte[] buf, int length, InetAddress address, int port) 24 // 创建数据 25 byte[] bys = "hello,UDP,我来了".getBytes(); // 把字符串转为字符数组 26 // 长度 27 int length = bys.length; 28 // 获取IP地址对象 29 InetAddress address = InetAddress.getByName("192.168.40.9"); 30 // 端口 31 int port = 10086; 32 DatagramPacket dp = new DatagramPacket(bys, length, address, port); 33 34 // 调用Socket对象的发送方法发送数据报包 35 // DatagramSocket类的成员方法:public void send(DatagramPacket p) 发送数据报包 36 ds.send(dp); 37 38 // 释放资源 39 ds.close(); 40 } 41 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_02; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 import java.net.InetAddress; 7 8 /* 9 * UDP协议接收数据: 10 * A:创建接收端Socket对象,并指定端口号(DatagramSocket) 11 * B:创建一个数据包(接收容器)(DatagramPacket) 12 * C:调用Socket对象的接收方法接收数据 13 * D:解析数据包,并显示在控制台 14 * E:释放资源 15 */ 16 public class ReceiveDemo { 17 public static void main(String[] args) throws IOException { 18 // 创建接收端Socket对象,并指定端口号(DatagramSocket) 19 // DatagramSocket类的构造方法:public DatagramSocket(int port) 20 DatagramSocket ds = new DatagramSocket(10086); 21 22 // 创建一个数据包(接收容器)(DatagramPacket) 23 // DatagramPacket类的构造方法:public DatagramPacket(byte[] buf, int length) 24 byte[] bys = new byte[1024]; 25 int length = bys.length; 26 DatagramPacket dp = new DatagramPacket(bys, length); 27 28 // 调用Socket对象的接收方法接收数据 29 // DatagramSocket类的成员方法:public void receive(DatagramPacket p) 接收数据报包,当此方法返回时,DatagramPacket的缓冲区里填充了接收的数据,数据报包也包含发送方的IP地址和发送方机器上的端口号。 30 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞) 31 32 // 解析数据包,并显示在控制台 33 // DatagramPacket类的成员方法:public InetAddress getAddress() // 获取IP地址(此时为发送端的IP地址) 34 InetAddress address = dp.getAddress(); 35 String ip = address.getHostAddress(); 36 // DatagramPacket类的成员方法:public byte[] getData() 获取数据缓冲区 37 // DatagramPacket类的成员方法:public int getLength() 获取数据的实际长度 38 byte[] bys2 = dp.getData(); 39 int len = dp.getLength(); 40 String s = new String(bys2, 0, len); 41 System.out.println(ip + "传递的数据是:" + s); 42 43 // 释放资源 44 ds.close(); 45 } 46 }
改进版本:(使用链式编程)
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_03; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 import java.net.InetAddress; 7 /* 8 * UDP协议发送数据: 9 * A:创建发送端Socket对象(DatagramSocket) 10 * B:创建数据,并把数据打包(DatagramPacket) 11 * C:调用Socket对象的发送方法发送数据报包 12 * D:释放资源 13 * 14 * DatagramSocket类:数据报套接字类,此类表示用来发送和接收数据报包的套接字。 15 */ 16 public class SendDemo { 17 public static void main(String[] args) throws IOException { 18 // 创建发送端Socket对象(DatagramSocket) 19 DatagramSocket ds = new DatagramSocket(); 20 21 // 创建数据,并把数据打包(DatagramPacket) 22 byte[] bys = "hello,UDP,我来了".getBytes(); // 把字符串转为字符数组 23 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086); 24 25 // 调用Socket对象的发送方法发送数据报包 26 ds.send(dp); 27 28 // 释放资源 29 ds.close(); 30 } 31 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_03; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 7 /* 8 * UDP协议接收数据: 9 * A:创建接收端Socket对象,并指定端口号(DatagramSocket) 10 * B:创建一个数据包(接收容器)(DatagramPacket) 11 * C:调用Socket对象的接收方法接收数据 12 * D:解析数据包,并显示在控制台 13 * E:释放资源 14 * 15 * Exception in thread "main" java.net.BindException: Address already in use: Cannot bind 16 * 多次启动接收端出现异常。端口被占用。 17 */ 18 public class ReceiveDemo { 19 public static void main(String[] args) throws IOException { 20 // 创建接收端Socket对象,并指定端口号(DatagramSocket) 21 DatagramSocket ds = new DatagramSocket(10086); 22 23 // 创建一个数据包(接收容器)(DatagramPacket) 24 byte[] bys = new byte[1024]; 25 DatagramPacket dp = new DatagramPacket(bys, bys.length); 26 27 // 调用Socket对象的接收方法接收数据 28 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞) 29 30 // 解析数据包,并显示在控制台 31 String ip = dp.getAddress().getHostAddress(); 32 String s = new String(dp.getData(), 0, dp.getLength()); 33 System.out.println(ip + "传递的数据是:" + s); 34 35 // 释放资源 36 ds.close(); 37 } 38 }
b:把发送数据改进为键盘录入
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_04; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.net.DatagramPacket; 7 import java.net.DatagramSocket; 8 import java.net.InetAddress; 9 /* 10 * UDP案例: 11 * 从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。 12 * 13 * 数据来自于键盘录入,键盘录入数据要自己控制录入结束。 14 */ 15 public class SendDemo { 16 public static void main(String[] args) throws IOException { 17 // 创建发送端Socket对象 18 DatagramSocket ds = new DatagramSocket(); 19 20 // 封装键盘录入数据对象 21 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 22 String line = null; 23 while ((line = br.readLine()) != null) { 24 if ("886".equals(line)) { 25 break; 26 } 27 28 // 创建数据,并把数据打包 29 byte[] bys = line.getBytes(); 30 // DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086); 31 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.255"), 10086); // 广播地址 32 33 // 调用Socket对象的发送方法发送数据报包 34 ds.send(dp); 35 } 36 37 // 释放资源 38 ds.close(); 39 } 40 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_04; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 7 /* 8 * UDP案例: 9 * 从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。 10 */ 11 public class ReceiveDemo { 12 public static void main(String[] args) throws IOException { 13 // 创建接收端Socket对象,并指定端口号 14 DatagramSocket ds = new DatagramSocket(10086); 15 16 while (true) { 17 // 创建一个数据包(接收容器) 18 byte[] bys = new byte[1024]; 19 DatagramPacket dp = new DatagramPacket(bys, bys.length); 20 21 // 调用Socket对象的接收方法接收数据 22 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞) 23 24 // 解析数据包,并显示在控制台 25 String ip = dp.getAddress().getHostAddress(); 26 String s = new String(dp.getData(), 0, dp.getLength()); 27 System.out.println(ip + "传递的数据是:" + s); 28 } 29 30 // 释放资源 31 // ds.close(); // 接收端应该一直开着等待接收数据,是不需要关闭的。 32 } 33 }
c:一个简易聊天小程序并用多线程改进
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_05; 2 3 import java.io.IOException; 4 import java.net.DatagramSocket; 5 6 /* 7 * 通过多线程改进刚才的聊天程序,这样我就可以实现在一个窗口发送和接收数据了 8 */ 9 public class ChatRoom { 10 public static void main(String[] args) throws IOException { 11 DatagramSocket dsSend = new DatagramSocket(); 12 DatagramSocket dsReceive = new DatagramSocket(10086); 13 14 SendThread st = new SendThread(dsSend); 15 ReceiveThread rt = new ReceiveThread(dsReceive); 16 17 Thread t1 = new Thread(st); 18 Thread t2 = new Thread(rt); 19 20 t1.start(); 21 t2.start(); 22 } 23 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_05; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.net.DatagramPacket; 7 import java.net.DatagramSocket; 8 import java.net.InetAddress; 9 10 public class SendThread implements Runnable { 11 12 private DatagramSocket ds; 13 14 public SendThread(DatagramSocket ds) { 15 this.ds = ds; 16 } 17 18 @Override 19 public void run() { 20 try { 21 // 封装键盘录入数据对象 22 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 23 String line = null; 24 while ((line = br.readLine()) != null) { 25 if ("886".equals(line)) { 26 break; 27 } 28 29 // 创建数据,并把数据打包 30 byte[] bys = line.getBytes(); 31 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086); 32 33 // 调用Socket对象的发送方法发送数据报包 34 ds.send(dp); 35 } 36 // 释放资源 37 ds.close(); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 } 42 43 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_05; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 7 public class ReceiveThread implements Runnable { 8 9 private DatagramSocket ds; 10 11 public ReceiveThread(DatagramSocket ds) { 12 this.ds = ds; 13 } 14 15 @Override 16 public void run() { 17 try { 18 while (true) { 19 // 创建一个数据包(接收容器) 20 byte[] bys = new byte[1024]; 21 DatagramPacket dp = new DatagramPacket(bys, bys.length); 22 23 // 调用Socket对象的接收方法接收数据 24 ds.receive(dp); 25 26 // 解析数据包,并显示在控制台 27 String ip = dp.getAddress().getHostAddress(); 28 String s = new String(dp.getData(), 0, dp.getLength()); 29 System.out.println("from " + ip + " data is : " + s); 30 } 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 36 }
B:TCP
a:最基本的TCP协议发送和接收数据
b:服务器给出反馈
c:客户端键盘录入,服务器输出控制台(字符流)
d:客户端键盘录入,服务器写到文本文件(字符流)
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_09; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.IOException; 6 import java.io.InputStreamReader; 7 import java.io.OutputStreamWriter; 8 import java.net.Socket; 9 10 /* 11 * 客户端键盘录入,服务器写到文本文件 12 */ 13 public class ClientDemo { 14 public static void main(String[] args) throws IOException { 15 // 创建客户端Socket对象 16 Socket s = new Socket("192.168.40.9", 2222); 17 18 // 包装键盘录入数据对象 19 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 20 21 // 包装通道内的流对象 22 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 23 24 String line = null; 25 while ((line = br.readLine()) != null) { 26 // 键盘录入数据要自定义结束标记 27 if ("886".equals(line)) { 28 break; 29 } 30 bw.write(line); 31 bw.newLine(); 32 bw.flush(); 33 } 34 35 // 释放资源 36 // bw.close(); // 已经不录入了,关不关流无所谓了 37 // br.close(); // 本质关闭的是s对象 38 s.close(); 39 } 40 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_09; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileWriter; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 /* 11 * 客户端键盘录入,服务器写到文本文件 12 */ 13 public class ServerDemo { 14 public static void main(String[] args) throws IOException { 15 // 创建服务器Socket对象 16 ServerSocket ss = new ServerSocket(2222); 17 18 // 监听客户端连接 19 Socket s = ss.accept(); 20 21 // 包装通道内的流对象 22 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 23 24 // 封装文本文件对象 25 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_09//b.txt")); 26 27 String line = null; 28 while ((line = br.readLine()) != null) { 29 bw.write(line); 30 bw.newLine(); 31 bw.flush(); 32 } 33 34 s.close(); 35 bw.close(); 36 } 37 }
e:客户端读取文本文件,服务器写到文本文件/输出控制台(字符流)
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_10; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileReader; 6 import java.io.IOException; 7 import java.io.OutputStreamWriter; 8 import java.net.Socket; 9 10 /* 11 * 客户端读取文本文件,服务器写到文本文件/输出到控制台 12 */ 13 public class ClientDemo { 14 public static void main(String[] args) throws IOException { 15 // 创建客户端Socket对象 16 Socket s = new Socket("192.168.40.9", 2222); 17 18 // 封装文本文件对象 19 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_10//a.txt")); 20 21 // 包装通道内的流对象 22 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 23 24 String line = null; 25 while ((line = br.readLine()) != null) { 26 bw.write(line); 27 bw.newLine(); 28 bw.flush(); 29 } 30 31 br.close(); 32 s.close(); 33 } 34 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_10; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileWriter; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 /* 11 * 客户端读取文本文件,服务器写到文本文件/输出到控制台 12 */ 13 public class ServerDemo { 14 public static void main(String[] args) throws IOException { 15 // 创建服务器Socket对象 16 ServerSocket ss = new ServerSocket(2222); 17 18 // 监听客户端连接 19 Socket s = ss.accept(); 20 21 // 包装通道内的流对象 22 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 23 24 // 封装文本文件对象 25 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_10//Copy.txt")); 26 27 String line = null; 28 while ((line = br.readLine()) != null) { 29 bw.write(line); 30 bw.newLine(); 31 bw.flush(); 32 33 System.out.println(line); 34 } 35 36 s.close(); 37 bw.close(); 38 } 39 }
服务器给出反馈的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_12; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileReader; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.io.OutputStreamWriter; 9 import java.net.Socket; 10 11 /* 12 * 按照我们正常的思路加入反馈信息,结果却没反应。为什么呢? 13 * 读取文本文件是以null作为结束信息的,但是呢,在通道里读数据,通道内并不是这样结束信息的。 14 * 所以,服务器根本就不知道你结束了。而客户端还想服务器给反馈。所以,就相互等待了。 15 * 16 * 如何解决呢? 17 * A:客户端写完数据后,再写一条数据作为结束标记,服务器读取到这条数据说明客户端结束了,所以服务器该结束了。 18 * 这样做可以解决问题,但是不好。 19 * 如果自定义的结束标记与文件的内容相同的话,文件发送就提前结束了。 20 * B:Socket对象提供了一种解决方案 21 * public void shutdownOutput() 22 */ 23 24 public class UploadClient { 25 public static void main(String[] args) throws IOException { 26 // 创建客户端Socket对象 27 Socket s = new Socket("192.168.40.9", 11111); 28 29 // 封装文本文件对象 30 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_12//a.txt")); 31 32 // 封装通道内流对象 33 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 34 35 String line = null; 36 while ((line = br.readLine()) != null) { // 阻塞 37 // 在文件里读数据,文件末尾是null! 38 bw.write(line); 39 bw.newLine(); 40 bw.flush(); 41 } 42 43 // 自定义一个结束标记 44 // bw.write("over"); 45 // bw.newLine(); 46 // bw.flush(); 47 48 // Socket类提供了一个终止方法,该方法会通知服务器你别等了,我没有数据过来了 49 s.shutdownOutput(); 50 51 // 客户端接收服务器反馈 52 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); 53 String client = brClient.readLine(); // 阻塞 54 System.out.println(client); 55 56 // 释放资源 57 br.close(); 58 s.close(); 59 } 60 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_12; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileWriter; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.io.OutputStreamWriter; 9 import java.net.ServerSocket; 10 import java.net.Socket; 11 12 public class UploadServer { 13 public static void main(String[] args) throws IOException { 14 // 创建服务器端Socket对象 15 ServerSocket ss = new ServerSocket(11111); 16 17 // 监听客户端连接 18 Socket s = ss.accept(); // 阻塞 19 20 // 封装通道内的流对象 21 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 22 23 // 封装文本文件对象 24 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_12//Copy.txt")); 25 26 String line = null; 27 while ((line = br.readLine()) != null) { // 阻塞 28 // 在通道里读数据,通道里可没有null!肿么办? 29 // 判断自定义结束标志 30 // if ("over".equals(line)) { 31 // break; 32 // } 33 bw.write(line); 34 bw.newLine(); 35 bw.flush(); 36 } 37 38 // 服务器给客户端反馈 39 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 40 bwServer.write("文件上传成功"); 41 bwServer.newLine(); 42 bwServer.flush(); 43 44 // 释放资源 45 bw.close(); 46 s.close(); 47 } 48 }
f:上传图片(字节流)
注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_13; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.FileInputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.net.Socket; 9 10 /* 11 * 上传图片(字节流) 12 * 13 * OutputStream抽象类的方法: 14 * public void flush() throws IOException 15 * 刷新此输出流并强制写出所有缓冲的输出字节 16 * 17 * 注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。 18 */ 19 public class UploadClient { 20 public static void main(String[] args) throws IOException { 21 // 创建客户端Socket对象 22 Socket s = new Socket("192.168.40.9", 19191); 23 24 // 封装图片文件对象(字节流) 25 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src//cn//itcast_13//林青霞.jpg")); 26 27 // 封装通道内的流对象(字节流) 28 BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()); 29 30 byte[] bys = new byte[1024]; 31 int len = 0; 32 while ((len = bis.read(bys)) != -1) { 33 bos.write(bys, 0, len); 34 bos.flush(); // 刷新此输出流并强制写出所有缓冲的输出字节 35 } 36 s.shutdownOutput(); 37 38 // 读取服务器的反馈 39 InputStream is = s.getInputStream(); 40 byte[] bys2 = new byte[1024]; 41 int len2 = is.read(bys2); 42 String client = new String(bys2, 0, len2); 43 System.out.println(client); 44 45 // 释放资源 46 bis.close(); 47 s.close(); 48 } 49 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_13; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.OutputStream; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 11 /* 12 * 上传图片(字节流) 13 */ 14 public class UploadServer { 15 public static void main(String[] args) throws IOException { 16 // 创建服务器Socket对象 17 ServerSocket ss = new ServerSocket(19191); 18 19 // 监听客户端连接 20 Socket s = ss.accept(); 21 22 // 封装通道内流对象(字节流) 23 BufferedInputStream bis = new BufferedInputStream(s.getInputStream()); 24 25 // 封装图片文件对象(字节流) 26 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src//cn//itcast_13//mn.jpg")); 27 28 byte[] bys = new byte[1024]; 29 int len = 0; 30 while ((len = bis.read(bys)) != -1) { 31 bos.write(bys, 0, len); 32 bos.flush(); // 刷新此输出流并强制写出所有缓冲的输出字节 33 } 34 35 // 给客户端一个反馈 36 OutputStream os = s.getOutputStream(); 37 os.write("图片上传成功".getBytes()); 38 39 bos.close(); 40 s.close(); 41 } 42 }
g:多线程改进上传文件
服务器的代码用线程进行封装(多线程),这样可以模拟一个同时接收多人上传文件的服务器。
(用循环也可以但是效率低,是单线程的程序)
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_15; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileReader; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.io.OutputStreamWriter; 9 import java.net.Socket; 10 11 public class UploadClient { 12 public static void main(String[] args) throws IOException { 13 // 创建客户端Socket对象 14 Socket s = new Socket("192.168.40.9", 11111); 15 16 // 封装文本文件 17 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_15//a.txt")); 18 19 // 封装通道内的流对象 20 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 21 22 String line = null; 23 while ((line = br.readLine()) != null) { // 阻塞 24 bw.write(line); 25 bw.newLine(); 26 bw.flush(); 27 } 28 29 // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了 30 s.shutdownOutput(); 31 32 // 接收反馈 33 BufferedReader brClient = new BufferedReader(new InputStreamReader( 34 s.getInputStream())); 35 String client = brClient.readLine(); // 阻塞 36 System.out.println(client); 37 38 // 释放资源 39 br.close(); 40 s.close(); 41 } 42 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_15; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class UploadServer { 8 public static void main(String[] args) throws IOException { 9 // 创建服务器Socket对象 10 ServerSocket ss = new ServerSocket(11111); 11 12 while (true) { 13 Socket s = ss.accept(); // 监听客户端连接 14 new Thread(new UserThread(s)).start(); 15 } 16 } 17 18 }
![](https://i-blog.csdnimg.cn/blog_migrate/5f94c2fecb4ccb2f76f50c965d122407.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/50828477840f9be8f01bb82af691c7d1.gif)
1 package cn.itcast_15; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileWriter; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.io.OutputStreamWriter; 9 import java.net.Socket; 10 11 public class UserThread implements Runnable { 12 13 private Socket s; 14 15 public UserThread(Socket s) { 16 this.s = s; 17 } 18 19 @Override 20 public void run() { 21 try { 22 // 封装通道内的流对象 23 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 24 25 // 封装文本文件对象 26 // BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_15//Copy.java")); 27 28 // 为了防止名称冲突(即为了防止所有文件的名字都一样) 29 String newName = System.currentTimeMillis() + ".txt"; 30 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_15//" + newName)); 31 // 如果在某一时间点,同时有很多人访问服务器,相同名字的文件也会出现很多,肿么办? 答:再加循环判断,一旦某个名字存在,就重新赋值另一名字即可。 32 33 String line = null; 34 while ((line = br.readLine()) != null) { // 阻塞 35 bw.write(line); 36 bw.newLine(); 37 bw.flush(); 38 } 39 40 // 给出反馈 41 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); 42 bwServer.write("文件上传成功"); 43 bwServer.newLine(); 44 bwServer.flush(); 45 46 // 释放资源 47 bw.close(); 48 s.close(); 49 } catch (IOException e) { 50 e.printStackTrace(); 51 } 52 } 53 54 }
TCP传输容易出现的问题: 客户端连接上服务端,两端都在等待,没有任何数据传输。 通过例程分析: 因为read()方法或者readLine()方法是阻塞式。 解决办法: 1.自定义结束标记。 2.使用shutdownInput()、shutdownOutput()方法。 =============================================================================
我的GitHub地址: https://github.com/heizemingjun
我的博客园地址: http://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun
Copyright ©2018 黑泽明军
【转载文章务必保留出处和署名,谢谢!】