CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  Java >  J2SE / 基础类

从socket读东东,对方不给出长度说明,我怎样高效准确的读出他通过socket传给我的东东?

楼主pxpbbz()2006-05-04 09:23:37 在 Java / J2SE / 基础类 提问

现要和一个系统进行socket连接,我发请求,它给响应信息。问题是它的响应里不包含说明它整个返回串(流)长度的说明,也没有约定它的返回串以一个什么字符结尾。(我以前做的很多通讯程序都是有长度说明的,我根据长度都取制定数量的字节就ok了,既正确又快)。  
  所幸的是我们是采取同步通讯方式,我不读完第n个请求的回应是不会发第n+1个请求的。所以这样应该不会造成无法区分两次回应消息的问题。  
  由于没有长度说明,我采取一个while循环一直读到返回-1长度为止(知道不会再有东东传过来了),同时我给socket设定了一个2秒钟的读超时,一到这个时间我也认为已经读完。这样处理到现在还没有出问题。  
  1:但是我总发现,几乎每次读操作的结束都是因为出现超时错,而不是因为read返回-1,这说明在os的底层通讯模块返回-1之前,超时时间已经到了!!!(请大家注意这个现象是否正常)????  
  2:每次等待socket读超时,导致速度很慢。请问我怎样高效准确的读出他通过socket传给我的东东?  
   
  问题点数:50、回复次数:33Top

1 楼pxpbbz()回复于 2006-05-05 12:27:27 得分 0

顶,顶,顶Top

2 楼zouxinfox(Read the source,Use the force)回复于 2006-05-05 17:17:51 得分 0

给你一个建议,我没试过。  
  InputStream   input=socket.getInputStream();  
  int   length=input.available();  
  byte   []buffer=new   byte[length];  
  把内容读到buffer里,在一定时间内再用几次   length=input.available();如果length为零,则表明已经读完,否则继续读Top

3 楼UnAgain()回复于 2006-05-05 19:59:51 得分 0

按照Specification中的说明,available()得到的数值是在无阻塞情况下,下一次读操作能读到的byte数。不能用来判断stream是否结束。  
   
  其实,直接使用stream的read方法就可以判断stream是否结束。这里的stream和fileIO的stream是一样的,比如我们读文件的时候,也不需要提前知道文件的大小,尽管可以得到。  
   
   
  InputStream   input=socket.getInputStream();  
  byte   []buffer=new   byte[length];  
   
  int   res;  
  while   ((res   =   input.read(length))   !=   -1   )   {  
          //res的值是本次读操作读到的字节量。  
          ...  
  }  
  //   如果res为-1,stream结束。  
  ...  
   
  Top

4 楼pxpbbz()回复于 2006-05-07 22:30:50 得分 0

"while   ((res   =   input.read(length))   !=   -1   )   {"  
  通过检查返回值为-1来确定已经到流的结束,这个方法我已经使用了。而且我结合了另外一个办法,就是把read的超时值设定得比较小,2s。然后我的读的代码大致如下:  
  try{  
  while   ((res   =   input.read(length))   !=   -1   )   {  
  .....}  
  }  
  catch(SocketTimeOutException   e){  
  ......  
  }  
  我的目的是:通过超时,和   read返回-1   这两个条件来判断本次已经读完(只要两个中的一个满足条件,就认为是读完)。  
   
  但是,java中read函数读socket时返回-1的时机我并不清楚,系统调用(tcp)何时会给你返回-1?文档上说   “到了流的结尾   ,就会返回-1”,那么哪位知道java何时   认为   ““到了流的结尾”?????  
   
  还有,经我的测试,我的这个程序总是达到了超时时间,而没有返回-1的时候(这两天都没有发现)。就是说,java到了超时时间,但是没有返回-1,说明如果我等它返回-1需要等待比超时更长的时间,谁帮我说说怎样才能避免这种无意义的时间效率的浪费???  
   
   
  谢谢  
  Top

5 楼pxpbbz()回复于 2006-05-07 22:31:11 得分 0

顶,顶,顶Top

6 楼pxpbbz()回复于 2006-05-08 08:58:01 得分 0

^_^,正在等待ingTop

7 楼lyfxzzb()回复于 2006-05-08 09:06:39 得分 0

关注,顶上去Top

8 楼realdreamer(楼主英明,贫僧久仰大名,特来拜见)回复于 2006-05-08 09:14:03 得分 0

如果响应不会太长的话.   要按你的工作逻辑来测定.  
  如果响应不太长,假设基本上一个包可以发完响应,或者假设只要响应发出,基本上是全部同时到达.  
   
  在这样的假设前提下,   每发一个请求后的第一次读取,   必须无限等待,因为当前没有任何响应,直到有响应后,可以等待一个比2秒更少的时间或者不等待,一直读取流里没有数据.   就视为响应结束.  
   
  这样可以么~?Top

9 楼realdreamer(楼主英明,贫僧久仰大名,特来拜见)回复于 2006-05-08 09:15:19 得分 0

不过这种协议确实有问题.协议没有长度,没有结束标志.   很不好处理.Top

10 楼zhang21cnboy(事了抚衣去,不留身与名)回复于 2006-05-08 11:23:25 得分 0

呵呵,看样子你们的连接是长连接,这样的话,永远不会返回-1的.Top

11 楼pxpbbz()回复于 2006-05-08 11:34:46 得分 0

“在这样的假设前提下,   每发一个请求后的第一次读取,   必须无限等待,因为当前没有任何响应,直到有响应后,可以等待一个比2秒更少的时间或者不等待,一直读取流里没有数据.   就视为响应结束.”  
  怎样知道   “   流里没有数据”????  
  Top

12 楼sheep219(sheep219)回复于 2006-05-08 11:36:11 得分 0

关注Top

13 楼terry6394(小猪,向前跑!)回复于 2006-05-08 11:53:04 得分 0

回复人:zhang21cnboy(事了抚衣去,不留身与名)   (   一星(中级))   信誉:95   2006-05-08   11:23:00   得分:0  
  ?    
  呵呵,看样子你们的连接是长连接,这样的话,永远不会返回-1的.  
   
  ----------------------------------------------------------  
  长连接?!  
  什么是长连接?!  
  Top

14 楼zhang21cnboy(事了抚衣去,不留身与名)回复于 2006-05-08 11:56:56 得分 0

长连接就是连接上之后就不断开了......一直连接着.  
   
  socket的流,标准的实现应该是SocketInputStreamImpl,你可以去看看代码.  
   
  呵呵,建议你修改一下服务器的代码,或者自己实现一个SocketInputStrem.  
   
  Top

15 楼pxpbbz()回复于 2006-05-08 12:10:35 得分 0

“在这样的假设前提下,   每发一个请求后的第一次读取,   必须无限等待,因为当前没有任何响应,直到有响应后,可以等待一个比2秒更少的时间或者不等待,一直读取流里没有数据.   就视为响应结束.”  
  怎样知道   “   流里没有数据”????Top

16 楼pxpbbz()回复于 2006-05-08 12:55:43 得分 0

ding,Top

17 楼jshi123()回复于 2006-05-08 15:32:28 得分 0

在read()的时候,如果对方关闭socket(closesocket   with   SO_LINGER),则read读出所有数据后,会返回-1。  
  你发现每次都回超时,是因为对方从来都不主动关闭连接。  
   
  我觉得这个问题从技术上没有什么好的办法解决,对方是个连byebye也不会说的坏孩子,你又不知道他什么时候才把话讲完,当然只有等他不说话,等到不耐烦时才离开。  
   
  非技术讨论:应该努力促使对方改进它们的系统,如果做不到,则不能要求通过你们的技术代价来弥补它们的缺陷。即便客户是上帝,上帝也应该讲理。Top

18 楼realdreamer(楼主英明,贫僧久仰大名,特来拜见)回复于 2006-05-08 16:21:14 得分 0

“在这样的假设前提下,   每发一个请求后的第一次读取,   必须无限等待,因为当前没有任何响应,直到有响应后,可以等待一个比2秒更少的时间或者不等待,一直读取流里没有数据.   就视为响应结束.”  
  怎样知道   “   流里没有数据”????  
  =================================================  
  InputStream   里有个   available   方法,   这个应该可以吧.Top

