不知有没有现成好用的类,有的话还请告诉我一声,先谢谢了!
这个例子仅供学习参考。我原来觉得使用udp应该会比tcp快一些,但是既然要延时重发,也就是要保证连接可靠性;既然要可靠,不如干脆用tcp得了,tcp在传输层实现了滑动窗口和流量及拥塞控制,直接用serverSocket 编程简单一点,实现多线程下载也方便
一、滑动窗口,延时重发:我是这么实现的:其中用到了Thread.suspend()和resume()这些被Deprecated的方法,不过我用的时候倒是没出什么问题。
发送方Sender大概如下:实例域包括两个Thread和一个timer。代码因为和其他功能有些耦合,所以没有贴出来;这里只写个大概
public class Sender extends ThreadGroup{
private Thread t;//用来发送滑动窗口中的数据包
private Thread r;//用来监听接收方的响应从而移动滑动窗口
private javax.swing.Timer timer;//滑动窗口中的数据发送完后还没有接收到回应,就启动这个定时器,计时结束后从新发送滑动窗口中的数据
public Sender(){
t = new T(this,"t");
r = new R(this,"r");
timer = new Timer(2000,new ActionListener(){
public void run(){
t.resume();
}
}
}
public void join(){
t.join();
r.join();
}
private class T extends Thread{
public void run(){
while(!eof){
while(要发送的数据包在滑动窗口范围中){//滑动窗口只不过是两个指针指向窗口的头和尾
datagramSocket.send(datagramPakcet);
已发送的长度 += 数据包长度;
Thread.sleep(100);
}
timer.start();
suspend();
}
}
}
private class R extends Thread{
public void run(){
while(!eof){
datagramSocket.receive(datagramPacket);
接收到的packet中携带了接收方接收到了第几帧的信息,将移动窗口向前移动
timer.stop();
t.resume();
}
}
}
}
timer中还可以设置一下,比如重发5次没有应答就终止线程
接收方Receiver:实例域包括一个HashMap和一个Stack
接收时从stack中取出一个缓冲区用来接收,若接收到的这一帧是我要的下一针,就写入文件;否则先放入hashmap,等要的下一帧到了在从hashmap中取出,按顺序一起写入文件,写完后把缓冲区放会到stack中。每接收几帧后像发送方回应一下接收到哪里了
public Receiver extends Thread{
private HashMap map;
private Stack stack;
public void run(){
while(!eof){
byte buf = stack.pop();
datagramPacket.setData(buf);
datagramSocket.receive(datagramPacket);
packet数据头携带有第几帧的信息
if(该帧是在我的接收滑动窗口中){
map.put(id,datagramPacket.getData());
while((buf = map.remove(id++)) != null){
将buf写入文件;
stack.push(buf);
}
}
每接收5个数据包后向发送方回应一下;
}
}
}
二、断点续传
接收方接收文件时可以创建两个同名文件,一个后缀".aaa",一个后缀".bbb",.aaa的就是正在接收的文件,为接收完成后在把后缀aaa去掉;.bbb的文件用来记录信息,接收过程中不断把接收到了第几个帧写入.bbb文件中。这样即使接收到一半程序终止,下一次只要读取这个文件就能知道上次接收到哪里,向发送方发出请求时让它从这里开始发送就行了
三、发送文件夹
不知为什么网络上很少见能发送整个文件夹的,只能打包在发送。
其实发送文件夹很简单:
public void sendDirctory(File dir){
if(dir.isFile()){
Sender sender = new Sender(dir);
sender.start();
sender.join();//用join,使发送完这个文件在发送下一个
}
else{
File[] list = dir.list();
for(int i=0;i
sendDirectory(list[i]);
}
}
}
四、ByteBuffer类: ByteBuffer 可以对字节数组进行操作,可以从字节数组中读出和写入int,byte,long ,short等数据类型,和FileChannel配合使用挺方便的,就是要注意其中position 和 limit 指针的移动。 ByteBuffer 对读写数据包的包头信息很方便,byteBuffer.array()方法能转化成数组,然后用datagramPacket发送出去。