能够传送文件的聊天室

本文回顾了作者最初学习Java时创建的类似QQ登录界面,随后深入探讨了计算机之间通信原理,通过TCP/IP协议在Java环境下实现了多人之间简单文字对话和文件传输功能。文章详细解析了从底层物理层到网络层的通信过程,解释了TCP和UDP协议的区别,以及IP协议在统一数据传输格式和唯一标识网络设备地址方面的作用。通过创建一个遵循TCP/IP协议的聊天室,文章介绍了服务器与客户端的概念,以及如何使用Java.net包中的API进行网络通讯,包括建立连接、发送和接收数据的过程。此外,文章分享了在实现过程中获得的心得体会,强调了不断学习和实践的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回顾最初学习Java之时,曾经做了一个类似QQ的登录界面。但那也仅仅是一个界面,并没有实现真正意义上的登陆服务器。近日学习了有关通信技术的原理,结合以前学过的知识,不仅实现了多人之间简单的文字对话,而且还能给对方传送文件。同时,在和别人交流的过程中也收获了不少心得,真可谓是“受益匪浅”啊。

实现上述的功能:
首先,得弄明白的计算机之间是怎么交流的?参阅文献,我们能够了解到:“……为了使不同计算机厂家生产的计算机能够相互通信,以便在更大的范围内建立计算机网络,ISO在1978年提出了“开放系统互联参考模型”,它将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。……”说了一大段,就自身目前的知识水平来看,底层的过程(物理层和数据链路层)还不能解释清楚,倘若从自己能够把握的层面(网络层、传输层)出发,也就就不难理解了。
在传输层,主机之间的数据传输遵循TCP,UDP协议等。其中,TCP协议是“面向连接”的协议,意思就是在正式通信之前,必须要先与对方建立起连接。就好比你给别人打电话,必须等线路接通了,双方之间才能相互通话;而UDP是“面向非连接”的,就是在正式通信前不必与对方先建立连接,不管对方状态如何就直接发送。这个与手机短信非常相似:你在发短信的时候,只需要输入对方手机号并发送出去就OK了,而不需考虑对方的状态。上述两种协议各有千秋,有差异那是因为适用于不同要求的通信环境。
在网络层,主机之间要实现网络互连,就得遵循IP协议。以太网、分组交换网等,它们相互之间不能互通,其主要原因是因为它们所传送数据的基本单元(技术上称之为“帧”)的格式不同。IP协议把各种不同“帧”统一转换成“网协数据包”格式,这种转换使所有各种计算机都能在因特网上实现互通。同时,IP还有一个重要的任务:给因特网的每一台联网设备规定一个地址,并且这个地址是唯一的。

