`
SurpriseLee
  • 浏览: 7247 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

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

 
阅读更多
       在只需要发送聊天消息时,我们只需要定义基于字符的协议就基本可以满足需求。但是,当同时需要传送文本消息与文件消息时,这样的基于字符的协议显然是不能满足要求了。因为,在发送的文件中,什么样的字符都是可能存在的,如果定义基于字符的协议,那么接收文件的一方,在解析文件时,就有可能出现错误。而基于字节的协议就可以很好的解决这个问题。
       既然是自定义的协议,说明协议的定义具有很强的灵活性,程序员可以根据自己的程序的需要,自定义通信协议。只要也只有严格遵守相同的协议,客户端与服务器之间才可以正常的通信。这就好比玩游戏,只有玩家遵守同一套游戏规则,游戏才能有序正常的进行。
       在我的程序中设计文本消息及文件消息的发送,他们的协议分别是:
       文本消息:消息总长 + 消息类型 + 消息内容长度 + 消息内容
       其中:消息总长为一个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上百实例源码以及开源项目

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...

    java源码包---java 源码 大量 实例

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...

    java源码包2

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...

    java源码包3

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...

    java源码包4

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码...

    JAVA上百实例源码以及开源项目源代码

     Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你... //增加信息 …… Java实现的点对点短消息发送协议(smpp)开发包源码 70个目标文件,如题。 Java实现的放大...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你... //增加信息 …… Java实现的点对点短消息发送协议(smpp)开发包源码 70个目标文件,如题。 Java实现的放大...

    FlashFXP(免注册)

    • 修正了通过 SFTP 下载时 0 字节文件没有适当关闭的问题 • 现在你能够使用 Ctrl+L 在本地与远端视图之间切换 FlashFXP v3.8 BETA - (3.7.5 build 1297) • 修正了应用某些示意图样式时传送示意图绘制的问题 • ...

    FlashFXP 3.4.1.1193 BETA

    可以自定义不同文件类型的显示颜色;可以缓存远端文件夹列表,支持FTP代理及 Socks 3&4;具有避免空闲功能,防止被站点踢出;可以显示或隐藏“隐藏”属性的文件、文件夹;支持每个站点使用被动模式等。 <br>性能...

    易语言程序免安装版下载

    ** 以下是易语言4.x及以前版本的升级信息 ******************************************************************************** 易语言4.14版相对于4.13更新说明: 对易语言核心支持库、编译器、开发环境的更新...

    C++MFC教程

    当然用户也可以定义自己的消息名称,也可以利用自定义消息来发送通知和传送数据。 2、谁将收到消息:一个消息必须由一个窗口接收。在窗口的过程(WNDPROC)中可以对消息进行分析,对自己感兴趣的消息进行处理。例如...

    C#编程经验技巧宝典

    88 <br>0136 如何进行文本加密与解密 88 <br>0137 如何区别0、空字符串、Null、Empty和Nothing 89 <br>0138 从字符串中分离文件路径、文件名及扩展名 89 <br>0139 如何批量替换某一类字符串 89...

    单片机主从控制系统的软硬件设计.doc

    主机与从机间的通信协议采取自定义的方式。每一次通信以主机向从机发送一个命令 帧开始,从机收到命令帧后,向主机回送一个响应帧。发送和接收以文本方式进行。 关键词:主从控制 单片机 89C51 RS485 通信协议 ...

    会计理论考试题

    2. Windows98支持下面___C__网络协议。 A、Net BEUI B、IPX/SPX C、TCP/IP D、Banyan VINES 3.为了方便人们记忆;阅读和编程,把机器语言进行符号化,相应的语言称为__D__。 A、数据库系统 B、高级语言 C、源程序...

    Visual Basic 6编程技术大全 中译本扫描版带书签 2/2

    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...

Global site tag (gtag.js) - Google Analytics