在只需要发送聊天消息时,我们只需要定义基于字符的协议就基本可以满足需求。但是,当同时需要传送文本消息与文件消息时,这样的基于字符的协议显然是不能满足要求了。因为,在发送的文件中,什么样的字符都是可能存在的,如果定义基于字符的协议,那么接收文件的一方,在解析文件时,就有可能出现错误。而基于字节的协议就可以很好的解决这个问题。
既然是自定义的协议,说明协议的定义具有很强的灵活性,程序员可以根据自己的程序的需要,自定义通信协议。只要也只有严格遵守相同的协议,客户端与服务器之间才可以正常的通信。这就好比玩游戏,只有玩家遵守同一套游戏规则,游戏才能有序正常的进行。
在我的程序中设计文本消息及文件消息的发送,他们的协议分别是:
文本消息:
消息总长 + 消息类型 + 消息内容长度 + 消息内容
其中:消息总长为一个int型占4个字节,消息类型为整数1占4个字节,消息内容长度为一个int型占4个字节,消息内容为一个byte型数组;
文件消息:
消息总长 + 消息类型 + 文件名长度 + 文件内容长度 + 文件名 + 文件内容
其中:消息总长为一个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");
}
}
分享到:
相关推荐
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...
Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...
Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你... //增加信息 …… Java实现的点对点短消息发送协议(smpp)开发包源码 70个目标文件,如题。 Java实现的放大...
Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你... //增加信息 …… Java实现的点对点短消息发送协议(smpp)开发包源码 70个目标文件,如题。 Java实现的放大...
• 修正了通过 SFTP 下载时 0 字节文件没有适当关闭的问题 • 现在你能够使用 Ctrl+L 在本地与远端视图之间切换 FlashFXP v3.8 BETA - (3.7.5 build 1297) • 修正了应用某些示意图样式时传送示意图绘制的问题 • ...
可以自定义不同文件类型的显示颜色;可以缓存远端文件夹列表,支持FTP代理及 Socks 3&4;具有避免空闲功能,防止被站点踢出;可以显示或隐藏“隐藏”属性的文件、文件夹;支持每个站点使用被动模式等。 <br>性能...
** 以下是易语言4.x及以前版本的升级信息 ******************************************************************************** 易语言4.14版相对于4.13更新说明: 对易语言核心支持库、编译器、开发环境的更新...
当然用户也可以定义自己的消息名称,也可以利用自定义消息来发送通知和传送数据。 2、谁将收到消息:一个消息必须由一个窗口接收。在窗口的过程(WNDPROC)中可以对消息进行分析,对自己感兴趣的消息进行处理。例如...
88 <br>0136 如何进行文本加密与解密 88 <br>0137 如何区别0、空字符串、Null、Empty和Nothing 89 <br>0138 从字符串中分离文件路径、文件名及扩展名 89 <br>0139 如何批量替换某一类字符串 89...
主机与从机间的通信协议采取自定义的方式。每一次通信以主机向从机发送一个命令 帧开始,从机收到命令帧后,向主机回送一个响应帧。发送和接收以文本方式进行。 关键词:主从控制 单片机 89C51 RS485 通信协议 ...
2. Windows98支持下面___C__网络协议。 A、Net BEUI B、IPX/SPX C、TCP/IP D、Banyan VINES 3.为了方便人们记忆;阅读和编程,把机器语言进行符号化,相应的语言称为__D__。 A、数据库系统 B、高级语言 C、源程序...
5.5.5 分隔文本文件的处理167 5.5.6二进制文件的处理169 5.5.7 FileSystemObject分层结构171 5.6与Windows交互178 5.6.1 App对象178 5.6.2 Clipboard对象180 5.6.3 Printer对象184 5.6.4运行其他应用程序186 5.6.5...