`
人生难得糊涂
  • 浏览: 117428 次
社区版块
存档分类
最新评论

基于UDP的可靠性高速传输(大文件)的实现(一)

 
阅读更多

TCP 协议和 UDP 协议是 TCP / I P协议栈中的两个最主要的传输层协议. 其中 TCP 提供可靠的、 有序的、 端到端的数据传输服务, 而 UDP则提供的是不可靠的、 不保证有序到达的、 端到端的数据传输服务. TCP 协议一般应用在数据传输量大或可靠性要求高的场合 (例如: 文件传输 ); UDP协议则一般是应用在数据传输量不大且可靠性要求不高的场合.
然而, 在实践中却会经常碰到这样的情况, 需要传输的数据不连续或数据量不确定, 但可靠性却要求很高. 例如这个项目: 一银行终端管理软件, 网内终端数量最少时 2 台, 最多时可达 150 台, 传输的数据具
有实时性, 最少时只需传输几个字节 ( 例如: 远程关机、 重启等简单指令 ) , 最多可达几 KB ( 例如: 远程设置
终端属性 ), 要求数据在传输过程中安全可靠. 在该系统中到底该采用哪种协议作为传输层协议呢? 采用
TCP 协议则会因为开辟的侦听线程过多而直接导致服务器的效率下降, 特别是该系统中的终端机根本就
没有多余的资源可以浪费; 如果采用 UDP协议, 虽然可以解决资源浪费问题, 但该协议是一种不可靠的没有差错处理的协议, 而该系统必须保证数据传输过程中的安全可靠. 因此针对这种情况应该放弃采用 TCP协议作为传输层协议的方案, 而是采用 UDP协议作为传输协议通过增加差错处理、 拥塞控制等措施来提高数据传输的安全性与可靠性.

以上文字摘自--周锦才的可靠 UDP 协议的设计思路与实现方法论文。

对于UDP的可靠性传输已经研究了几天,今天实现了大文件的可靠性传输的初步,在接下来的几天里,会把它慢慢实现。

好的,下面入正题。

我们要先定义可靠性UDP的数据报,这相当于一种协议,只不过是由我们自己定义的。

先说说整体思路:

对于大文件的UDP传输,在发送端,首先要发送一个开始发送数据包,在接受端接受到这个开始发送数据包以后,要发送一个开始发送确认数据包,当发送端在一定时间内(这个时间根据网络情况自己调整)收到了确认数据包以后,开始将一个大文件分成若干个组,每个组包含n个数据包,每个数据包长m。然后根据上面发送开始发送数据包相似的原理,进行发送(这一部分很复杂,具体实现我也还在研究,在后文中会详细解析,敬请关注)。

当发送端发送完到最后一个数据包时,向接收端发送一个发送完成包,在收到接收端发来的反馈后,结束发送。

在以下代码中,实现了开始数据包的收发实现。

发送端



import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Random;

import javax.xml.crypto.Data;


public class UdpSend {
	private int myPort;// 我方端口
	private int otherPort;// 对方端口
	private String otherIP;// 对方IP
	private DatagramSocket dataSocket;
	private final int GROUP_DATA = 256;// 一个组拥有的数据包个数
	private final int DATA_LEN = 1024;// 一个数据包的子节长度
	private final int GROUP_LEN = GROUP_DATA * DATA_LEN;// 一个组的字节长度
	/* 用于分组的数据 */
	private final int START_TIP[] = { 8, 0, 4 }; // 开始传输包(8) 标识(4)|次随机数(4)
	private final int STARTACK_TIP[] = { 8, 0, 4 }; // 开始确认包(8) 标识(4)|次随机数(4)
	private final int END_TIP[] = { 8, 0, 4 };// 结束传输包
	private final int ENDACK_TIP[] = { 8, 0, 4 };// 结束确认包
	// 分组标识
	private final String START_IDEN = "[]01";// 开始传输标识
	private final String STARTACK_IDEN = "[]02";// 开始确认标识
	private final String END_IDEN = "[]03";// 结束传输标识
	private final String ENDACK_IDEN = "[]04";// 结束确认标识

	/**
	 * 构造方法设置端口 Ip
	 * 
	 * @param myPort
	 * @param otherPort
	 * @param otherIP
	 */
	public UdpSend(int myPort, int otherPort, String otherIP) {
		this.myPort = myPort;
		this.otherPort = otherPort;
		this.otherIP = otherIP;
		try {
			dataSocket = new DatagramSocket(myPort);
			dataSocket.setReceiveBufferSize(GROUP_LEN);
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将数据拆成若干个组 ,每个组有GROUP_DATA(256)个数据包,每个数据包为DATA_LEN(1024)个字节长
	 * 
	 * @param totBuf
	 * @param totLen
	 */
	public void sendAll(byte[] totBuf, int totLen) {
		int groupNum = totLen / GROUP_LEN + (totLen % GROUP_LEN == 0 ? 0 : 1);// 分组个数
		byte tempBuf[] = new byte[GROUP_LEN];
		int numRandom = (new Random()).nextInt();
		// 发送开始传输包
		System.out.println("开始发送开始传输包");
		sendStart(numRandom);
		System.out.println("开始传输包发送完毕");
		// 拆分 
		/*
		for (int i = 0; i < groupNum; i++) {
			int groupLen;// 当前分组长度
			if (i == groupNum - 1) {
				groupLen = totLen % GROUP_LEN;
			} else {
				groupLen = GROUP_LEN;
			}
			for (int j = 0; j < groupLen; j++) {
				tempBuf[j] = totBuf[i * GROUP_LEN + j];
			}
			// 发送一个分组
			sendGroup(tempBuf, numRandom);
		}
		sendEnd(numRandom);
		*/
	}

	/**
	 * 发送开始传输包
	 * 
	 * @param numRandom
	 */
	void sendStart(int numRandom) {
		DatagramPacket sendPacket;
		DatagramPacket recePacket;
		byte[] sendBuf = new byte[START_TIP[0]];
		byte[] receBuf = new byte[STARTACK_TIP[0]];

		// 填充开始传输包
		fillStartData(sendBuf, numRandom);

		try {
			sendPacket = new DatagramPacket(sendBuf, sendBuf.length,
					InetAddress.getByName(otherIP), otherPort);
			recePacket = new DatagramPacket(receBuf, receBuf.length);
			// 设置接受超时时间
			dataSocket.setSoTimeout(1);
			boolean canSend = true;
			while (true) {
				try {
					if (canSend) {
						dataSocket.send(sendPacket);
					}
					dataSocket.receive(recePacket);
					// 只要收到了数据包 就不再发送开始数据包
					canSend = false;
					// 判断收到的数据包是否是开始确认包
					// 判断标识
					String checkStr = new String(receBuf, STARTACK_TIP[1],
							STARTACK_TIP[2]);
					if (!checkStr.equals(STARTACK_IDEN)) {
						continue;
					}
					// 判断随机数
					int checkNum = ByteChange.byteToInt(receBuf,
							STARTACK_TIP[2]);
					if (checkNum != numRandom) {
						continue;
					}
					// 能执行到这里说明接受到的是开始确认包 退出循环
					break;
				} catch (Exception e) {
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	

	/**
	 * 填充开始传输包
	 * 
	 * @param buf
	 *            开始传输包
	 * @param numRandom
	 *            次随机数
	 */
	void fillStartData(byte[] buf, int numRandom) {
		// 开始传输包组成 标识 次随机数
		// 填充标识
		ByteChange.stringToByte(START_IDEN, buf, START_TIP[1]);
		// 填充随机数
		ByteChange.intToByte(numRandom, buf, START_TIP[2]);
	}



	public static void main(String[] args) {
		UdpSend udpSend = new UdpSend(9090, 9191, "127.0.0.1");
		byte totBuf[] = "hello world!".getBytes();
		udpSend.sendAll(totBuf, totBuf.length);

	}

}

 

接受端:

 


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;



public class UdpRece {
	private int myPort;// 我方端口
	private int otherPort;// 对方端口
	private String otherIP;// 对方IP
	private DatagramSocket dataSocket;
	private final int GROUP_DATA = 256;// 一个组拥有的数据包个数
	private final int DATA_LEN = 1024;// 一个数据包的子节长度
	private final int GROUP_LEN = GROUP_DATA * DATA_LEN;// 一个组的字节长度
	/* 用于分组的数据 */
	private final int START_TIP[] = { 8, 0, 4 }; // 开始传输包(8) 标识(4)|次随机数(4)
	private final int STARTACK_TIP[] = { 8, 0, 4 }; // 开始确认包(8) 标识(4)|次随机数(4)
	private final int END_TIP[] = { 8, 0, 4 };// 结束传输包
	private final int ENDACK_TIP[] = { 8, 0, 4 };//结束确认包
	// 分组标识
	private final String START_IDEN = "[]01";// 开始传输标识
	private final String STARTACK_IDEN = "[]02";// 开始确认标识
	private final String END_IDEN = "[]03";// 结束传输标识
	private final String ENDACK_IDEN = "[]04";// 结束确认标识

	/**
	 * 构造方法设置端口 Ip
	 * 
	 * @param myPort
	 * @param otherPort
	 * @param otherIP
	 */
	public UdpRece(int myPort, int otherPort, String otherIP) {
		this.myPort = myPort;
		this.otherPort = otherPort;
		this.otherIP = otherIP;
		try {
			dataSocket = new DatagramSocket(myPort);
			dataSocket.setReceiveBufferSize(GROUP_LEN);
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}

	void receAll() {
		int numRandom = receStart();
		
	}

	/**
	 * 用于收取开始传输包,收取成功时会自动发送开始确认包
	 * 
	 * @return 收到的次随机数
	 */
	int receStart() {
		DatagramPacket recePacket;
		byte[] receBuf = new byte[START_TIP[0]];// 用于接受开始传输包
		try {
			recePacket = new DatagramPacket(receBuf, receBuf.length);
			// 接收超时时间为无穷大
			dataSocket.setSoTimeout(0);
			while (true) {
				dataSocket.receive(recePacket);
				// 检查标识
				String checkStr = new String(receBuf, START_TIP[1],
						START_TIP[2]);
				if (!checkStr.equals(START_IDEN)) {
					continue;
				}
				System.out.println("收到开始传输包");
				int numRandom = ByteChange.byteToInt(receBuf, START_TIP[2]);
				sendStartAck(numRandom);
				return numRandom;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	
	/**
	 * 用于发送开始确认包
	 * 
	 * @param numRandom
	 */
	void sendStartAck(int numRandom) {
		DatagramPacket sendPacket;
		byte[] sendBuf = new byte[STARTACK_TIP[0]];// 用于发送开始确认包
		try {
			sendPacket = new DatagramPacket(sendBuf, sendBuf.length,
					InetAddress.getByName(otherIP), otherPort);
			fillStartAckData(sendBuf, numRandom);
			dataSocket.send(sendPacket);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 填充开始确认包
	 * 
	 * @param buf
	 *            开始确认包
	 * @param numRandom
	 *            次随机数
	 */
	void fillStartAckData(byte[] buf, int numRandom) {
		// 开始传输包组成 标识 次随机数
		// 填充标识
		ByteChange.stringToByte(STARTACK_IDEN, buf, STARTACK_TIP[1]);
		// 填充随机数
		ByteChange.intToByte(numRandom, buf, STARTACK_TIP[2]);
	}
	

	public static void main(String[] args) {
		UdpRece udpRece = new UdpRece(9191, 9090, "127.0.0.1");
		udpRece.receAll();
	}
}

 

数据转换:

public class ByteChange {
	
	public static void stringToByte(String s, byte[] buf, int start) {
		byte tByteArr[] = s.getBytes();
		for (int i = 0; i < tByteArr.length; i++) {
			buf[start + i] = tByteArr[i];
		}
	}
	
	public static void intToByte(int num,byte[] buf ,int start)
	{
		for (int i = start; i < start +4;i++){
	            buf[i]=(byte)num;
	            num>>>=8;
	        }
	}
	 //从byte数组中提取int,使用小端模式
    public static int byteToInt(byte b[],int start){
        int num=0;
        for(int i=3+start;i>=start;i--){
            num=num<<8;
            num+=(0xff&(int)b[i]);
        }
        return num;
    }
}

 

1
0
分享到:
评论

相关推荐

    基于UDP大文件传输.rar

    标题"基于UDP大文件传输.rar"所指的,就是通过UDP协议来实现大文件的高效传输。大文件传输通常面临的问题包括数据包的丢失、乱序以及网络拥塞等问题。在使用UDP进行大文件传输时,我们需要解决这些挑战,确保文件...

    基于UDP协议的多文件传输

    为了解决UDP协议在可靠性方面的不足,本文提出了一种基于UDP协议的多文件传输设计方法,该方法在保证文件传输速度的同时,通过引入多种机制来增强传输的可靠性: 1. **数据流控制**:用于避免网络拥塞,确保数据的...

    基于UDP协议的多线程高速接收QT工程

    总之,这个"基于UDP协议的多线程高速接收QT工程"是一个结合了网络编程、多线程技术和可靠传输策略的实践案例,对于理解如何在高负载情况下保证数据传输的稳定性和效率具有很好的学习价值。开发者需要具备QT编程基础...

    delphi_UDP文件传输

    虽然UDP的不可靠性增加了开发的复杂性,但其高速传输和低延迟的特性使得它在某些场景下成为更好的选择,特别是对于大文件的实时传输和多播应用。然而,对于需要高度可靠性的文件传输,如金融交易记录,TCP可能是更...

    可靠传输UDP库SDK汇总

    UDX是另一个旨在改善UDP可靠性的库,它通过实现类似TCP的确认机制和重传策略,保证数据的完整性。UDX特别关注低延迟和高吞吐量,适合实时通信和游戏场景。UDX还考虑了网络拥塞的问题,采用了一种自适应的拥塞控制...

    基于udp的局域网聊天及文件传输程序

    标题中的“基于udp的局域网聊天及文件传输程序”是指使用UDP(User Datagram Protocol)协议在局域网内实现的通信应用。UDP是一种无连接的、不可靠的传输层协议,它不保证数据包的顺序和完整性,但因为其简单高效的...

    udt.sdk.3.3.tar.gz_TCP 滑动窗口_UDP 可靠_UDP 滑动窗口_udp 可靠传输_可靠 udp

    UDT(UDP-based Data Transport)是一种专为高速数据传输设计的协议,它基于用户数据报协议(UDP)但提供了类似于传输控制协议(TCP)的可靠性。这个“udt.sdk.3.3.tar.gz”压缩包文件包含了UDT的SDK版本3.3,它是一...

    udt 基于udp的可靠连接

    在实际应用中,UDT常被用于需要高速传输和高可靠性的场景,如大数据分析、远程协作工具、在线游戏、多媒体流媒体服务等。与TCP相比,UDT更适合处理大数据量、低延迟的要求,因为TCP的握手和确认过程可能会增加额外的...

    udt.sdk.2.3.tar.gz_TCP NAT_UDP 传输_udp 可靠传输_udt_传输 udp

    UDT(User Datagram Transport)是UDP(用户数据报协议)的一种增强版本,旨在提供类似于TCP(传输控制协议)的可靠性和性能,同时保留UDP的低延迟和高吞吐量特性。UDT SDK 2.3 是这个项目的开源版本,封装了实现...

    VC UDP协议的网络文件传输源码

    MFC是微软提供的一套C++类库,用于简化Windows应用程序开发,而UDP则是一种无连接的、不可靠的传输层协议,常用于需要高速传输但对数据完整性要求不高的应用。 首先,理解UDP协议的基本特性至关重要。UDP是一种无...

    基于UDP的局域网按键信息传输

    "基于UDP的局域网按键信息传输"是一个典型的应用实例,它利用了用户数据报协议(UDP)来实现在局域网内快速交换键盘事件。以下是关于这个主题的详细知识点: 1. UDP通信: UDP是一种无连接的传输层协议,它不建立...

    基于winsocket的文件传输源程序

    总的来说,这个基于Winsocket的文件传输源程序结合了网络编程和图形用户界面设计,实现了高效、可靠的文件交换功能。开发者可以通过学习和理解这个项目,提升在网络通信和MFC应用开发方面的技能。

    易语言源码易语言UDP文件传输.rar

    UDP是一种无连接的、不可靠的传输协议,常用于需要高速传输但可以容忍数据丢失的场景,如在线游戏和实时音视频流等。 易语言中的UDP文件传输涉及以下几个关键知识点: 1. **UDP套接字编程**:在易语言中,你需要...

    Ne Plus Ultra:基于UDP的文件传输客户端/服务器应用程序(基于lib UDT)-开源

    基于UDT协议-通过libudt-http://udt.sourceforge.net/ UDT是一种可靠的基于UDP的应用程序级数据传输协议。 UDT是为超高速网络设计的,已用于支持TB级数据集的全局数据传输。 Ne Plus Ultra的构建具有极低的依赖性...

    UDT协议UDP可靠数据传输协议.docx

    UDT,全称为User Datagram Transport,是一种基于UDP(用户数据报协议)的可靠数据传输协议,设计用于在高带宽时延乘积(Bandwidth-Delay Product, BDP)环境中提供高效、公平和稳定的传输服务。传统的TCP协议在面对...

    Upd 高速传输代码

    【标题】"Upd 高速传输代码"指的是一个基于UDP协议实现的高效文件传输程序,该程序由Delphi编程语言开发。UDP(User Datagram Protocol)是一种无连接的、不可靠的传输层协议,常用于对实时性要求较高的场景,如在线...

    基于LabVIEW FPGA的数据传输技术.pdf

    数据传输的质量直接影响到整个系统的性能,因此确保其稳定性和可靠性至关重要。 2. 基于LabVIEW FPGA的数据传输技术 LabVIEW是一种图形编程环境,广泛用于数据采集、仪器控制以及工业自动化等领域。FPGA(现场可...

    delphi开发的一个高速屏幕传输演示代(功能很强大)!!!

    通过深入研究这些文件,特别是源码部分,我们可以学习到如何使用Delphi进行网络编程,如何设计高效的屏幕捕获和压缩算法,以及如何优化程序以实现高速传输。对于想要提升Delphi编程技巧或对屏幕传输技术感兴趣的...

Global site tag (gtag.js) - Google Analytics