Java通信——传文件、消息
服务器Server端
首先,创建服务器,循环等待接入客户端
java.net.ServerSocket server = new java.net.ServerSocket(port);//创建服务器
java.net.Socket client = server.accept();//客户端接入
接入客户端之后,开始接受消息。
在这里,接受的消息不只是一个简单的内容,还含有消息头,消息头中含有有关消息的规范。
- 消息长度——一个int(4字节)
- 消息类型——一个byte(1字节)
- 目标用户号码——一个int(4字节)
消息类型这里分为两种:
- 1代表普通消息
- 2代表文件
接收普通消息
byte[] data = new byte[totalLen-4-1-4];
//从流中读取data.length个字节放入数组
dins.readFully(data);
String msg = new String(data);
System.out.println("发送文本给:"+destNum+"内容是:"+msg);
这里注意readfully和read的区别:
网络通信中,当发送大的数据量时,有这样一种可能:一部
分数据已发送到对方,有一部分数据还在本地的网卡缓存中,如果调用 read()方法,可能会提前
返回而没有读取到足够的数据,在传送大块数据(如一次传送一个较大文件时)可能会出错,而
readFully()方法会一直等待,读取到数组长度的所有数据后,才会返回。
这里是个大坑,我之前传图片会出现传送之后,接受到的图片只显示一半。原因就在此。
我用read进行了测试,结果生成的图片虽然大小一模一样,但是生成图片无法显示!!!
接收文件
//256个字节作为文件名大小
System.out.println("发送文件给:"+destNum); //destnum为接收者
byte[] data = new byte[256];
dins.readFully(data);
String fileName = new String (data).trim();
System.out.println("读到文件名字是:"+fileName);
//读文件内容
data = new byte[totalLen-4-1-4-256];
dins.readFully(data);
//保存在当前目录下
FileOutputStream fous = new FileOutputStream(fileName);
fous.write(data);
fous.flush(); //保证可靠
fous.close();
关于flush:原文链接:https://blog.csdn.net/qq_38129062/article/details/87115620
设想要给鱼缸换水,所以需要一个水泵,水泵是连接鱼缸和下水道的,咱们的任务就是将鱼缸里面水全抽干,这时,我们就可以把水管当做缓冲区。如果咱们一见鱼缸里面水抽干了就立马关了水泵,这时会发现水管里还有来不及通过水泵流向下水道的残留水,我们可以把抽水当做读数据,排水当做写数据,水管当做缓冲区,这样就容易明白了。
那么这样一来我们如果中途调用close()方法,输出区也还是有数据的,就像水缸里有水,只是在缓冲区遗留了一部分,这时如果我们先调用flush()方法,就会强制把数据输出,缓存区就清空了,最后再关闭读写流调用close()就完成了。
收文件时,用了FileOutputStream,文件输出流,它把文件直接写在了当前目录下。
可以直接规定文件输出的位置:
FileOutputStream fous = new FileOutputStream("C:\\Users\\董润泽\\Desktop");
客户机client端
首先创建客户端并通过ip和主机端口连接到服务器
java.net.Socket client = new java.net.Socket(ip,port);
然后获取输入流和输出流:
java.io.InputStream ins = client.getInputStream();
OutputStream ous = client.getOutputStream();
在while循环内持续发送信息
发送普通消息
byte[] strb = msg.getBytes();//得到消息的字节数
int totalLen = 4+4+1+strb.length;
System.out.println("发送总长为:"+totalLen);
dous.writeInt(totalLen);
dous.writeByte(1);//类型1代表文本
dous.writeInt(destNum);//写入目标用户号
dous.write(strb);
dous.flush();
其中dous为数据输出流
private DataOutputStream dous;
发送文件
//根据文件名创建文件对象
File file = new File(fileName);
//根据文件对象构造一个输入流
FileInputStream ins = new FileInputStream(file);
int fileDataLen = ins.available(); //文件数据总长
int totalLen = 4+1+4+256+fileDataLen; //得到了要发送数据包的总长
dous.writeInt(totalLen);
dous.writeByte(2);
dous.writeInt(destNum);
String shortFileName = file.getName();
//写入文件名
writeString(dous,shortFileName,256);
byte[] fileData = new byte[fileDataLen];
ins.read(fileData);//读入文件数据
dous.write(fileData);//写到服务器的流中
dous.flush();
注意:发送文本和文件的正文前,首先要发出文件头!
代码
server
package qq_Server_test;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import org.omg.CORBA.portable.InputStream;
public class Server {
public void setUpServer(int port) {
try {
java.net.ServerSocket server = new java.net.ServerSocket(port);
System.out.println("服务器创建完成");
while (true) {
java.net.Socket client = server.accept();
System.out.println("客户端已接入");
process(client);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void process(java.net.Socket client) {
try {
OutputStream ous = client.getOutputStream();
java.io.InputStream ins = client.getInputStream();
//将输入流包装成DataInputStream以便读取数据
DataInputStream dins = new DataInputStream(ins);
while(true){
//读取消息,消息以int开头
//1、读消息长度
int totalLen = dins.readInt();
System.out.println("发来消息长度为:"+totalLen);
//2、读消息类型标识,读一个字节
byte flag = dins.readByte();
System.out.println("接收消息类型为:"+flag);
//3、读取目标客户号码,一个 int
int destNum = dins.readInt();
System.out.println("目标用户号码为:"+destNum);
//根据消息内容读取消息体
if(flag==1){//文本消息
byte[] data = new byte[totalLen-4-1-4];
//从流中读取data.length个字节放入数组
dins.readFully(data);
String msg = new String(data);
System.out.println("发送文本给:"+destNum+"内容是:"+msg);
}
else if(flag==2){//文件数据
//256个字节作为文件名
System.out.println("发送文件给:"+destNum);
byte[] data = new byte[256];
dins.readFully(data);
String fileName = new String (data).trim();
System.out.println("读到文件名字是:"+fileName);
//读文件内容
data = new byte[totalLen-4-1-4-256];
dins.readFully(data);
//保存在当前目录下
FileOutputStream fous = new FileOutputStream(fileName+1);
fous.write(data);
fous.flush();
fous.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.setUpServer(9900);
}
}
client
package qq_client_test;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
import org.omg.CORBA.portable.InputStream;
public class Client {
private DataOutputStream dous;//输出流对象
private void writeString(DataOutputStream out,String str,int len){
//向流中写入指定长度的字节,如果不足,就补0
try{
byte[] data = str.getBytes();
out.write(data);
while(len>data.length){
out.writeByte('\0');
len--;
}
}catch(Exception e){
e.printStackTrace();
}
}
private void sendTextMsg(String msg,int destNum){
//发送消息
try{
byte[] strb = msg.getBytes();//得到消息的字节数
int totalLen = 4+4+1+strb.length;
System.out.println("发送总长为:"+totalLen);
dous.writeInt(totalLen);
dous.writeByte(1);//类型1代表文本
dous.writeInt(destNum);//写入目标用户号
dous.write(strb);
dous.flush();
}catch(Exception e){
e.printStackTrace();
}
}
private void sendFileMsg(String fileName, int destNum){
try{
//根据文件名创建文件对象
File file = new File(fileName);
//根据文件对象构造一个输入流
FileInputStream ins = new FileInputStream(file);
int fileDataLen = ins.available(); //文件数据总长
int totalLen = 4+1+4+256+fileDataLen; //得到了要发送数据包的总长
dous.writeInt(totalLen);
dous.writeByte(2);
dous.writeInt(destNum);
String shortFileName = file.getName();
//写入文件名
writeString(dous,shortFileName,256);
byte[] fileData = new byte[fileDataLen];
ins.read(fileData);//读入文件数据
dous.write(fileData);//写到服务器的流中
dous.flush();
}catch(Exception e){
e.printStackTrace();
}
}
public void conn2Server(String ip, int port){
try{
java.net.Socket client = new java.net.Socket(ip,port);
java.io.InputStream ins = client.getInputStream();
OutputStream ous = client.getOutputStream();
dous = new DataOutputStream(ous);
int testCount = 0;
while(true){
System.out.println("登陆服务器成功,选择要发的类型(1、聊天,2、文件):");
java.util.Scanner sc = new Scanner(System.in);
int type = sc.nextInt();
if(type==1){
sendTextMsg("abc聊天"+testCount, 8888);
}
if(type==2){
sendFileMsg("C:\\Users\\董润泽\\Desktop\\test1.jpg", 8888);
}
testCount++;
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client();
client.conn2Server("localhost", 9900);
}
}
运行结果:
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2470290795@qq.com
文章标题:Java通信——传文件、消息
文章字数:1.9k
本文作者:runze
发布时间:2020-01-29, 19:47:57
最后更新:2020-01-29, 20:42:05
原始链接:http://yoursite.com/2020/01/29/Java/Java%E9%80%9A%E4%BF%A1%E2%80%94%E2%80%94%E4%BC%A0%E6%96%87%E4%BB%B6/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。