Java小强个人技术博客站点    手机版
当前位置: 首页 >> NET >> UDP 上传文件

UDP 上传文件

124730 NET | 2014-11-11

目前我们的客户端主要是手机,那么手机程序的有问题时,需要一些少量的调试信息上传到服务器。

之前是用http流操作的,但是发现这样非常耗费服务器连接资源,因为手机不一定使用了wife,在使用2G信号或者信号不稳定时,这样的操作对服务器来说压力不小。

因为我做过网络编程,希望通过使用udp的方式来做,因为udp适用于信号不稳定的场景。

 

那么下面就简单定义下协议:

消息头有三个字段,全都是整形,这样共12个字节,剩下的是内容部分。

第一字段是包长度,通过该字段来验证包是否完整,更严格的是要CRC验证的,我暂时先不说这部分

第二个字段是文件索引,因为文件不是一次性传递完毕的,是一节一节传递的,所以必须告诉服务器你传递的是那部分

第三个字段是文件名,这里要说一下,因为我们就是拿一个主键来存储的,所以用的整形。

 

服务器处理消息后返回1,服务器是单线程的,每个包的内容长度是2048Byte。

那么看下服务端代码:

package com.dlwx.net;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.dlwx.util.StreamTool;
/**
 * UDP服务类
 * @author java小强
 */
public class LogFileSer {
 private static int sendLen = 2048;
 private byte[] buffer = new byte[sendLen + 4 + 4 + 4]; // 内容 + 长度 + 索引 + 名称
 private static DatagramSocket ds = null;
 private DatagramPacket packet = null;
 private InetSocketAddress socketAddress = null;
 private static String filePath = "C:\\";
 public static void main(String[] args) throws Exception {
  String serverHost = "127.0.0.1";
  int serverPort = 3344;
  LogFileSer udpServerSocket = new LogFileSer(serverHost, serverPort);
  while (true) {
   byte[] re = udpServerSocket.receive();
   System.out.println("收到(" + re.length + "):" + Arrays.toString(re));
   if(null != re && re.length > 13){
    byte[] btTemp = new byte[4];
    System.arraycopy(re, 0, btTemp, 0, 4); // 数组源,数组源拷贝的开始位子,目标,目标填写的开始位子,拷贝的长度
    int len = StreamTool.bytesToInt(btTemp);
    if(len == re.length){ // 标记的长度是否正确
     btTemp = new byte[4];
     System.arraycopy(re, 4, btTemp, 0, 4);
     int index = StreamTool.bytesToInt(btTemp);
     if(index >= 0){ // 标记的索引正确
      btTemp = new byte[4];
      System.arraycopy(re, 8, btTemp, 0, 4);
      Integer name = StreamTool.bytesToInt(btTemp);
      String nameStr = name.toString();
      File file = new File(filePath + nameStr + ".txt");
      if(!file.exists()) file.createNewFile(); // 不存在就创建新文件
      RandomAccessFile fdf = new RandomAccessFile(filePath + nameStr + ".txt", "rw");
      fdf.seek(index * sendLen); // 跳过索引部分
      byte[] btFile = new byte[re.length - 12];
      System.arraycopy(re, 12, btFile, 0, re.length - 12);
      fdf.write(btFile);
      
      ByteBuffer bf = ByteBuffer.allocate(16);
      bf.put(StreamTool.intToByte(16));    // 总长度
      bf.put(StreamTool.intToByte(index)); // 索引
      bf.put(StreamTool.intToByte(name));  // 名称
      bf.put(StreamTool.intToByte(1));     // 成功
      udpServerSocket.response(bf.array());
     }
    }
   }
  }  
 }
 /**
  * 构造函数,绑定主机和端口
  */
 public LogFileSer(String host, int port) throws Exception {
  socketAddress = new InetSocketAddress(host, port);
  ds = new DatagramSocket(socketAddress);
  System.out.println("服务端启动!");
 }
 /**
  * 接收数据包,该方法会造成线程阻塞
  */
 public final byte[] receive() throws IOException {
  packet = new DatagramPacket(buffer, buffer.length);
  ds.receive(packet);
  byte[] re = new byte[packet.getLength()];
  System.arraycopy(packet.getData(), 0, re, 0, packet.getLength());
  return re;
 }
 /**
  * 将响应包发送给请求端
  */
 public final void response(byte[] info) throws IOException {
  System.out.println("客户端地址 : " + packet.getAddress().getHostAddress() + ",端口:" + packet.getPort());
  DatagramPacket dp = new DatagramPacket(buffer, buffer.length, packet.getAddress(), packet.getPort());
  dp.setData(info);
  ds.send(dp);
 }
}