19 楼UnAgain()回复于 2006-05-08 17:03:26 得分 0

这几天我看这个帖子。  
  我同意jshi123()的观点。文件流的提供方在把本地的字节全部注入流中之后,就应该主动关闭流。而对于接收方,等读入流中全部数据之后,再读就会返回-1。  
  流和socket是两种机制。通过socket建立好双方的连接,数据就可以通过连接“流”动。如果在没有读完的情况下,通讯断了,下一次的读操作就会抛出违例,指示连接中断。流本身没有超时的概念,也事先不知道数据的长度;所以,这要对方不关闭流,接收方就无法判断传输是否结束,也就不会返回-1。  
   
  我突然想到你说的“不包含说明它整个返回串(流)长度的说明”。其实socket只是一个基于TCP/IP协议建立一个数据传输的通道,而不是数据传输协议。就是说,如果你要连接的系统是你自己写的,那么,在确认双方建立了连接之后,你不是马上传送数据,而是按照一定的结构,把所传数据的长度先传送出去。接收端也是先读取约定数量数据,从中求出数据长度,并接着返回一个确认信息。发送方收到确认信息之后,就开始发送正式数据。现有的协议应该也是这么处理的。你或者找一份合适的协议,按照该协议定制自己传输方式(这样做比较好),或者自己制定一个协议,按照我上面说的办法。Top

20 楼xiachedan(瞎扯蛋)回复于 2006-05-09 13:35:56 得分 0

加个特殊的结束标实孚Top

21 楼UnAgain()回复于 2006-05-09 15:09:53 得分 0