基于以上的认识,我选择创建一个遵循TCP/IP协议的聊天室。在那之前,我们得明白“服务器”和“客户机”的概念。这就好比我们日常生活中对话一样,前者可以喻为“话题的开启者”,在等待着别人的参与,另一方是“话题的参与者” ,主动去参与别人开启的话题。。。因此,首要任务就是开启话题:Java实现网络通讯程序需要引入java.net包下面的API。过程如下:
第一步,通俗的讲,就是一个人(暂定这个人叫A)买了一台只能够接听别人(暂定这个人叫B)来电的手机,并且给手机注册了手机号码。你可能会问,怎么会有这样的人嘛,SB?嘿嘿,勿喷,先听我娓娓道来,A买手机ss_A,并注册号码: ServerSocket ss_A= new ServerSocket(port); 其中port为“端口序号”,并且port是一个0~65535的int型十进制数;在这里,我们把每个手机号码理解为"ip地址 + port"为什么要这样,一会儿就明白了)。一台手机可能不只有一个号码哦,嘿嘿。
第二步,A等待B给 他(她) 打电话:ss_A.accept(); 这个方法会“阻塞”,也就是A一直等待,直到B给他(她)打电话。才会跳出这个方法。
第三步,B有“要事”得跟A商量,所以B得去买一部手机,并且给A打过去: Socket s_B = new Socket (ip, port); 因为每台“手机”的ip是唯一的,而“手机号码”可能不止一个,所以用“ip地址+port”表示“手机号码”就能说得过去了。。一旦B给A打电话,如果没有拨错“手机号码”的话,就能拨通,然后A与B之间的连接就建立起来,这个连接的桥梁就是s_A : Socket s_A = ss_A.accept();
第四步,既然已经连接上了,那么,接下来就可以“说话”了?嘿嘿,还差一步:要找到“话筒”和“听筒”,试想如果没有这两个,会怎么样呢。 这里有很多类型“话筒”和“听筒”噢:DataOutputStream和DataInputStream ,OutputStream和InputStream与BufferedOutputStream和BufferedInputStream等,只需选其中一种就可以了,以A为例:A的手机的“话筒”:DataOutputStream dos = new DataOutputStream(s.getOutputStream());“听筒”:DataInputStream dis = new DataInputStream(s.getInputStream()); ,B同理。
第五步,可以商量“要事”啦!!。怎么听?又怎么说? 在此之前,双方要在事先“约定”说什么话就怎么听。 还是以A为例,A要“说话" ,对着A的“话筒”说不就完了吗? 也就是 dos.writeUTF; 当然也有writeInt(); , writeByte(); writeFloat();等,就理解为“说不同的语言”吧,“听筒”也类似,对方怎么说你就怎么听。否则很有可能造成“听风就是雨”。
第六步,到这里的话很感谢您能继续阅读下去,不过我很遗憾的告诉您:“没有第六步啦!!” 既然都能够实现简单对话了,那么传送文件就不在话下了。双方要事先达成传送文件的约定,那就是定义一个“秘密口令”,如果有其中一方发出了这条“秘密口令”,那么另一方收到“秘密口令”之后就做好接收文件的准备,然后“口令的发出者”还得把 文件的基本属性:文件名称,文件大小,文件内容等发送给对方,“文件的接收者”就根据接收到的消息在自己指定的地方创建一个一模一样的文件即可。到了这一步,要做的工作就和第五步一样了。
至此,分析过程基本完成。文件传送部分的关键代码如下:
发送文件端:
//给“发送文件”按钮 添加监听器
sendFile.addMouseListener(new MouseAdapter() {

@Override
public void mouseClicked(MouseEvent e) {

JFileChooser jfc = new JFileChooser();//触发按钮之后实例化文件选择器
jfc.showOpenDialog(null);//在屏幕上居中显示对话框
File f = jfc.getSelectedFile();//获取文件选择器选中的文件

if(f != null && f.isFile()){ //如果选中的文件不为空并且就是文件
try {
String str = "THIS_IS_A_FILE_WILL_BE_SEND";//传送文件的“秘密口令”
dis = new DataInputStream(new BufferedInputStream(new FileInputStream(f.getAbsolutePath())));
dos = new DataOutputStream(s.getOutputStream());
//将文件名及长度传给客户端
dos.writeUTF(str); //写入“口令”来告诉服务器“我要传送文件给你”
dos.writeUTF(f.getName());//写入“文件名”来告诉服务器“我要传送的文件的文件名是XXX”
dos.flush();//强制输出
dos.writeLong((long) f.length());//写入“文件占用的空间大小”来告诉服务器“我要传送的文件所占用的空间大小为XXXXX”
dos.flush();//强制输出
int bufferSize = 8192;////缓冲区的大小
byte[] buf = new byte[bufferSize];//开辟预定缓冲区大小的byte数组,用来存取文件数据
//将文件内容写入“输入流”
while (true) {
int read = 0;
if (dis != null) read = dis.read(buf);
if (read == -1) break;
dos.write(buf, 0, read);
}
dos.flush();//强制输出
System.out.println("文件传输完成");
String string = "您成功上传了" + f.getName() + "到服务器!";
appendMassage(string);//在“聊天记录区域”显示传送结果

} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});

接收文件端:首先,读入一行字符串,以此来判断下一步的操作:
String str = dis.readUTF();//从“客户端”的“输入流”中读出字符串赋给str 

然后,判断是否为 传送文件的"秘密口令",如果是,就执行接收文件的方法,否则就当作文本信息处理:
 //判断是否要接收文件
if(str.contains("THIS_IS_A_FILE_WILL_BE_SEND")) {
getFile();//接收文件的方法
}

接收文件的方法如下:
//接收文件的方法
public void getFile() {

try {
String savePath = "C:\\Users\\Administrator.ACER-PC\\Desktop\\";//预存放接收文件存放的路径
int bufferSize = 8192;//缓冲区的大小
byte[] bytebuffer = new byte[bufferSize];//开辟预定缓冲区大小的byte数组,用来存取文件数据
int passedLength = 0;//已经传送的文件的大小
long fileLength = 0;//文件总长度
savePath += dis.readUTF();//存放接收文件的绝对路径 = 预存放接收文件存放的路径 + 文件名
System.out.println("dis.readUTF():" + dis.readUTF());
dos = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
fileLength = dis.readLong();
System.out.println("文件的长度为:" + fileLength + "\n");
System.out.println("开始接收文件!" + "\n");
while (true) {
int read = 0;
if (dis != null) {
read = dis.read(bytebuffer);
}
passedLength += read;//已经传送的长度 = 输入流读取的长度
if (read == -1) break;
System.out.println("文件已接收了" + (passedLength * 100 / fileLength) + "%");
dos.write(bytebuffer, 0, read);
}
System.out.println("文件接收完成,存为: " + savePath + "\n");
// fileOut.close();
} catch (Exception e) {
System.out.println("接收消息错误!!" + "\n");
return;
}
}

心得:一方面,加深了对技术的理解;另一方面,我认为更重要的是,一个人如果真的决心去做一件事,那么任何理由都不成理由.就像现在,我熬到这个点也要写好我的技术博客一样,再累再苦再难再忙,都不要忘了使自己进步,都不要忘了坚持自己的理想。只要时常用理想来激励自己,就不会迷失自己,碌碌无为。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值