客户端是比较麻烦的,要分包发送,要多次发送。

所以在程序中有两个循环,一个是分包,一个多次发送部分,如果一个包多次发发送失败,那么就整体失败,关闭连接。

package com.dlwx.net;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import com.dlwx.util.StreamTool;
/**
 * UDP客户端程序,用于对服务端发送数据,并接收服务端的回应信息
 * @author java小强
 */
public class LogFileClient {
 private static int sendLen = 2048;
 private byte[] buffer = new byte[sendLen + 4 + 4 + 4]; // 内容 + 长度 + 索引 + 名称
 private static DatagramSocket ds = null;
 private static int name = 123456; // 订单号
 /**
  * 测试客户端发包和接收回应信息的方法
  */
 public static void main(String[] args) throws Exception {
  LogFileClient client = new LogFileClient();
  String serverHost = "127.0.0.1";
  int serverPort = 3344;
  FileInputStream fi = new FileInputStream("C:\\log.txt");
  byte[] fbt = StreamTool.inputStreamToByte(fi);
  if(null != fbt && fbt.length > 0){
   int pk = fbt.length / sendLen; // 需要发多少包
   if(fbt.length % sendLen > 0) pk++;
   if(pk == 1){ // 只有一个包
    ByteBuffer bf = ByteBuffer.allocate(fbt.length + 12);
    bf.put(StreamTool.intToByte(fbt.length + 12)); // 总长度
    bf.put(StreamTool.intToByte(0));               // 索引
    bf.put(StreamTool.intToByte(name));            // 名称
    bf.put(fbt);
    client.send(serverHost, serverPort, bf.array());
    byte[] bt = client.receive();
    System.out.println("服务端回应数据:" + new String(bt));
   }else{
    A:for(int i = 0;i < pk;i++){
     int len = sendLen;
     if(i == pk - 1 && fbt.length % sendLen > 0){ // 最后一个包,且不满足一个整包
      len = fbt.length % sendLen;
     }
     
     byte[] sd = new byte[len];
     System.arraycopy(fbt, i * sendLen, sd, 0, len);  // 数组源,数组源拷贝的开始位子,目标,目标填写的开始位子,拷贝的长度
     
     ByteBuffer bf = ByteBuffer.allocate(len + 12);
     bf.put(StreamTool.intToByte(len + 12)); // 总长度
     bf.put(StreamTool.intToByte(i));        // 索引
     bf.put(StreamTool.intToByte(name));     // 名称
     bf.put(sd);
     byte[] bySd = bf.array();
     
     B:for(int t=0;t<5;t++){
      client.send(serverHost, serverPort, bySd);
      try {
       byte[] bt = client.receive();
       if(null != bt && bt.length == 16){
        break B; // 继续发送
       }
      } catch (Exception e) {
      }finally{
       if(t==4){ // 发送5次不成功
        break A;
       }
      }
     }
    }
   }
  }
  ds.close(); // 关闭连接
 }
 /**
  * 构造函数,创建UDP客户端
  */
 public LogFileClient() throws Exception {
  ds = new DatagramSocket(8899); // 邦定本地端口作为客户端
 }
 /**
  * 向指定的服务端发送数据信息
  */
 public final void send(final String host, final int port, final byte[] bytes) throws IOException {
  DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(host), port);
  ds.send(dp);
 }
 /**
  * 接收从指定的服务端发回的数据
  */
 public final byte[] receive() throws Exception {
  DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
  ds.setSoTimeout(5000); // 超时时间5秒钟
  ds.receive(dp);  
  byte[] data = new byte[dp.getLength()];
  System.arraycopy(dp.getData(), 0, data, 0, dp.getLength());  
  return data;
 }
}

这里我的超时时间是5秒钟,尝试5次。

当然这里只是功能代码,具体完善逻辑需要根据测试结果和业务需要来修正。

推荐您阅读更多有关于“ 服务器 tcp 客户端 资源 UDP 上传 文件 断点 ”的文章

上一篇:HTTP Referer与网站流量来路统计 下一篇:SNMP 在Windows上建一个SNMP服务并获得该机器的机器名

猜你喜欢

发表评论: