基于自定义字节协议的文本消息及文件消息传送

在只需要发送聊天消息时,我们只需要定义基于字符的协议就基本可以满足需求。但是,当同时需要传送文本消息与文件消息时,这样的基于字符的协议显然是不能满足要求了。因为,在发送的文件中,什么样的字符都是可能存在的,如果定义基于字符的协议,那么接收文件的一方,在解析文件时,就有可能出现错误。而基于字节的协议就可以很好的解决这个问题。
既然是自定义的协议,说明协议的定义具有很强的灵活性,程序员可以根据自己的程序的需要,自定义通信协议。只要也只有严格遵守相同的协议,客户端与服务器之间才可以正常的通信。这就好比玩游戏,只有玩家遵守同一套游戏规则,游戏才能有序正常的进行。
在我的程序中设计文本消息及文件消息的发送,他们的协议分别是:
文本消息:[color=red]消息总长 + 消息类型 + 消息内容长度 + 消息内容[/color]
其中:消息总长为一个int型占4个字节,消息类型为整数1占4个字节,消息内容长度为一个int型占4个字节,消息内容为一个byte型数组;
文件消息:[color=red]消息总长 + 消息类型 + 文件名长度 + 文件内容长度 + 文件名 + 文件内容[/color]
其中:消息总长为一个int型占4个字节,消息类型为整数2占4个字节,文件名长度为一个int型占4个字节,文件内容长度为一个int型占4个字节,文件名为一个byte型数组,文件内容为一个byte型数组;
实现通行的原则及关键只有一个,那就是通信双方,即服务器及客户端严格遵守协议;

实现技术细节:
1.将输入流包装成DataInputStream、输出流包装成DataOutputStream方便读取原始类型流
将从Socket上得到的输入流包装为DataInputStream流对象后,如果调用DataInputStream对象的readByte()方法时,只会从底层的数据流中读取一个字节返回,而调用readInt()时,则从底层读取4个字节(32位),readInt()方法内部经过为运算实现了将读到的4个字节组成一个int型数据返回。
2.read()方法与readFully()方法的区别
byte[] content = new byte[contentSize];
// 读取文件内容
dis.readFully(content);

此处从流中读取字节,填充字节数组时,没有使用read(要填充的数组)的方法,而是调用readFully()方法,这样做更安全。在网络通信中,当发送大的数据粮食,有这样一种可能:一部分数据已发送到对方,有一部份还在本地的网卡缓存中,如果调用read()方法
,可能会提前返回而没有读取到足够的数据,在传送大块数据(如一次传送一个较大文件时)可能会出错,而readFully()方法会一直等待,直到读取到的数组长度的所有数据后,才会返回。