上次我说的不对题,下面的代码应该是正解。  
  单独建立一个线程监视超时,一到时间就断掉socket,也就终止了超时等待。  
  以前没把线程当回事儿,现在总算对这块儿有点认识了。  
   
  代码太长,可能要分几部分帖出。  
   
  import   java.io.*;  
  import   java.net.*;  
  import   java.util.*;  
   
  public   class   TestBlocking   {  
          public   static   void   main(String[]   args)   {  
                  try   {  
                          Sender   sender   =   new   Sender();  
                          sender.start();  
                          (new   Receiver(sender)).start();  
                  }   catch   (Exception   e)   {  
                          e.printStackTrace();  
                  }  
          }  
   
          static   class   Receiver   extends   Thread   {  
                  private   final   int   WAITING_TIME   =   800;  
                  private   final   int   COUNT_ONCE   =   3;  
                  private   Boolean   terminate   =   false;  
   
                  Socket   socket;  
                  InetSocketAddress   addr;  
   
                  Receiver(Sender   serv)   {  
                          socket   =   new   Socket();                          
                          addr   =    
                                  new   InetSocketAddress(serv.getPort());  
                  }  
                  class   Monitor   implements   Runnable   {  
                          Thread   me;  
                          Boolean   stop   =   false;  
   
                          public   synchronized   void   wakeup()   {  
                                  this.notify();  
                          }  
   
                          public   void   start()   {  
                                  me   =   new   Thread(this);  
                                  me.start();  
                          }  
                          public   synchronized   void   stop()   {                                  
                                  stop   =   true;                                          
                                  this.notify();                                  
                          }  
                           
                          public   void   run()   {                                  
                                  while   (true)   {  
                                          try   {  
                                                  synchronized   (this){  
                                                          wait(WAITING_TIME   *   2);  
                                                  }  
                                                  if   (stop)   {                                                                  
                                                          break;  
                                                  }                                                          
                                                   
                                                  synchronized   (this){  
                                                          wait(WAITING_TIME);          
                                                  }  
                                                  if   (stop)   {                                                                  
                                                          break;  
                                                  }  
                                                   
                                                  synchronized(terminate)   {  
                                                          if   (terminate)   {  
                                                                  try   {  
                                                                          socket.close();  
                                                                  }   catch   (IOException   ioe)   {  
                                                                          ioe.printStackTrace();  
                                                                  }  
                                                          }  
                                                  }  
                                          }   catch   (Exception   e)   {  
                                                  e.printStackTrace();  
                                                  continue;  
                                          }  
                                  }  
                          }  
                  }  
                                   
                  public   void   run()   {  
                          int   times   =   0;  
                          Monitor   monitor   =   new   Monitor();  
                          while   (true)   {  
                                  try   {  
                                          socket.connect(addr,   40);  
                                  }   catch   (SocketTimeoutException   ste)   {  
                                          if   (times++   <   10)   {  
                                                  System.out.println("recv:   where   are   you?");  
                                          }  
                                          continue;  
                                  }   catch   (IOException   ioe)   {  
                                          break;  
                                  }  
                          }  
   
                          terminate   =   false;  
                          monitor.start();  
                                                   
                          try   {  
                                  InputStream   in   =   socket.getInputStream();  
                                  //char[]   buf   =   new   char[50];  
                                  //int   off   =   0,   len   =   COUNT_ONCE;  
   
                                  System.out.println("recv:   Hello");  
                                   
                                  while   (true)   {                                                                                  
                                          int   c;  
                                           
                                          System.out.print("recv   heard:   \n\t");  
                                          while   (socket   !=   null)   {  
                                                  synchronized(terminate)   {  
                                                          terminate   =   true;  
                                                  }  
                                                  monitor.wakeup();  
   
                                                  c   =   in.read();  
                                                  if   (c   ==   -1)   {  
                                                          break;  
                                                  }  
                                                  System.out.print((char)c);  
                                                  synchronized(terminate)   {  
                                                          terminate   =   false;  
                                                          monitor.wakeup();  
                                                  }  
                                          }  
   
                                          break;  
                                  }  
   
                          }   catch   (SocketException   se)   {  
                                  System.out.println(  
                                          "\nrecv:   I   can't   bear   to   wart   for   so   long   time,   bye!");  
                                  monitor.stop();  
                          }   catch   (Exception   e)   {          
                                  e.printStackTrace();  
                          }  
                  }  
          }  
  Top

22 楼UnAgain()回复于 2006-05-09 15:10:30 得分 0

还好,2次就行了  
   
          static   class   Sender   extends   Thread   {  
                  private   final   int   WAITING_TIME   =   400;  
                  private   final   int   COUNT_ONCE   =   5;  
   
                  private   final   String   toSay   =    
                          "Hello,   I   love   you,   and   you?";  
   
                  private   ServerSocket   serv   =   null;  
   
                  Sender()   throws   IOException{  
                          serv   =   new   ServerSocket(0);  
                          serv.setSoTimeout(50);  
                  }  
                  public   InputStream   getStream(){  
                          return   null;  
                  }  
                  public   int   getPort()   {  
                          return   serv.getLocalPort();  
                  }  
   
                  public   void   run()   {  
                          int   times=0;  
                          System.out.println("serv   want   to   say:   \n\t"   +   toSay);  
                          while   (true)   {  
                                  try   {  
                                          Socket   socket   =   serv.accept();  
   
                                          PrintWriter   out   =    
                                                  new   PrintWriter(  
                                                          new   OutputStreamWriter(  
                                                                  socket.getOutputStream()));  
   
                                          InputStream   in   =    
                                                  socket.getInputStream();  
                                           
                                          for   (int   i=0;   i<toSay.length();   i+=COUNT_ONCE)   {  
                                                  int   writeCount   =   toSay.length()   -   i;  
                                                  writeCount   =   writeCount<=COUNT_ONCE   ?    
                                                          writeCount   :   COUNT_ONCE;  
   
                                                  String   s   =   toSay.substring(i,   i   +   writeCount);                                                  
                                                  out.write(s);  
                                                  out.flush();  
                                                   
                                                  if   (i<10)   {  
                                                          sleep(WAITING_TIME);  
                                                  }   else   {  
                                                          sleep(WAITING_TIME   +   100);  
                                                  }                                                  
                                          }  
   
                                          //out.close();  
   
                                  }   catch   (SocketTimeoutException   ste)   {  
                                          //ste.printStackTrace();  
                                          if   (times++<3)   {  
                                                  System.out.println("serv:   are   you   here?");  
                                          }   else   {                                                  
                                                  System.out.println("serv:   oh,   shit!");  
                                                  System.out.println("\nUnagain:   haha!");  
                                                  break;  
                                          }  
                                  }   catch   (IOException   ioe)   {  
                                          ioe.printStackTrace();  
                                          break;  
                                  }   catch   (InterruptedException   ire)   {  
                                          ire.printStackTrace();  
                                          break;  
                                  }                                  
                          }  
   
                          if   (serv   !=   null   &&   !serv.isClosed())   {  
                                  try   {  
                                          serv.close();  
                                  }   catch   (IOException   e)   {  
                                  }   finally   {  
                                          serv   =   null;  
                                  }  
                          }  
                  }  
          }  
  }  
  Top

