TCP通信文件上传_接收端给出反馈
要求如下 客户端:数据来自于文本文件,且接收来自接收端的反馈。客户端即发送端 服务器:接收到的数据写入文本文件,且向发送端给出反馈。服务端即接收端
注意:发送端的数据来自'a_13_3文本.txt',接收端的数据会写入到'a_15_3文本.txt' 注意:'a_13_3文本.txt'是已存在的,'a_15_3文本.txt'是系统帮我们创建的
相当于实现了文件上传,即把你的文件发给你的同桌
如何解决文本里面出现了结束标记字眼时,位于结束标记后面的文本数据将不会被执行的问题,解决如下 shutdownInput() 将此套接字的输入流放在“流结束” shutdownOutput() 禁用此套接字的输出流 简单理解就是,不需要我们使用结束标记来终止接收端的接收,Socket类里面已经提供了两个用于标记的方法 原理:发送端在方式数据时,会把结束标记也发出去,当接收端接收数据完成后会接收到这个结束标记,接收到结束标记接收端就会关闭接收,避免了接收端一直等待
总结:当出现程序一直等待的问题时,原因是:读数据的方法是阻塞式的。解决方式有两种,如下 1、自定义结束标记,但是这种方式会带来另一个问题,当文本数据出现'自定义结束标记'字眼会被接收端认为文本数据已经接收完了,所以后面的数据就没有继续接收 2、使用shutdownOutput方法,这种方式解决起来比较便捷,只需要在发送端写就行
TCP通信文件上传_接收端给出反馈的练习
xxxxxxxxxxpackage ch22;
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;
//注意:这里的部分代码是上一节课的'a_14_1测试_发送'复制过来的public class a_15_1测试_发送 {
public static void main(String[] args) throws IOException {
//创建发送端Socket对象 Socket s = new Socket("10.99.20.3",11114);
//把要发送的文本文件数据先封装一下 BufferedReader br = new BufferedReader(new FileReader("D:\\huanf\\java\\src\\ch22\\a_13_3文本.txt"));
//封装输出流写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//读取要发送的数据,并且把读到的数据发生出去 String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); }
//自定义结束标记,即告诉接收端数据已经读完了,避免下面的程序一直等待着读取数据 /*bw.write("end");//实现很简单,即数据写完之后,我们再多写一个数据。这样,接收端接收完文本数据后,会多接收一个end数据 bw.newLine(); //当接收端接收到end数据,接收端就停止对文本数据的接收,解决了接收端一直处于接收等待状态的问题 bw.flush();*/ //注意:上面的自定义结束标记是有一个问题的,当"a_13_3文本.txt"文本里面出现了end字眼时,位于end后面的文本数据将不会被执行 //原因:出现end字眼会被接收端认为文本数据已经接收完了,所以end后面的数据就没有继续接收 //好像JDK17不会出现上面的情况哦,对于JDK17的话,接收端会默认把文件里面的全部数据读完,不会出现上面的问题 //如何解决文本里面出现了end字眼时,位于end后面的文本数据将不会被执行的问题,解决如下 //Socket类里面已经提供了两个用于标记的方法,如下 //shutdownInput() 将此套接字的输入流放在“流结束” //shutdownOutput() 禁用此套接字的输出流
//即不需要自己自定义结束标记,我们把上面的自定义标记也先注释掉,用下面这种写法 s.shutdownOutput();//表示输出结束
//接收一下来自接收端发来的反馈,接收的反馈用BufferedReader来封装一下 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); //由于接收端只给了一次反馈数据过来,所以我们只需要只读一次 String data = brClient.readLine();//这里也会等待读取数据 System.out.println("接收端的反馈:"+data);
//释放资源 br.close(); s.close(); //注意:要把br和s都释放
}}xxxxxxxxxxpackage ch22;
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.ServerSocket;import java.net.Socket;
//注意:这里的部分代码是上一节课的'a_14_2测试_接收'复制过来的public class a_15_2测试_接收 {
public static void main(String[] args) throws IOException {
//创建接收端Socket对象 ServerSocket ss = new ServerSocket(11114);
//监听发送端的连接,返回一个对应的Socket对象 Socket s = ss.accept();
//接收数据 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //用FileWriter来实现把数据写到文本文件,即需要把FileWriter包装成BufferWriter BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\huanf\\java\\src\\ch22\\a_15_3文本.txt"));//目标路径
//读写操作 String line; while((line=br.readLine())!=null){ //注意:这一行在一直等待着读数据,解决:在下面那行加上if判断 /*if("end".equals(line)){ break;//当把数据都接收完之后,就退出循环。依据是由发送端的结束标记 }*/ //即不需要自己自定义结束标记,在发送端使用Socket类提供的结束方法即可,这里的接收端不需要额外操作 bw.write(line); bw.newLine(); bw.flush(); }
//向发送端给出反馈。把反馈封装成BufferedWriter BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("接收端已成功接收到文件"); bwServer.newLine(); bwServer.flush();
//释放资源 bw.close(); ss.close(); //注意ss和bw都需要释放资源
}}
TCP通信文件上传_多线程
要求如下 客户端:数据来自于文本文件,且接收服务器反馈。客户端即发送端 服务器:接收到的数据写入文本文件,且向发送端给出反馈,其中,代码用线程进行封装,为每一个客户端开启一个线程。服务端即接收端\
注意:发送端的数据来自'a_13_3文本.txt',接收端的数据会写入到'a_16_4文本.txt' 注意:'a_13_3文本.txt'是已存在的,'a_16_4文本.txt'是系统帮我们创建的
如何运行:先运行'a_16_2测试接收',再运行'a_16_1测试发送'
TCP通信文件上传_多线程的练习
xxxxxxxxxxpackage ch22;
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;
//注意:这里的部分代码是上一节课的'a_15_1测试_发送'复制过来的public class a_16_1测试_发送 {
public static void main(String[] args) throws IOException {
//创建发送端Socket对象 Socket s = new Socket("10.99.20.3",11116);
//把要发送的文本文件数据先封装一下 BufferedReader br = new BufferedReader(new FileReader("D:\\huanf\\java\\src\\ch22\\a_13_3文本.txt"));
//封装输出流写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//读取要发送的数据,并且把读到的数据发生出去 String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); }
//自定义结束标记,即告诉接收端数据已经读完了,避免下面的程序一直等待着读取数据 /*bw.write("end");//实现很简单,即数据写完之后,我们再多写一个数据。这样,接收端接收完文本数据后,会多接收一个end数据 bw.newLine(); //当接收端接收到end数据,接收端就停止对文本数据的接收,解决了接收端一直处于接收等待状态的问题 bw.flush();*/ //注意:上面的自定义结束标记是有一个问题的,当"a_13_3文本.txt"文本里面出现了end字眼时,位于end后面的文本数据将不会被执行 //原因:出现end字眼会被接收端认为文本数据已经接收完了,所以end后面的数据就没有继续接收 //好像JDK17不会出现上面的情况哦,对于JDK17的话,接收端会默认把文件里面的全部数据读完,不会出现上面的问题 //如何解决文本里面出现了end字眼时,位于end后面的文本数据将不会被执行的问题,解决如下 //Socket类里面已经提供了两个用于标记的方法,如下 //shutdownInput() 将此套接字的输入流放在“流结束” //shutdownOutput() 禁用此套接字的输出流
//即不需要自己自定义结束标记,我们把上面的自定义标记也先注释掉,用下面这种写法 s.shutdownOutput();//表示输出结束
//接收一下来自接收端发来的反馈,接收的反馈用BufferedReader来封装一下 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); //由于接收端只给了一次反馈数据过来,所以我们只需要只读一次 String data = brClient.readLine();//这里也会等待读取数据 System.out.println("接收端的反馈:"+data);
//释放资源 br.close(); s.close(); //注意:要把br和s都释放
}}xxxxxxxxxxpackage ch22;
import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;
public class a_16_2测试_接收 {
public static void main(String[] args) throws IOException {
//创建接收端Socket对象 ServerSocket ss = new ServerSocket(11116);
//让服务器一直开着 while(true){ //监听发送端的连接,返回一个对应的Socket对象 Socket s = ss.accept(); //为每一个客户端开启一个线程 new Thread(new a_16_3ServerThread(s)).start(); //我们要写一个'ServerThread'类,并让这个类实现Runnable接口 }
}}xxxxxxxxxxpackage ch22;
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;
//接收端的线程类,是为接收端服务的public class a_16_3ServerThread implements Runnable {
//定义成员变量 private Socket s;
//带参构造方法 public a_16_3ServerThread(Socket s) { this.s = s; }
//重写Run方法 public void run() { try {//try...catch捕获异常,把可能会出现异常的代码写到try里面 //接收数据,且写到文本文件 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\huanf\\java\\src\\ch22\\a_16_4文本.txt")); //上面那行有点小问题:如果有多台发送端,那就不应该使用固定的文件路径,我们需要让每个发送端都有的接收端的文件,解决如下: //提示:如何启动多台发送端,即在发送端那里多运行一次就相当于多了一个发送端,每个发送端发送的数据都会被这个接收端处理成一个文件,且这些文件名不同 int count = 0; File file = new File("D:\\huanf\\java\\src\\ch22\\a_16_4["+count+"]文本.txt"); //判断是否有文件已经使用了这个名称 while(file.exists()){ count++;//这个count自加1 file = new File("D:\\huanf\\java\\src\\ch22\\a_16_4["+count+"]文本.txt");//重新new一个File对象 } 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(); //释放资源 s.close(); }catch (IOException e){ e.printStackTrace(); } }}