www520507
2009-08-25 23:03 阅读 336
已采纳

精通Socket的朋友请进~~~紧急求援啊!

我用Socket进行远程文件传输,但是在循环时总是差最后一点点就不传了,真郁闷!下面代码稍作配置就可以运行的。在readme.txt中说明的很详细。请各位帮我看看哦!
我的悬赏分没有了,真不好意思啊!还请各位不吝赐教啊!

[b]问题补充:[/b]
我改小了每次写入的大小,也没有用啊!

[b]问题补充:[/b]
在Eclipse中可以设置字符编码的。MyEclipse应该也有吧?不过我没有过MyEclipse啊!不好意思,麻烦各位了啊

[b]问题补充:[/b]
我在处理Socket端对数据的接收做了一个方法,具体在[code="java" color=#FF0000]// 得到去除头信息的字节流
readInfo = SoftwareProcessUtil.getInputStreamByte(in,
byteSize);[/code] 具体引用在SoftwareProcessUtil.java 中的 [code="java"]/**
*
* getInputStreamByte decription : 从Socket inputStream中得到去除头信息的字节数组
*
* @param in
* @param byteSize
* 需要读取的包含头信息的字节流的长度
* @return
*/
public static byte[] getInputStreamByte(InputStream input, Integer byteSize) {

    // 设置已经读取数从去头信息开始
    int readed = 0; // 已经读取数
    // 头信息长度
    int headSize = HTTP_HEAD_GET_INFO.getBytes().length;
    // 初始化需要返回的数组
    byte[] result = new byte[byteSize - headSize];

    try {
        int available = 0; // 可读数
        if (available > (byteSize - readed)) {
            available = byteSize - readed;
        }
        while (readed < byteSize) {
            while (available == 0) {
                // 等到有数据可读
                available = input.available(); // 可读数
            }
            if (available > (byteSize - readed)) {
                available = byteSize - readed; // 剩余数
            }
            if (available > SOCKET_INPUT_ARRAY_LENGTH) {
                available = SOCKET_INPUT_ARRAY_LENGTH; // size-readed--剩余数
            }
            byte[] buffer = new byte[available];
            int reading = input.read(buffer);
            for (int n = 0; n < reading; n++) {
                if ((readed + n) < headSize) {
                    continue;
                }
                result[readed + n - headSize] = buffer[n];
            }
            readed += reading; // 已读字符
        }
    } catch (IOException e) {
        System.out.println("Read readLenData Error!");
    }
    System.out.println("SoftwareProcessUtil result length = "+ result.length);
    return result;
}[/code]

[b]问题补充:[/b]
我在这块代码中的 public static final int BLOCK_SIZE = 1024; 只是1024个byte啊,并没有1024K啊
[b]问题补充:[/b]
我试过512个字节了,没有用,我在SoftwareProcessUtil.java中的public static byte[] getInputStreamByte(InputStream input, Integer byteSize)方法中修改了一下,修改后的代码如下:面的附件。我现在问题是:最后一次读取服务器的传输的数据流时,他总是有最后一个字节读不到,所有在Client端就会死在getInputStreamByte方法中的[code="java"]while (available == 0) {
// 等到有数据可读
available = input.available(); // 可读数
}[/code]这儿。郁闷死了
[b]问题补充:[/b]
我在SoftwareProcessUtil.java 中做了一点点修改。代码如下:[code="java"]package edu.sjtu.infosec.ismp.manager.virus.software.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.sjtu.infosec.ismp.manager.virus.software.exception.SoftwareManagerException;

/**

  • @Title: SoftwareProcessUtil.java
  • @Package edu.sjtu.infosec.ismp.manager.virus.software.util
  • @Description: TODO
  • @author wjianzhuo
  • @date 2009-8-24 下午10:52:50
  • @version V1.0
    / /*
  • @ClassName: SoftwareProcessUtil
  • @Description: TODO
  • @author wjianzhuo
  • @date 2009-8-24 下午10:52:50
  • */
    public class SoftwareProcessUtil {

    public static void main(String[] args) {
    System.out.println(splitString("CXDOEER LENGTH:324 ", "LENGTH"));
    System.out.println(HTTP_HEAD_GET_INFO.getBytes().length);
    }

    /**

    • HTTP Get 头信息(14byte) / public static final String HTTP_HEAD_GET_INFO = "Get HTTP/1.1 "; /*
    • CLOSE SOCKET HTTP Post头信息 */ public static final String HTTP_HEAD_POST_INFO = "Post HTTP/1.1 ";

    /**

    • CLOSE SOCKET HTTP Get头信息 */ public static final String HTTP_HEAD_CLOSE_SOCKET = "CLOSE SOCKET";

    /**

    • CONTINUE FOR HTTP Get头信息 / public static final String HTTP_HEAD_CONTINUE_FOR = "CONTINUE FOR"; /*
    • BREAK FOR HTTP Get头信息 */ public static final String HTTP_HEAD_BREAK_FOR = "BREAK FOR";

    /**

    • Server Socket端的通信阻塞时间 */ public static final int SERVER_SOCKET_TIME_OUT = 3 * 60 * 1000;

    /**

    • Client Socket端的通信阻塞时间 */ public static final int CLIENT_SOCKET_TIME_OUT = 3 * 60 * 1000;

    /**

    • 对文件进行分割的每块的大小 : 30K */ public static final int BLOCK_SIZE = 30*1024;

    /**

    • Socket inputStream 每次文件流传输的数组长度 */ public static final int SOCKET_INPUT_ARRAY_LENGTH = 1024 + HTTP_HEAD_GET_INFO.getBytes().length;

    /**

    • spliceHttpHeadInfo decription : 拼接请求的HTTP头信息 :Get HTTP/1.1
    • @return */ public static byte[] spliceRequestHttpHeadInfo() { return addByte(HTTP_HEAD_GET_INFO); }

    /**

    • spliceDownloadLengthHeadInfo decription : 拼接下次下载长度的响应头信息
    • @param size
    • @return
      */
      public static byte[] spliceDownloadLengthHeadInfo(int size) {

      return addByte(HTTP_HEAD_GET_INFO + "LENGTH:" + size);
      }

    /**

    • spliceDownloadNameHeadInfo decription : 拼接下载文件名的请求头信息
    • @param downloadSize
    • @return */ public static byte[] spliceDownloadNameHeadInfo(String downloadName) { return addByte(HTTP_HEAD_GET_INFO + "NAME:" + downloadName); }

    /**

    • spliceCloseSocketHeadInfo decription : 拼接关闭Socket的HTTP请求头信息
    • @return */ public static byte[] spliceCloseSocketHeadInfo() { return addByte(HTTP_HEAD_GET_INFO + HTTP_HEAD_CLOSE_SOCKET); }

    /**

    • spliceContinueForHeadInfo decription : 拼接跳出循环的HTTP请求头信息
    • @return */ public static byte[] spliceContinueForHeadInfo() { return addByte(HTTP_HEAD_GET_INFO + HTTP_HEAD_CONTINUE_FOR); }

    /**

    • spliceBreakForHeadInfo decription : 拼接终止循环的HTTP请求头信息
    • @return */ public static byte[] spliceBreakForHeadInfo() { return addByte(HTTP_HEAD_GET_INFO + HTTP_HEAD_BREAK_FOR); }

    /**

    • addByte decription : 给不足socket 每次传输的数组长度的字符添加到每次传输的数组长度个字节数
    • @param str
    • @return */ public static byte[] addByte(String str) { byte[] result = new byte[SOCKET_INPUT_ARRAY_LENGTH]; byte[] temp = str.getBytes(); for (int i = 0; i < result.length; i++) { if (i < temp.length) { result[i] = temp[i]; } else { result[i] = ' '; } } return result; }

    /**

    • splitString decription : 截取split字符串后面的字符串 如:(Get HTTP/1.1 NAME:QQ.exe
    • 调用此方法后为 QQ.exe)
    • @param request
    • @param split
    • @return */ public static String splitString(String request, String split) { // 请求分割的参数和分割符为空的情况 if (request == null || request.trim().length() <= 0 || split == null) { return null; } int startIndex = request.indexOf(split + ":") + split.length() + 1; return request.substring(startIndex, request.trim().length()); }

    /**

    • getNameAndLengthMap decription : 根据需要下载的软件信息得到软件名和软件大小的Map
    • @param downloadInfos
    • @return
      */
      public static Map getNameAndLengthMap(byte[] downloadInfos) {
      // 需要解析的参数为空的情况
      if (downloadInfos == null || downloadInfos.length <= 0) {
      return null;
      }
      // 初始化返回结果
      Map result = new HashMap();
      // 转换成字符串
      String info = new String(downloadInfos);
      // 以分号分隔
      String[] nameAndLengths = info.split(";");
      // 当需要下载的软件名和软件大小为空时
      if (nameAndLengths == null || nameAndLengths.length <= 0) {
      return null;
      }
      for (int i = 0; i < nameAndLengths.length; i++) {
      // 得到每一个名字和大小的组合:name:xxx.exe&size:123213
      String nameAndLength = nameAndLengths[i];
      // 得到以"&"分割后的字符串组
      String[] temp = nameAndLength.split("&");
      // 得到文件名name:xxx.exe
      String nameStr = temp[0];
      // 得到文件大小size:123213
      String sizeStr = temp[1];
      // 如果软件名和软件大小为空的情况
      if (nameStr == null || sizeStr == null
      || sizeStr.trim().length() <= 0) {
      continue;
      }
      nameStr = nameStr.substring(nameStr.indexOf(":") + 1, nameStr
      .length());
      sizeStr = sizeStr.substring(sizeStr.indexOf(":") + 1, sizeStr
      .length());
      // 如果软件名和软件大小为空的情况
      if (nameStr == null || sizeStr == null
      || sizeStr.trim().length() <= 0) {
      continue;
      }
      // 得到文件大小
      Long size = new Long(sizeStr.trim());

      // 把软件名和软件大小设置到返回结果Map中
      result.put(nameStr, size);
      

      }
      // 如果需要返回的结果为空的情况
      if (result.isEmpty()) {
      return null;
      }
      return result;
      }

    /**

    • getBlockNum decription : 根据传进来的文件大小,决定文件的可分割数
    • @param fileSize
    • @return */ public static int getBlockNum(long fileSize) { // 当文件等于0的情况 if (fileSize == 0) { return 0; } // 当文件小于等于10M的情况 if (fileSize <= BLOCK_SIZE) { return 1; } // 当文件大于等于10M的情况 return (int) (fileSize / BLOCK_SIZE + 1); }

    /**

    • processSoftwareInfos decription :
    • 把服务器传来的可以下载的软件数据信息和本地的已经下载的软件信息进行比较,得出可以下载的软件信息
    • @param softwareInfos
    • 如:name:xxx&size:12312;name: xxx&size:12312;...
    • @return
    • @throws SoftwareManagerException
      */
      public static Map processSoftwareInfos(String softwareInfos,
      List prefixnames) throws SoftwareManagerException {
      // 如果输入的需要处理的字符串为空的情况
      if (softwareInfos == null || softwareInfos.trim().length() < 0) {
      return null;
      }
      // // 得到本地软件分发父文件夹中的所有xml的名字
      // List names = softwareManagerService
      // .getAllSoftwareNames(parentFolderAbsolutePath);

      // 对需要处理的字符串以分号分割
      String[] tempInfos = softwareInfos.split(";");
      // 分割处理的结果
      if (tempInfos == null || tempInfos.length <= 0) {
      return null;
      }
      // 初始化需要返回的结果
      Map result = new HashMap();

      // 临时存储变量
      List softwareNames = new ArrayList();

      // 对分割的结果集进行循环 name:xxx&size:12312
      for (int i = 0; i < tempInfos.length; i++) {
      // 得到每个可以下载的Xml的详细信息
      // name:xxx&size:12312;
      String definiteInfos = tempInfos[i];
      if (definiteInfos == null || definiteInfos.trim().length() <= 0) {
      continue;
      }
      // 把得到的单个xml详细信息以&分割
      String[] definiteInfo = definiteInfos.split("&");
      if (definiteInfo == null || definiteInfo.length < 1) {
      continue;
      }
      // 得到软件名name:xxx
      String nameStr = definiteInfo[0];
      // 得到文件大小size:123213
      String sizeStr = definiteInfo[1];
      // 如果软件名和软件大小为空的情况
      if (nameStr == null || sizeStr == null
      || sizeStr.trim().length() <= 0) {
      continue;
      }
      nameStr = nameStr.substring(nameStr.indexOf(":") + 1, nameStr
      .length());
      sizeStr = sizeStr.substring(sizeStr.indexOf(":") + 1, sizeStr
      .length());
      // 如果软件名和软件大小为空的情况
      if (nameStr == null || sizeStr == null
      || sizeStr.trim().length() <= 0) {
      continue;
      }
      // 加入临时比较变量集合中
      softwareNames.add(nameStr);
      // 加入需要返回的结果集合中
      result.put(nameStr, new Long(sizeStr.trim()));
      }
      // 如果需要下载的软件集合为空的情况
      if (result.isEmpty()) {
      return null;
      }
      // 本地父目录里的软件信息列表为空的情况
      if (prefixnames == null || prefixnames.isEmpty()) {
      return result;
      }

      // 循环本地的父文件夹中的所有xml文件名
      for (int i = 0; i < prefixnames.size(); i++) {
      // 得到本地父目录中不含后缀名的文件名
      String prefixname = prefixnames.get(i);

      // 循环服务器端可以下载的软件信息列表
      for (int j = 0; j < softwareNames.size(); j++) {
          // 得到可以下载的文件名
          String softwareName = softwareNames.get(j);
          // 可以下载的文件名中没有后缀名的情况"."
          if (softwareName.lastIndexOf(".") == -1) {
              // 文件名不符合标准的情况下
              result.remove(softwareName);
              continue;
          }
          // 如果本地父目录中的软件前缀名和服务器可以下载的软件的前缀名一致的情况
          if (prefixname.equals(softwareName.substring(0, softwareName
                  .lastIndexOf(".")))) {
              result.remove(softwareName);
              break;
          }
      }
      

      }
      // 可以下载的软件信息为空的情况
      if (result.isEmpty()) {
      return null;
      }
      // 返回可以下载的软件名
      return result;
      }

    /**

    • addInputByte decription : 给需要传递的文件信息加上伪HTTP头信息
    • @param str
    • @return */ public static byte[] addInputHeadInfo(byte[] infos) { // 需要传递的文件信息为空的情况 if (infos == null) { return null; } // 得到默认的头信息 byte[] headByte = HTTP_HEAD_GET_INFO.getBytes(); // 初始化需要返回的信息 byte[] result = new byte[headByte.length + infos.length]; for (int i = 0; i < result.length; i++) { if (i < headByte.length) { result[i] = headByte[i]; } else { result[i] = infos[i - headByte.length]; } } return result; }

    /**

    • getInputStreamByte decription : 从Socket inputStream中得到去除头信息的字节数组
    • @param in
    • @param byteSize
    • 需要读取的包含头信息的字节流的长度
    • @return
      */
      public static byte[] getInputStreamByte(InputStream input, Integer byteSize) {

      // 设置已经读取数从去头信息开始
      int readed = 0; // 已经读取数
      // 头信息长度
      int headSize = HTTP_HEAD_GET_INFO.getBytes().length;
      // 初始化需要返回的数组
      byte[] result = new byte[byteSize - headSize];

      try {
      int available = 0; // 可读数
      if (available > (byteSize - readed)) {
      available = byteSize - readed;
      }
      while (readed < byteSize) {
      while (available == 0) {
      // 等到有数据可读
      available = input.available(); // 可读数
      }
      if (available > (byteSize - readed)) {
      available = byteSize - readed; // 剩余数
      }
      if (available > (BLOCK_SIZE+HTTP_HEAD_GET_INFO.getBytes().length)) {
      available = (BLOCK_SIZE+HTTP_HEAD_GET_INFO.getBytes().length); // size-readed--剩余数
      }
      byte[] buffer = new byte[available];
      int reading = input.read(buffer);
      for (int n = 0; n < reading; n++) {
      if ((readed + n) < headSize) {
      continue;
      }
      result[readed + n - headSize] = buffer[n];
      }
      available = 0;
      readed += reading; // 已读字符
      }
      } catch (IOException e) {
      System.out.println("Read readLenData Error!");
      }
      System.out.println("SoftwareProcessUtil result length = "+ result.length);
      return result;
      }
      }
      [/code]
      [b]问题补充:[/b]
      谢谢walsh的热心回复,虽然你没有帮我实质性的解决问题,但是我还是要感谢你的帮助!谢谢了!

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

6条回答 默认 最新

  • 已采纳
    walsh_bupt walsh_bupt 2009-08-26 10:43

    那可能就是你的方法写的不对了。

    点赞 评论 复制链接分享
  • walsh_bupt walsh_bupt 2009-08-25 23:13

    楼主,今天我刚解决了一个朋友的提问,你可以看看。

    [url]http://www.iteye.com/problems/23772[/url]

    你看是不是每次传的数据太多了,如果多接收方就不可能接收完整,这个如果都在自己机子上测试可能会没有问题,但是放到网络上,就会出现。

    解决办法是,每次限制每个包最大为512个字节,然后再传,传到对方之后,再合在一块,就行了。

    点赞 评论 复制链接分享
  • walsh_bupt walsh_bupt 2009-08-25 23:32

    [code="java"]/**
    * 对文件进行分割的每块的大小 : 10M
    */
    public static final int BLOCK_SIZE = 1024;

    /**
     * Socket inputStream 每次传输的数组长度
     */
    public static final int SOCKET_INPUT_ARRAY_LENGTH = BLOCK_SIZE
            + HTTP_HEAD_GET_INFO.getBytes().length;[/code]
    

    [color=red]楼主上面是你写的代码,我大致看了看,你吧每次传的数据大小设置为512个自己,多传几次没有什么,你如果传的多,在客户端就已经把数据截断了,服务器端当然就收不到数据了。[/color]

    楼主写的代码,注释做的还不错。唯一不好的就是,不能直接导到Myeclipse中。

    点赞 评论 复制链接分享
  • walsh_bupt walsh_bupt 2009-08-25 23:35

    [size=medium]不好意思,上面误写了两个字,是把数据包的大小设置为512个字节。你可以做个试验,先设置为512个字节,然后再设置为1024个字节。[/size]

    点赞 评论 复制链接分享
  • walsh_bupt walsh_bupt 2009-08-26 00:31

    楼主可以单步测试一下,看看究竟是由于什么原因引起的,是由于阻塞,还是由于接收方的socket关闭了,如果不是上述原因,

    我觉得还是因为每次传的数据太多的原因,数据包小一点,512个字节就可以了,然后在传一段时间sleep一会,别一下次就把所有的包都扔到网上了。

    点赞 评论 复制链接分享
  • walsh_bupt walsh_bupt 2009-08-26 09:46

    [quote]我在这块代码中的 public static final int BLOCK_SIZE = 1024; 只是1024个byte啊,并没有1024K啊 [/quote]

    改为512个字节,试试。

    点赞 评论 复制链接分享

相关推荐