`
SurpriseLee
  • 浏览: 7546 次
  • 性别: 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");	
	}

}


   
分享到:
评论

相关推荐

    网上很难找到的---利用串口传送文件实例

    8. **协议设计**:文件传输可能需要自定义简单的协议,以标识文件开始、结束、数据块以及错误处理等信息。例如,可以使用特定的起始和结束标志符,或者在数据块中包含长度信息。 9. **效率优化**:为了提高传输效率...

    两个VB程序间互相通信 可以传送数值 也可以传送字节数组

    在VB中,可以使用`MSWinsock`控件创建TCP或UDP套接字,传输二进制数据(如字节数组)或文本数据(数值可转换为字符串)。 5. **文件共享(File Sharing)** 简单的方法是通过读写文件来共享数据。一个程序写入文件,...

    聊天传送文件抓取屏幕

    标题中的“聊天传送文件抓取屏幕”是一个基于网络通信的项目,它允许用户在聊天过程中发送文件并捕获屏幕图像。这个项目展示了如何利用Socket编程技术实现这些高级功能,这在许多即时通讯软件中是非常常见的。 1. *...

    visual c++ vc在电脑间传送大文件.支持同时多文件传输,断点续传,传送过程中可聊天IM及时通信.zip

    2. 网络协议:设计一套自定义的文件传输协议,包含文件列表、文件分块、校验和等信息,确保数据完整性和传输可靠性。 3. 用户界面:创建友好的用户界面,显示文件传输进度、聊天窗口等,提供直观的操作体验。 4. ...

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

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

    通过IO流把文件传到前端并下载.zip

    文件流(FileInputStream和FileOutputStream)用于与本地文件系统交互,而字节流(如InputStream和OutputStream)和字符流(如Reader和Writer)则用于处理二进制和文本数据。在传输文件时,我们通常会使用...

    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 源码DEMO 4(1-4是独立压缩包)

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

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

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

    计算机应用基础-学习指南.doc

    【计算机应用基础学习指南】 1. 电子计算机的起源:世界上第一台电子计算机ENIAC是在1946年诞生的,它标志着计算机...28. Excel数据库排序:排序是基于单元格中的数据进行的,可以按照字母顺序或自定义顺序进行排序。

    2021-2022计算机二级等级考试试题及答案No.2757.docx

    `InputStream` 类和 `OutputStream` 类:主要用于处理字节流而非字符流,不适合文本文件的行处理。 - C. `FileReader` 类和 `FileWriter` 类:虽然也可以用来读写文本文件,但没有内置按行读写的方法,不如使用缓冲...

    浅谈Java的输入输出流

    而字符流主要用于文本数据的处理,Reader的常见子类有FileReader和BufferedReader,FileReader直接从文件读取字符,BufferedReader则增加了缓冲功能,提高了读取效率。Writer的子类包括FileWriter和BufferedWriter,...

    FlashFXP(免注册)

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

    UDP.rar_UDP_UDP server client_c# UDP

    由于没有文件传送功能,这个例子可能只关注基本的文本通信,对于更复杂的应用场景,如文件传输,可能需要自定义协议来处理数据的分块、校验和重传等问题。 总的来说,了解和掌握UDP协议以及如何使用C#进行UDP编程,...

    网络命令使用说明.doc

    - `-w:windowsize`:自定义传送缓冲区的大小,默认为4096字节。 FTP命令包括: - `!`:退出FTP子系统,返回到命令提示符。 - `?`或`help`:显示FTP命令帮助。 - `append`:追加文件到远程服务器,如`append local...

Global site tag (gtag.js) - Google Analytics