TCP通信文件上传_接收端给出反馈
要求如下 客户端:数据来自于文本文件,且接收来自接收端的反馈。客户端即发送端 服务器:接收到的数据写入文本文件,且向发送端给出反馈。服务端即接收端
注意:发送端的数据来自'a_13_3文本.txt',接收端的数据会写入到'a_15_3文本.txt' 注意:'a_13_3文本.txt'是已存在的,'a_15_3文本.txt'是系统帮我们创建的
相当于实现了文件上传,即把你的文件发给你的同桌
如何解决文本里面出现了结束标记字眼时,位于结束标记后面的文本数据将不会被执行的问题,解决如下 shutdownInput() 将此套接字的输入流放在“流结束” shutdownOutput() 禁用此套接字的输出流 简单理解就是,不需要我们使用结束标记来终止接收端的接收,Socket类里面已经提供了两个用于标记的方法 原理:发送端在方式数据时,会把结束标记也发出去,当接收端接收数据完成后会接收到这个结束标记,接收到结束标记接收端就会关闭接收,避免了接收端一直等待
总结:当出现程序一直等待的问题时,原因是:读数据的方法是阻塞式的。解决方式有两种,如下 1、自定义结束标记,但是这种方式会带来另一个问题,当文本数据出现'自定义结束标记'字眼会被接收端认为文本数据已经接收完了,所以后面的数据就没有继续接收 2、使用shutdownOutput方法,这种方式解决起来比较便捷,只需要在发送端写就行
TCP通信文件上传_接收端给出反馈的练习
xxxxxxxxxx
package 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都释放
}
}
xxxxxxxxxx
package 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通信文件上传_多线程的练习
xxxxxxxxxx
package 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都释放
}
}
xxxxxxxxxx
package 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接口
}
}
}
xxxxxxxxxx
package 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();
}
}
}