`

基于自定义通信协议的文件传输

 
阅读更多

        要实现通信,首先我们需要一个服务器,然后需要多个客户机。主要方法是通过java.net 包下面的API。创建一个服务器ServerSocket,然后用Socket创建多个客户机。

          首先,我们的目地是创建一个简单服务器,能将客户机发来的字符串显示出来,并且再回送给客户
机——有必要解释一下:在这里,服务器指的是等待别人来连接的机器;客户机,当然就指的是主
动去连接别人的机器了,这就像打电话过程中的主叫与被叫的区分一样,一旦连结成功,就不存在
这样谁是客户机谁是服务器的区分了。

        

package HelloQQxy_0302;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 简单服务器的实现
 * @author 
 *
 */
public class ChatServer {
/**
 * 在指定的端口上启动一个服务器
 */
	private void setUpServer(int port){
		try{
			//建立绑定在指定端口上的服务器对象
			ServerSocket server=new ServerSocket(port);
			
			System.out.println("服务器创建成功!"+port);
			while(true){
			//让服务器进入等待状态:阻塞状态
			Socket client=server.accept();
			//从连接对象上得到输入输出流对象
			OutputStream out=client.getOutputStream();
			InputStream ins=client.getInputStream();
			String s="hello,welcome to our chatroom\r\n";
			
			byte[] data=s.getBytes();//取得组成这个字符串的字节
			out.write(data);//用输出对象发送数据!
			out.flush();//强制输出
			int in=0;//一个一个字节读取客户机的输入函数
			while(in!=13){//如果读到的不是13,即回车字符
				in=ins.read();
				System.out.println("读到是:"+in);
			}
			System.out.println("客户机按了回车,退出:"+in);
			client.close();//关闭与客户机的连接
			}
		}
		catch(Exception ef){
			ef.printStackTrace();
		}
	}
	
	//主函数
	public static void main(String[] args){
		ChatServer cs=new ChatServer();
		cs.setUpServer(1000);
	}
}

 然后在 cmd 下输入telnet localhost 1000 这里的telnet 是连接的意思,localhost是本地的IP地址 1000是计算机的端口号,这样就实现了通信之间的连接。

<!--StartFragment -->


 然后就是,传文件。我们组自定义文件的写入格式为
  dous.writeInt(type);//文件类型
  dous.writeInt(size);//文件名长度
  
  int filesize = ins.available();//输入流取出文件的长度
  byte[] filedatalen=new byte[filesize];
  ins.read(filedatalen);
 dous.writeInt(filesize);//文件总长度
  dous.write(filename);//文件名
  dous.write(filedatalen);//文件数据组
总的来说我们只要记住一点how to write ,how to read就行
下面是具体代码
 
package ChatServer0713_xy;

import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 简单服务器实现
 * 1.根据协议,接受解析文本聊天消息
 * 2.根据协议,接受解析文件传送消息
 * @author 
 *
 */
public class ChatServer {
	//启动主函数
	public static void main(String[] args){
		ChatServer cs=new ChatServer();
		cs.setUpSever(8080);
	}
/**
 * 在指定端口上启动一个服务器
 * @param port:服务器所用的端口
 */
	private void setUpSever(int port) {
		try {
			
			//1.建立绑定在指定端口上的服务器对象
			ServerSocket server=new ServerSocket(port);
			System.out.println("服务器创建成功!"+port);
			//2.让服务器进入等待状态:阻塞状态
			//3.当有客户机连接上来时,等待方法就会返回,返回一个代表与客户机连接的对象
			while(true){//让服务器进入循环等待状态
				Socket client=server.accept();
				System.out.println("Incoming client"
						+client.getRemoteSocketAddress());
				//调用处理连接对象的方法去处理连接
				processChat(client);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	//处理客户机进入的连接对象
private void processChat(Socket client) {
            try {
            	System.out.println("hi");
            	//得到一个输入、输出流对象
				OutputStream	out=client.getOutputStream();
				InputStream ins=client.getInputStream();
				//将输入流包装为DataInputStream方便读取原始类型的流
				DataInputStream dins=new DataInputStream(ins);
				while(true){
					//开始读取数据:每一条消息,总是以一个int开头
					//1.读取消息长度,readInt()方法底层从流中读取4个字节,组成一个int
					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){//类型为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){
						//文件数据包体解析
						System.out.println("发给文本给:"+destNum);
						byte[] data=new byte[256];
						dins.readFully(data);//读取256个字节作为文件名字
						//解析出文件名字,并除去末尾空格
						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();
						System.out.println("文件保存完成!");
					}else{
						System.out.println("收到未知数据包:"+flag);
						client.close();
					}
				}
            } catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
}
}
 下面是客户机的代码
package ChatServer0713_xy;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * 简单客户端实现
 * 1.根据协议,发送文本聊天消息
 * 2.根据协议,发送文件传送消息
 * @author 
 *
 */
public class ChatClient {
private DataOutputStream dous;//输入流对象
/**
 * 向流中写入定长字节,如果不足,补二进制0
 * @param out:要写入的流
 * @param str字符串
 * @param len要写入的长度
 */

private void writeString(DataOutputStream out,String str,int len){
	
	try {
		byte[] data=str.getBytes();
		out.write(data);
		//假设都是短,需要补0
		while(len>data.length){
			out.writeByte('\0');//补二进制0
			len--;
		}
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
/**
 * 发送一条文本消息
 * @param msg:消息内容
 * @param destNum:接受者号码
 * 打包过程
 */
private void sendTextMsg(String msg,int destNum){
	//System.out.println("1");
	try {
	//	System.out.println("2");
		byte[] strb=msg.getBytes();//得到消息的字节数
		int totalLen=4+1+4+strb.length;
		System.out.println("发送总长度为:"+totalLen);
		dous.writeInt(totalLen);//总长
		dous.writeByte(1);//类型:1为文本消息
		dous.writeInt(destNum);//用户号
		dous.write(strb);//写入消息内容
		dous.flush();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}//总长
}
/**
 * 发送一个文件数据包
 * @param fileName:文件的绝对路径名
 * @param destNum:用户号
 * @throws IOException 
 */
private void sendFileMsg(String fileName,int destNum) throws IOException{
	
	
		//根据文件名创建文件对象
		File file=new File(fileName);
		//根据文件对象,构造一个输入流
		InputStream ins=new FileInputStream(file);
		
		int type=2;
		dous.writeInt(type);
		
		byte[] filename = file.getName().getBytes();
		int size=filename.length;
		dous.writeInt(size);
		
		int filesize = ins.available();
		byte[] filedatalen=new byte[filesize];
		ins.read(filedatalen);
		dous.writeInt(filesize);
		dous.write(filename);
		dous.write(filedatalen);
		
		
		dous.flush();
		
		
		/**
		 * int fileDataLen=ins.available();//文件数据总长
		int totalLen=4+1+4+256+fileDataLen;
		dous.writeInt(totalLen);
		dous.writeByte(2);//类型是2,即文件数据包
		//写入目标用户号
		dous.writeInt(destNum);
		//文件名:得到文件的短名字
		String shortFileName=file.getName();
		writeString(dous,shortFileName,256);
		//写入文件名,不足256个长度时,补\0
		 * byte[] fileData=new byte[fileDataLen];
		ins.read(fileData);
		
		dous.write(fileData);//写出到服务器的流
		 */
	}
/**
 * 连接上服务器
 * @param ip:服务器ip
 * @param port:服务器端口
 */
public void conn2Server(String ip, int port){
	// 创建一个到服务器端的Socket对象
	try {
	Socket client = new Socket(ip, port);
	// 得到输入输出流对象
	InputStream ins = client.getInputStream();
	OutputStream ous = client.getOutputStream();
	//将输出流包装为DataOutputStream对象
	dous=new DataOutputStream(ous);
	
	int testCount=0;
	while(true){
		System.out.println("登录服务器成功,请选择你要发的类型(1:聊天 2:文件:");
		//让用户从命令行输入要发送的文件的名字
		Scanner sc=new Scanner(System.in);
		int type=sc.nextInt();
	
		if(type==1){//发文本
			sendTextMsg("abc聊天类容"+testCount,8080);
		}
		if(type==2){//发文件,祝这个文件必须存在
			sendFileMsg("F:\\a.txt",8080);
		}
		testCount++;
		}
	}
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
public static void main(String[] args) {
	ChatClient qqc = new ChatClient();
	qqc.conn2Server("localhost", 8080);
	//qqc.conn2Server("192.168.0.185", 8080);
	}
}
 其次,我们要注意
第一 ,将 InputStream 包装为DataInputStream 流对象 : 
InputStream ins = client.getInputStream();
//将输入流包装为DataInputStream方便读取原始类型的流
DataInputStream dins=new DataInputStream(ins);
 如上代码中将从Socket上得到的输入流包装成为DataInputStream流对象,随后如果调用dins的readByte()时,只会从底层的数据流中读取一个字节返回:而调用readInt()时,方法内部经过位运算  实现了将读到的  4个字节 组成一个 int型数据返回。
所以读取数据,读几个字节,是什么类型,必须按照与客户端所准守的协议执行,如果多读或少读,都会导致通信出错。
第二read()方法与readFully()方法的区别:
 
byte[] data=new byte[totalLen-4-1-4];
//从流中读取data.length个字节放入数组中
dins.readFully(data);
 此处从流中读取字节,填充字节数组时,没有使用read(要填充的数组)方法,而是调用readFully()方法,这样做更为安全。网路通信中,当发送大的数据量时,有这样一种可能:一部分数据已发送到对方,有一部分数据还在本地的网卡缓存中,如果调用read()方法,可能会提前返回而没有读到足够的数据,在传大块数据(如一次传送一个较大文件时)可能出错,而readfully()方法会一直等待,读取到数组长度的所有数据,才会返回。
  • 大小: 40.5 KB
4
0
分享到:
评论

相关推荐

    自定义串口文件传输通信协议

    ### 自定义串口文件传输通信协议 #### 一、引言 随着计算机技术的发展,串行通信作为一种简单有效的数据传输方式被广泛应用。特别是在工业控制、远程监控等领域,串行通信接口(例如RS232)成为了重要的通信手段之...

    C实现基于Socket实现自定义协议通信

    本教程主要聚焦于如何使用C语言实现基于Socket的自定义协议通信,涵盖了客户端和服务端的实现。我们将深入理解Socket编程的基本概念,学习如何创建、连接、监听Socket,并探讨如何设计和实现自定义的数据传输协议。 ...

    TCP自定义通讯协议参考

    TCP自定义通讯协议是一种基于TCP协议的通信框架,它利用非阻塞异步通信机制,实现了客户端和服务端之间的双向信息交换。以下是对该协议的详细解释: 1. 机制: - TCP协议基础:TCP(Transmission Control Protocol...

    基于STM32F103ZET6的自定义通信协议电梯监控系统设计.pdf

    总结来说,文档描述了一种以STM32F103ZET6为核心的电梯监控系统的设计方案,该方案通过自定义通信协议、串口通信、云计算等技术手段,实现了电梯运行状态的实时监控,并能够将数据传输至云端进行存储和分析,最终向...

    Mina自定义协议通信的示例

    本示例聚焦于Mina中的自定义协议通信,这允许开发人员根据特定需求设计通信协议,而不是局限于预设的如TCP或UDP等标准协议。 首先,我们要理解Mina的基本工作原理。Mina提供了一个事件驱动的异步I/O模型,使得处理...

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

    在IT行业中,自定义字节协议是一种常见的通信方式,它允许开发者根据特定需求设计数据传输格式,以提高效率、灵活性和兼容性。本主题聚焦于如何利用自定义字节协议来实现文本消息和文件消息的传送。下面我们将深入...

    java socket通信自定义消息协议

    自定义消息协议是在标准TCP/IP协议之上,根据具体需求设计的一种数据传输格式。本篇将详细讲解如何在Java中实现基于Socket的自定义消息协议,以及客户端与服务器端的数据转换与解析过程。 首先,我们来看`Socket...

    基于RS485通信协议的实例

    在RS485网络中,通信协议通常是自定义的,但常见的是MODBUS RTU协议。MODBUS RTU是一种简单而广泛使用的通信协议,适用于各种工业设备,如PLC、变频器等。它基于ASCII码的二进制表示,以起始位、数据位、奇偶校验位...

    计算机网络课程设计 基于TCP协议的文件传输简单实现

    而"FTP"可能是指FTP(File Transfer Protocol),这是一个基于TCP协议的文件传输协议,虽然我们这里讨论的是自定义的简单实现,但FTP是一个很好的参考标准,因为它已经处理了许多文件传输中的复杂问题,如断点续传、...

    串口助手代码(可自定义协议)

    本文将详细介绍一款基于C#语言编写的串口助手程序,该程序的一大特色在于允许用户自定义协议帧结构,从而满足各种不同通信协议的需求。 首先,我们来理解“串口助手”的基本功能。串口助手是一种用于测试和调试串行...

    Winsock_file_transform.rar_B/S_winsock file_文件传输_自定义协议

    综上所述,"Winsock_file_transform.rar"提供了一个基于B/S架构的自定义协议文件传输示例,对于理解网络编程、Winsock接口以及自定义协议的设计和实现具有很好的学习价值。开发者可以通过阅读和分析源代码,深入掌握...

    自定义串口数据通信协议的分析与设计

    特别是在需要进行实时数据交换的情况下,例如双机短信聊天或文件传输等场景,自定义串口数据通信协议能够提供更加灵活和高效的数据传输方案。本文主要介绍了基于微软公司的MSComm串口通信控件建立的一个简单串口通信...

    基于TCPUDP的文件传输

    本项目“基于TCP/UDP的文件传输”着重探讨了如何利用这两种协议进行文件的可靠传输,特别是在实现UDP的可靠性方面。 TCP是一种面向连接的协议,它提供了可靠的、基于字节流的数据传输服务。TCP通过三次握手建立连接...

    socket通信 文件传输等的功能

    在文件传输方面,Socket通信通常用于实现自定义的文件传输协议,例如FTP(文件传输协议)和HTTP(超文本传输协议)的底层就是基于Socket实现的。通过Socket,我们可以构建一个客户端-服务器模型,其中客户端发起请求...

    C#作为上位机,控制51单片机(下位机),串口通信源程序(附效果图)

    总结,C#作为上位机与51单片机的串口通信是一个综合了硬件接口、网络协议和软件设计的实践过程。通过C#的`SerialPort`类,我们可以轻松实现与51单片机的双向通信,从而实现各种控制任务。在实际开发中,需要关注通信...

    基于android的无线文件传输

    综上所述,基于Android的无线文件传输技术涉及了多个方面的知识,包括网络通信、多线程编程、UI设计、资源管理、权限控制、数据安全等。这个项目提供了学习和实践这些技术的机会,同时也可以根据需要自定义界面和...

    C#使用SuperSocket实现自定义协议实现CS架构服务器和客户端程序设计).zip

    4. **自定义协议**:SuperSocket允许开发者定义自己的通信协议,以满足特定的应用需求。这意味着服务器和客户端之间的数据交换格式可以按照项目需求定制,如使用特定的报文头、编码方式或数据结构。 5. **项目工程*...

    基于可靠协议的串口文件传输软件

    总的来说,这款基于可靠协议的串口文件传输软件提供了一种安全、可靠的文件交换解决方案,尤其适合于那些需要在不支持网络通信或网络不稳定的情况下,利用串口进行数据交换的环境。它的灵活性、用户友好性和可靠性使...

    基于C#的局域网文件传输软件

    在局域网环境中,文件传输通常涉及到TCP/IP协议族中的Socket编程,用于建立客户端和服务器之间的通信链路。C#提供了System.Net命名空间,其中的Socket类可以方便地进行网络编程。文件传输可能采用FTP(文件传输协议...

    基于C++ 使用 UDP Socket 实现可靠文件传送(构建简单的文件传输协议)【100013164】

    通过以上步骤,我们可以构建一个简单的基于UDP的文件传输协议,尽管它的复杂性和开销可能会高于直接使用TCP,但这种方式提供了更多的灵活性,适用于某些对实时性要求高且可以容忍一定数据丢失的场景。理解并熟练掌握...

Global site tag (gtag.js) - Google Analytics