服务器示例代码:
package 文件传输;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
private InputStream ins = null;
private OutputStream ous = null;
private DataInputStream dis = null;
private DataOutputStream dos = null;
public Server()
{
try
{
ServerSocket server = new ServerSocket(8080);
System.out.println("Server create success!");
Socket client = server.accept();
System.out.println("Client connect success!");
ins = client.getInputStream();
ous = client.getOutputStream();
dis = new DataInputStream(ins);
dos = new DataOutputStream(ous);

// 发送消息
// sendMsg("可以啦服务器->客户端");

// 发送文件
// sendFile("D://","abc.txt");

while(true)
{
int totalLen = dis.readInt();
System.out.println(totalLen);
int type = dis.readInt();
// 文本消息
if(type == 1)
{
// 读取消息内容长度
int len = dis.readInt();
byte[] msg = new byte[len];
// 读取消息内容
dis.readFully(msg);
String message = new String(msg);
System.out.println(message);
}

// 文件消息
if(type == 2)
{
// 读取文件名长度
int nameSize = dis.readInt();
// 读取文件内容长度
int contentSize = dis.readInt();

System.out.println("文件名长度"+nameSize+"~~~文件内容长度"+contentSize);

byte[] fileName = new byte[nameSize];
dis.read(fileName);
// 读取文件名
String name = new String(fileName);
System.out.println("文件名:" + name);

byte[] content = new byte[contentSize];
// 读取文件内容

dis.readFully(content);

File file = new File("F:/" + name);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file.getAbsolutePath(),true);
fos.write(content);
fos.flush();

fos.close();
dis.close();

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


// 发送消息
public void sendMsg(String str)
{
byte[] data = str.getBytes();
// 消息总长度
int totalLen = 4 + 4 + 4 + data.length ;
try {
dos.writeInt(totalLen);
dos.writeByte(1);
dos.writeInt(data.length);
dos.write(data);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}

}

public void sendFile(String path, String name)
{
try
{
FileInputStream fis = new FileInputStream(path+name);
try
{
// 获取文件内容总字节数
int contentLen = fis.available();
byte[] content = new byte[contentLen];
// 将文件内容读取到文件输入流中
fis.read(content);

// 获取文件名字节长度
int nameLen = name.getBytes().length;
byte[] nameArry = name.getBytes();

// 获取总长度
int totalLen = 4 + 4 + 4 + nameLen + contentLen;
dos.writeInt(totalLen);
dos.writeInt(2);
dos.writeInt(nameLen);
dos.writeInt(contentLen);
dos.write(nameArry);
dos.write(content);

dos.close();
fis.close();

} catch (IOException e)
{
e.printStackTrace();
}
} catch (FileNotFoundException e)
{
e.printStackTrace();
}


}


// private void writeString (DataOutputStream out, String str,int len)
// {
// byte[] data = str.getBytes();
// try {
// out.write(data);
// } catch (IOException e1)
// {
// e1.printStackTrace();
// }
// while(len > data.length)
// {
// try
// {
// out.write('\0');
// len--;
// } catch (IOException e)
// {
// e.printStackTrace();
// }
// }
//
// }


public static void main(String[] args)
{
new Server();
}


}

客户端示例代码:
package 文件传输;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

public class Client {
private InputStream ins;
private OutputStream ous;
private DataInputStream dis = null;
private DataOutputStream dos = null;

public Client(String str)
{
try
{
Socket client = new Socket(str, 8080);
ins = client.getInputStream();
ous = client.getOutputStream();
dis = new DataInputStream(ins);
dos = new DataOutputStream(ous);


// 实例化一个文本扫描器对象
Scanner scn = new Scanner(System.in);
System.out.println("请选择你要发送的消息类型:1 文本消息 2 文件消息");
// 从控制台读取一个int型数据
int a = scn.nextInt();
if(a == 1)
{
System.out.println("请输入你要发送的消息内容:");
String tempStr = scn.next();
System.out.println(tempStr);
// 发送文本信息
sendMsg(tempStr);
System.out.println("消息发送成功");
}
else if(a == 2)
{
System.out.println("请输入你要发送的文件名字:");
String tempStr = scn.next();
System.out.println(tempStr);
// 发送文件
sendFile("D://",tempStr);
System.out.println("文件发送成功");
}
System.out.println(a);
// 从控制台读取一个字符串


// 消息解析
// while(true)
// {
// int totalLen = dis.readInt();
// System.out.println(totalLen);
// int type = dis.readInt();
// // 文本消息
// if(type == 1)
// {
// // 读取消息内容长度
// int len = dis.readInt();
// byte[] msg = new byte[len];
// // 读取消息内容
// dis.read(msg);
// String message = new String(msg);
// System.out.println(message);
// }
//
// // 文件消息
// if(type == 2)
// {
// // 读取文件名长度
// int nameSize = dis.readInt();
// // 读取文件内容长度
// int contentSize = dis.readInt();
//
// System.out.println("文件名长度"+nameSize+"~~~文件内容长度"+contentSize);
//
// byte[] fileName = new byte[nameSize];
// dis.read(fileName);
// // 读取文件名
// String name = new String(fileName);
// System.out.println("文件名:" + name);
//
// byte[] content = new byte[contentSize];
// // 读取文件内容
// dis.readFully(content);
//
// File file = new File("F:/" + name);
// file.createNewFile();
// FileOutputStream fos = new FileOutputStream(file.getAbsolutePath(),true);
// fos.write(content);
// fos.flush();
//
// fos.close();
// dis.close();
// }
//
// }

} catch (UnknownHostException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
}

// 发送消息
public void sendMsg(String str)
{
byte[] data = str.getBytes();
int totalLen = 4 + 4 + 4 + data.length;
try
{
dos.writeInt(totalLen);
dos.writeInt(1);
dos.writeInt(data.length);
dos.write(data);
dos.flush();
} catch (IOException e)
{
e.printStackTrace();
}

}

// 发送文件
public void sendFile(String path, String name)
{
try
{
// 文件输入流
FileInputStream fis = new FileInputStream(path+name);
try
{
// 获取文件内容总字节数
int contentLen = fis.available();
byte[] content = new byte[contentLen];
// 将文件内容读取到文件输入流中
fis.read(content);

// 获取文件名字节长度
int nameLen = name.getBytes().length;
byte[] nameArry = name.getBytes();

// 获取总长度
int totalLen = 4 + 4 + 4 + nameLen + contentLen;
dos.writeInt(totalLen);
dos.writeInt(2);
dos.writeInt(nameLen);
dos.writeInt(contentLen);
dos.write(nameArry);
dos.write(content);

dos.close();
fis.close();

} catch (IOException e)
{
e.printStackTrace();
}
} catch (FileNotFoundException e)
{
e.printStackTrace();
}

}

public static void main(String[] args)
{
new Client("localhost");
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值