引言
本项目的目的是实现两个应用,经过网络链接在不一样的主机之间传输一个文件的功能。两个应用应该分别利用 UDP 和 TCP 协议,以具备传输至少 1 MB 文件的能力。
实现和说明
源代码
两个应用都由单个程序实现,源代码下载地址。
说明
程序使用如下命令行进行编译:
javac *.java
而后使用如下两个命令行运行:html
Receiver:
# java FileReceiver [protocol] [port]
Sender:
# java FileSender [protocol] [host] [port] [filename]java
其中 [protocol] 参数能够是 "udp" 或者 "tcp",但 sender 和 receiver 必须一致。
文件将会在 receiver 启动的目录下生成,默认指定名为 "Received-[filename]"。
TCP 实现
实现概述
在 TCP 实现中,Receiver 打开了一个 ServerSocket,并对定义好的端口进行监听。Sender 启动后将会为监听者 Receiver 打开一个新的 Socket,这致使了 socket 两端 InputStream 和 OutputStream 对象的建立。
一个包含了文件名和文件大小的初始信息将由 Sender 发送给 Receiver。这样 Receiver 可使用一个有意义的名字来存储接收到的文件,并能够判断何时文件彻底传输完毕。此信息并非必须的,当 Receiver 没法接收文件时中止 Sender 占用没必要要的带宽。
文件经过一个 FileInputstream 对象对它的读取进行传输,而后将数据写到一个 Socket 返回的 OutputStream 对象。为提升应用效率,每次读取和中继的数据是 8 kb,使用一个字节数组做为缓存。
TCP 使用经验
实践证实,TCP 文件传输是简单可靠的。程序的效率取决于使用的缓存大小,但传输的文件在全部执行的测试中都准确地被接收和保存。
UDP 实现
实现概述
UDP 文件传输的实现使用的是标准 Java datagram 类:DatagramPacket 和 DatagramSocket。
当 receiver 被执行时,它打开一个指定端口号的 socket 并等待,监听传入的数据包。sender 启动后,它打开一个链接到指定主机和端口的 socket,并传输包含有文件名以及将要传输文件大小等信息的单个 packet。当这个 package 发送之后,这个 socket 将等待并监听 package。
基于接收到的初始 package,receiver 为文件建立一 outputStream 对象,并给监听着的 sender 发送一个含有 "OK" 单词的 package。收到这个 "OK" 包之后,sender 开始读取文件内容,并将其经过 UDP 数据包发送,每次含有 512 字节的块。receiver 将这些块按照接收到的次序写入文件,并重复接收,直到接收到的字节达到它所指望数字。以后程序终止。
UDP 使用经验
UDP 是一种不可靠的传输连续数据的协议。这意味着传输过程当中会有丢包,并且接收到包的次序也是随机的。上面的例子并无解决文件传输中的这些问题。这意味着以 上应用在其每次运行时(所获得的文件)并非正确的和完整的。如下是关于两个常常发生的问题的缘由以及可行解决方案的描述。
若是在文件传输过程当中两个包接收顺序错误,而写入文件的顺序是按接收顺序来的。这将形成接收文件损坏。对于这种问题的解决方案是每次传输时定义一个序列号。这可让 Receiver 按照正确的顺序来存储这些包,无论它们到达的前后次序。
若是传输文件时出现丢包,Receiver 将不能收到它所指望数量的数据。在上面的示例中,这会致使 Receiver 继续运行,等待剩余的数据。对于这个问题一个可行的解决方案是,receiver 在给定时间跨度以后进行每次传输,调用超时。但为了使这次请求具备目的性,咱们要像上面说的那样为包扩展序列号。不然咱们没法接收到给定数量的数据,并局 限于请求文件的完整传输。
另外一个关于这两个问题的解决方案,在每次正确接收包以后再向 sender 发起接收请求。这个方法消除了丢包的可能性,但却会使传输异常缓慢。
结语
上面的实现让文件在主机之间传输变得可行。但若是使用的是 UDP 协议的话,咱们就没法保证文件的完整性和接收(顺序)的正确性。咱们对解决这些问题进行了大致说明,但具体在实际的文件传输中,对这些问题的最简单的解决方案就是,用 TCP 取代 UDP。数组