由于这是课程实验大作业记录,所以决定不拘于文章的编排格式,虽然看起来可能有点费劲,但是也算是清楚明了呈现,图片是从word文档转过来的,所以像素会被压缩模糊了一些,但也是可以接受的,希望对有需要的朋友能有所帮助。
---------------------------------------------------------------------------------------------------------------------------- ?
一、实验目的与内容:
目的:掌握面向java的互联网编程开发环境的搭建。学习、掌握java网络程序编写基本步骤,例如,基于MyEclipse或者IntelliJ IDEA平台编写简单的基于 TCP 的 单线程文件收发程序,掌握编译、运行等基本步骤和操作。
内容要求:
- 搭建开发环境(基本要求,50分):
(在自己电脑上)下载安装和配置Java开发环境(如Eclipse或IntelliJ IDEA等),并熟悉该开发环境中的常用操作。
请在报告中给出搭建环境成功的截图和简要文字说明,简述自己所了解掌握的常用操作。
- 尝试编写一个基于 TCP 的单线程文件收发程序(CS架构)(50分):
可试着根据java编程课所学到的java socket编程技术,尝试编写一个基于 TCP 的单线 程文件收发程序,需满足:
服务端程序预先定义好需要发送的文件并等待客户端的连接。
客户端连接成功后,服务端将文件发送给客户端,客户端将文件保存到本地。
需要在同一个 TCP 连接内发送多个文件,不限制文件的类型和大小(操作系统支持的前提 下)。
注意:?实验报告中需要有实验结果的截屏图像。
二、实验过程和代码与结果
1.开发环境的搭建实验过程及结果
由于之前已经安装过IDEA,又为了记录一下IntelliJ?IDEA的安装与环境搭建,所以选择先卸载然后重新安装并配置IDEA;以下为详细过程:
1)下载;
?
2)安装
?
3)安装成功后目录结构:
?
bin:容器,执行文件和启动参数等
help:快捷键文档和其他帮助文档
jbr:全称为 JetBrains Runtime, 是IDEA自带的Java运行环境
lib: idea 依赖的类库
license:各个插件许可
plugins:插件
redist:一些杂项
4)启动IDEA并登陆学生账号;
?
5)创建一个工程同时随意命名为project,同时依次打开File->Project Structure设置SDK;
?
6)在setting里面设置字的大小和行高
?
7)调试编程输出“Hello World!”;
?
8)创建模块(Module)
IDEA 是无法在同一个窗口管理n个项目。 IDEA提供的解决方案是打开多个项目实例,即打开多个项目窗口。即:一个Project打开一个Window窗口。
在IDEA中Project是最顶级的级别,次级别是 Module。一个Project可以有多个Module。
之后,我们可以在Module的src里写代码,此时Project工程下的src就没什么用了,可删。
?
9)通过插件(plugins)更换主题
?
10)设置鼠标滚轮修改字体大小
?
11)设置自动导包功能
?
?
12)修改类头的文档注释信息
?
13)设置项目文件编码
?
14) 设置当前源文件的编码
对单独文件的编码修改还可以点击右下角的编码设置区。如果代码内容中包含中文,则会弹出如上的操作选择。 其中:
①Reload 表示使用新编码重新加载,新编码不会保存到文件中,重新打开此文件,旧编码是什么依旧还是什么。
②Convert 表示使用新编码进行转换,新编码会保存到文件中,重新打开此文件,新编码是什么则是什么。
③含有中文的代码文件, Convert 之后可能会使中文变成乱码,所以在转换成请做好备份,不然可能出现转换过程变成乱码,无法还原。
?
15)设置快捷键(Keymap)
?
16)在Debugger里transport把socket改成shared memory以节省内存;
?
17)debug操作
首先点好断点,然后右键-debug出现下图:第一行(前4个箭头)从左到右是
step over下一步(不进入方法内)(F8)
step into下一步(进入方法内)(F7)
force step into 下一步(进入方法内)(alt+shift+F7)
*step into会进入你自己写的方法。force step into能够进入所有的方法,比如jdk的方法。
step out 跳出(shfit+F8)
红色正方形是stop(ctrl+F2)
小结:由第7)点和第8)点可以看出已经可以成功调试并运行程序,说明环境已经搭建成功。
见图:
?
2. 给出满足内容要求2的程序源码及运行结果,简述思路或实验过程。
1)同一个 TCP 连接内发送多个文件的本地目录
?
2)传输成功后的目标存储地址
?
3)我的基于 TCP 的单线程文件收发程序的编写思路
一次传输一个文件比较简单,但是在一次传输多个文件时会遇到这样的问题,怎样在接收文件时确定文件之间的边界呢?为了在接收端正确的拆分文件,决定在传输文件时须要传输每一个文件的大小。于是采用了这样一种策略:首先发送每一个文件的名称和大小。然后传输每一个文件的内容。部分代码如下(同一个包下建了三个class,分别是服务端(Service)、客户端(Client)静态函数接口端(StreamUtils)):
① Service端调用函数发送多个文件
②StreamUtils端写入多个文件
?
③读取文件核心代码(代码有充分注释)
详细代码见 实验一源码.zip
4)实验过程及结果
①传输文件前:
?
②运行程序传输文件后:
实验完毕。
三、实验总结
(此处写你的过程,比如遇到的错误,以及解决方法,你的所想、所得)
实验内容一:我下载重装并重新配置了Java开发环境(IntelliJ IDEA),并熟悉了该开发环境中的常用操作,比如:打开File->Project Structure设置SDK、在setting里面设置字的大小和行高、创建模块(Module)、通过插件(plugins)更换主题、设置鼠标滚轮修改字体大小、设置自动导包功能、修改类头的文档注释信息、设置项目文件编码、设置当前源文件的编码、设置快捷键(Keymap)、在Debugger里transport把socket改成shared memory以节省内存、debug操作等等,总体难度较小,花时间和心思查阅一下资料同时上手操作熟悉便就做完。
不过在其中遇到个问题:idea中java项目增加module后,增加的目录(src)无法增加包(Package)。解决方法:需要将新增src目录变为Sources Root即可。
实验内容二:一次传输一个文件比较简单,但是在一次传输多个文件时会遇到这样的问题,怎样在接收文件时确定文件之间的边界呢?为了在接收端正确的拆分文件,决定在传输文件时须要传输每一个文件的大小。于是采用了这样一种策略:首先发送每一个文件的名称和大小,然后传输每一个文件的内容。最后在客户端接受并保留到指定地址即可。
感想:我是挺想学Java编程的,上学期没有选修上也没有加位Java基础课,虽然这门互联网编程课前提要有Java基础,但是这并不妨碍我学习这门课,一是我较早学了Java基础,之前掌握了C++和C上手Java就很快;二是也学习了舍友的Java课的大作业实验,心里对Java有底。这个实验帮我熟悉并记录了Java开发环境配置与常用操作,以及实操了基于 TCP 的单线程文件收发程序,收获充足,自己也有不足的地方,比如在一次性发送多个文件的程序设计中,除了上述方式外并没有拓展想出新传输方式,希望迟点能在答案中启发新的思路。未来希望自己在这门课的学习中有所成长,提升自己的Java互联网编程能力,希望自己不要羡于形形色色的方法论和技巧,脚踏实地,知行合一,深入思考,一步一个脚印地学,宁拙勿巧,做一个诚心想学的学徒。
(请在报告中给出搭建环境成功的截图和简要文字说明,简述自己所了解掌握的常用操作。)
指导教师批阅意见: 成绩评定: ????????????????????????????????????指导教师签字:? ????????????????????????????????????????????????????年 ???月 ???日 | 备注: |
注:1、报告内的项目或内容设置,可根据实际情况加以调整和补充。
????2、教师批改学生实验报告时间应在学生提交实验报告时间后10日内。
补充:实验内容二源码
package socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 吕同学
* @create 2022-03-13 22:24
*/
public class Service {
//发现必须要处理异常,不处理就会报错,可以用 throws,也可以用 try...catch
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);//创建socket对象并监听8888端口
System.out.println("服务器成功启动,在8888端口监听,等待连接\n");
//等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("socket连接成功!服务端 socket = " + socket.getClass() + "\n");
//创建读取磁盘文件的输入流
//String filePath = "e:\\qie.png";
File srcFile = new File("D:\\javaHomeworkFiles");
File[] files = srcFile.listFiles();
// for(File file:files){
// System.out.println(file);
// }
//服务端调用函数发送文件
StreamUtils.sendFiles(socket,files);
//=====接收从服务端回复的消息=====
InputStream inputStream = socket.getInputStream();
//使用StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭流和socket
socket.close();
serverSocket.close();//关闭
}
}
//list()方法是返回某个目录下所有文件和目录的名字,返回的是String数组
//listFiles()方法是返回某个目录下所有文件和目录的绝对路径,返回的是File数组
//java.io.File.isFile() 检查表示此抽象路径名的文件是否是一个正常的文件。
/*String filePath = "D:\\javaHomeworkFiles\\" + files[0];
System.out.println(filePath);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));*/
//bytes 就是filePath对应的字节数组
// byte[] bytes = StreamUtils.streamToByteArray(bis);
//通过socket获取到输出流, 将bytes数据发送给客户端
/*BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
socket.shutdownOutput();//设置写入数据的结束标记*/
package socket;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;
import static socket.StreamUtils.streamToString;
/**
* @author 吕同学
* @create 2022-03-13 11:19
*/
public class Client {
public static void main(String[] args) throws Exception {
//客户端连接服务端 8888,得到Socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
System.out.println("客户端成功连接服务端 8888,得到Socket对象");
//System.out.println(socket.getInputStream());
StreamUtils.receiveFiles(socket);
// 向服务端回复 "收到图片"
// 通过socket获取到输出流(字符)
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("客户端成功收到文件并保存到了指定地址");
writer.flush();//把内容刷新到数据通道
socket.shutdownOutput();//设置写入结束标记
//关闭其他资源
writer.close();
// bis.close();
socket.close();
}
}
/*
//读取服务端发送的数据
//通过Socket得到输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//将得到 bytes 数组,写入到指定的路径,就得到一个文件了
String destFilePath = "C:\\Users\\HW\\IdeaProjects\\project1\\exercise\\src\\socket\\"+"gogogo.txt";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
bos.write(bytes);
bos.close();*/
package socket;
/**
* @author 吕同学
* @create 2022-03-13 11:12
*/
import java.io.*;
import java.net.Socket;
/**
* 功能:将输入流转换成byte[]
*/
public class StreamUtils {
public static byte[] streamToByteArray(InputStream is) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
bos.write(b, 0, len);
}
byte[] array = bos.toByteArray();
bos.close();
return array;
}
/**
* 功能:将InputStream转换成String
*/
public static String streamToString(InputStream is) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) { //当读取到 null时,就表示结束
builder.append(line + "\r\n");
}
return builder.toString();
}
/**
* 功能:发送多个文件
*/
public static void sendFiles(Socket socket, File[] files) throws Exception {
long totalSize = 0;
byte buf[] = new byte[8192];
int len;
if (socket.isOutputShutdown()) {
return;
}
DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
//将文件数量大小写入数据通道
dout.writeInt(files.length);
//System.out.println(files.length);
for (int i = 0; i < files.length; i++) {
//写入文件名
dout.writeUTF(files[i].getName());
dout.flush();
//写如单个文件长度,用以正确拆分不同文件,注意写入的格式是不一样的
dout.writeLong(files[i].length());
dout.flush();
totalSize += files[i].length();
//System.out.println(files[i].getName());
}
dout.writeLong(totalSize);
//BufferedOutputStream[] bos = new BufferedOutputStream[files.length];
for (int i = 0; i < files.length; i++) {
// BufferedInputStream din = new BufferedInputStream(new FileInputStream(files[i]));
// while ((len = din.read(buf)) != -1) {
// dout.write(buf, 0, len);
// }
//bos[i] = new BufferedOutputStream(socket.getOutputStream());
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(files[i]));
byte[] bytes = StreamUtils.streamToByteArray(bis);
bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
bos.flush();
//socket.shutdownOutput();//设置写入数据的结束标记
}
System.out.println("文件传输完毕");
socket.shutdownOutput();
return;
}
public static void receiveFiles(Socket socket) throws Exception {
// File dirs = new File(mFilePath);
// if (!dirs.exists()) {
// dirs.mkdirs();
// }
long totalSize = 0;
DataInputStream din = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
int fileNum = din.readInt();
String[] fileinfos = new String[fileNum];
for (int i = 0; i < fileNum; i++) {
//fileinfos[i] = new FileInfo();
//读取文件名
fileinfos[i] = din.readUTF();
System.out.println(fileinfos[i]);
// fileinfos[i].length()= din.readLong();
//读取文件长度
if (fileinfos[i].length() == din.readLong())
continue;
}
totalSize = din.readLong();
System.out.println(fileNum);
//System.out.println(totalSize);
for(int i=0;i<fileNum;i++){
//读取服务端发送的数据
//通过Socket得到输入流
byte[] bytes = din.readAllBytes();
//将得到 bytes 数组,写入到指定的路径,就得到一个文件了
String destFilePath = "C:\\Users\\HW\\IdeaProjects\\project1\\exercise\\src\\socket\\" + fileinfos[i];
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
bos.write(bytes);
bos.close();
}
int leftLen = 0; // 写满文件后缓存区中剩余的字节长度。
}
}
我是吕同学,祝自己也您变强了~
|