23 楼jshi123()回复于 2006-05-09 15:49:11 得分 0

楼上:“单独建立一个线程监视超时,一到时间就断掉socket”  
  在你断掉连接前,搂主的程序还不是得等着……  
   
  现在的问题是,按理说双方通信可能几十个毫秒就完成了,可是现在得多等两秒钟。  
  照你的方案,有办法不要等这2秒钟吗?想得太多了吧。  
  Top

24 楼UnAgain()回复于 2006-05-09 16:04:01 得分 0

to   jshi123():  
  你先把问题看清楚。由于发送方在把数据发送结束后,不关掉stream,导致接收方要一直等到socket实效之后才能结束通信。这个时间可不是几十个毫秒的问题。  
   
  我的例子中的发送方在正常发送10个字节(也不正常,故意延迟400ms)之后,把延时改为以前的3倍,因为超过监视器的延时设定,socket就会被中断。  
   
  注意,Sender中这个地方我发错了,  
                                                  if   (i<10)   {  
                                                          sleep(WAITING_TIME);  
                                                  }   else   {  
                                                          //sleep(WAITING_TIME   +   100);  
                                                          //改为  
                                                          sleep(WAITING_TIME   *   3);  
                                                  }Top

25 楼jshi123()回复于 2006-05-09 16:21:39 得分 0

UnAgain:  
  你的程序实现的功能应该是:超过你设定的延时时间后,socket就会被中断掉。  
   
  但这个问题并不是楼主的问题,搂住已经说了,他设了读超时时间。楼主没有贴出代码,估计差不多这样:  
  socket.setSoTimeout(2000);  
  这样就是等2秒钟钟。要达到你的程序同样的效果,楼主只要改成:  
  socket.setSoTimeout(400);  
  就可以了。  
   
  但无论如何,这都是通过侦听-等待-关闭的方式结束通信,不是以协议方式结束(closesocket   gracefully方式)  
   
  Top

26 楼UnAgain()回复于 2006-05-09 17:49:35 得分 0

他这种设置不能用于限制超时,你有没有自己试试?Top

27 楼jshi123()回复于 2006-05-09 18:34:47 得分 0

你是说Socket.setSoTimeout()方法吗,这个当然是可以的,我们在好几个项目中都用到了,完全工作正常。  
  Top

28 楼UnAgain()回复于 2006-05-09 18:52:34 得分 0

To:   jshi123()    
  确实如你所说。我刚才试了一下,我原来的代码只是设置了ServerSocket的SO_TIMEOUT,没有设置客户端。  
   
  收获不小,多谢指点。Top

29 楼UnAgain()回复于 2006-05-09 18:58:07 得分 0

To:   jshi123()    
  有MSN吗?我的MSN是tl21cen@hotmail.com,我们可以讨论讨论Top

30 楼jshi123()回复于 2006-05-09 19:25:00 得分 0

不好意思嘞,我平时上网不多的,就这两天多泡了下。  
  看你写的代码也很漂亮,有空我还是会常上来学习交流的,希望以后有机会多切磋。Top

31 楼UnAgain()回复于 2006-05-09 19:40:30 得分 0

哈哈,承蒙夸奖。  
  常联系Top

32 楼UnAgain()回复于 2006-05-09 19:41:49 得分 0

我的blog:http://blog.csdn.net/Unagain。多给提点意见。Top

33 楼UnAgain()回复于 2006-05-11 12:49:34 得分 0

to:   pxpbbz   ()    
  象我的那个例子,使用线程。Top

相关问题

关键词

得分解答快速导航

  • 帖主:pxpbbz

相关链接

  • CSDN Java频道
  • Java类图书
  • Java类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo