在java中,InputStream 的 read() ,read(byte [] bs), read(byte [] bs, offset, length)方法在读到流结尾的时候,会返回-1我们在读取数据的时候,会这么干:byte [] bs = new byte[1024];
int i=0;
while((i=ins.read(bs))!=-1)
Log.debug("Client Read: " + i+" >> " ,bs);
现在从Socket中读取,遇到了一点问题,
服务器的socket数据早就写完了,可客户端上的read(byte [] b)就是不返回 -1,一直塞着
目前是用超时来解决了可是老用超时会影响性能呀,为什么不会返回-1呢?API文档中可明明是写着的
难道服务器端发送的数据有问题?服务器发送代码:
byte [] bs= "0123456789abcde".getBytes();
Log.debug("Server Writer: ",bs);
ous.write(bs);
ous.flush();
这段代码应该不会有问题吧
输出流的结束标记是什么?难道还要手动发一个结束标记?才能让SocketInputStream返回-1
伙计们,不用提它建议,比如服务器发送的时候,加上数据长度...
我现在只是想知道为什么不能返回 -1,我的做法错在哪?
钻个牛角尖

解决方案 »

  1.   

    服务端没有将输出流做关闭操作,所以,客户端无法感应到,是否已经是流的末尾.
    楼主在服务端flush之后,close掉输出流,客户端读取时就会返回-1了.
      

  2.   

    TCP长连接在退出之前是不能关闭流的
      

  3.   

    那这就跟tcp有关系了,tcp在断开连接前(FIN标记),c/s双方是协商好的
    同时SocketInputStream还没返回-1,是因为你的client端的流对象还没结束
      

  4.   

    服务器端 Socket 发完数据后把输出流关掉 Socket.shutdownOutput();
      

  5.   

    tcp中发完数据后,主动关闭流,就会被感知到然后返回-1
      

  6.   

    流结束,并不代表连接结束,如果只传送一次,你关掉可以,如果是持续性的,你还能关掉Socket吗?,
    例如批量发送文件,发送开始的时间,最好设置一个标志位,一个文件发送结束的时间,改变这个标志位,
    接收方根据标志位,来决定是继续接收,还是接收完毕,开始接收下一个文件.
      

  7.   

    你去看看这个方法setSoTimeOut();你看了就知道他干啥用的....
      

  8.   

    兄弟,你用PrintWriter吧,这样readline()多好,
      

  9.   

    呵呵3楼讲的没错,但是TCP长连接,输出流是不能关闭的11楼讲的更深入一些,流结束了,并不表示连接结束. 可是流关闭了,那连接也就跟着关闭了,所以,眼前的问题也可以说是怎样让流结束而连接不关闭
    输入流的读取没有返回-1,就是因为没有读到结束标记嘛13楼呢,呵呵,现在就是用超时来解决问题的,但这不是根本之法,老用超时,对性能影响太大14楼的兄弟,readline方法是读取一行数据,在回车换行为结束标记,它是字符流,并非字节流对这些底层的东东,研究很少看了以上各位的高见,看来不把输出流close掉,那输入流中是返回不了-1的了罗?
      

  10.   

    不明白版主说的Socket是长连接是什么意思.
    Java当中的Socket类,其实是使用TCP协议进行传输的.TCP是可靠的一种传输协议.如果版主想用TCP协议,并且,服务端和客户端,在没有信息进行传输的时候,也不断开连接,一般情况下,客户端会在Socket超时之前,想服务端发送一个用于维持连接的信息包,来维持连接.但是TCP协议,并不是指长连接.我们每天上网浏览网页,其实,也是以TCP协议为基本的传输协议的.只是,这个是短连接的形式,每次浏览器向服务器提交一个请求,服务短应答请求,然后断开连接.在应用TCP协议,并且是长连接传输信息的情况下.通常会再封装一层协议的.但,观察版主的收发内容,并没有涉及这一层,所以,我这里并不知道是长连接.首先,我们要明确一点,发送方如果不将输出流进行关闭,接收方就会认为输入流没有结束,直到超时.
    其次,我们判断一个信息是否已经完全的读取完毕,除了使用输入流结束这种办法,还可以自行封装一层协议,用于信息的交互.
    当然,我们也可以采用Http那样的交互方式进行信息的传递,但是,它是短连接的.下面我来说一下,TCP长连接传输数据的一般做法.
    一般情况下,我们会在TCP的基础上再封装一层协议,用户长连接的传输.协议的信息包,也分包头和包体两个部分.
    包体,主要就是我们要传输的信息.(维持连接的信息包,包体可为空)
    包头,一般分为三个部分.第一部分是信息包的长度(长度一般是指整个信息包的长度);第二部分是包体信息的类型(在这里指出是否是维持连接包);第三部分是信息包的序列号,一般情况下,这个序列号要确保在传输过程中唯一标识该信息包.
    如果为了安全起见,还可以在包体后添加包尾,包尾数据用于对包体数据的验证)这样,通信双方就可以根据包长来判断一次接收的操作是否结束了.
      

  11.   

    可不可以考虑把timeout设置的短一点
      

  12.   

    说说俺现在的理解:在长连接中,对方的输出流不close掉,那么己方的输入流是返回不了-1的为了解决这个问题,对方在发送报文的时候就会在一个约定段上加上报文长度。己方在收到报文后,根据报文中说明报文长度的,读取指定的字节数
    读完之后,若发送方还有数据过来,就不理它了或者当做下一个报文来处理如果己方还未读足报文中说明的字节数量对方就没发了,那么己方就继续等待,在指定时间之后还没有读足数据,就报超时报文长度加上报文中的数据格式定义等信息,就成了通讯协议
    小结:
    在长连接时,通讯协议中报文长度信息是必不可小的,我认为协议中有报文长度一段,最初的出发点就在这里。
    如果发送方不说明报文长度,那么接收方在收完数据后就只能阻塞直到超时。这样对性能影响是非常大的
      

  13.   

    在长连接时,通讯协议中报文长度信息是必不可小的,我认为协议中有报文长度一段,最初的出发点就在这里。 如果发送方不说明报文长度,那么接收方在收完数据后就只能阻塞直到超时。这样对性能影响是非常大的
    这也跟timeout有相当大的关系,
      

  14.   

    LZ这样理解不对。
    read返回-1是作为判断socket流关闭的标志。即如果双方之间的连接中断。
    默认使用socket输入流都是阻塞形式。
    不过可以使用如下方式来实现无阻塞的读取数据:
    BufferedInputStream bis = new BufferedInputStream(client.getInputStream())  //其中client为socket客户端对象
    int num = bis.available();  ////返回不受阻塞可读取的输入流的字符数if (num == 0){ //表示输入流中没有可读取的数据
       //do something
    }else{//输入流有数据,其中num表示有多少字节的数据
       byte buf[]=new byte[num];
       int i= bis.read(buf); 
    }
      

  15.   

    服务器的socket数据早就写完了,可客户端上的read(byte [] b)就是不返回 -1,一直塞着 
    ---------------------------------------------------------------------------------------
    数据是写完了。但连接还建立着啊。LZ可在服务器端调用close操作。这样你客户端就会返回-1。
      

  16.   

    所有的贴子看完了,我觉得本质的一些问题还没有解决掉.
    可以看http://topic.csdn.net/u/20080626/09/18db2301-f672-4ec8-aaa8-bf8c75489893.html
    SUN公司就是因为这样的结束流的方式不好处理,才让我们用包装流来解决.
    我们用字节流来读取数据的时候,就因为返回不了-1这种情况需要做很多的判断,首先一点,连接是不能是短连接.
    这样的问题有时候讨论可以看着是多余的,我其实不这么看,但有人告诉我他是这样的看法,既然是readLine()方法,就不必用read()方法.
    但是原理我们还是要知道.
    流的结束如果不是文件的话是不以-1结尾的,所以不可能返回-1,所以当没有关闭连接时或没有调用shutdownInput()方法时是一直阻塞的.
    我想过用什么办法从服务端写一个-1过来,但是事实告诉我这种方法是很行不通的.
    有两种方案来结束流:
    1\让客服端自己来判断流是束结束(根据读取的信息)
    2\从服务端告诉客服端一个信息
    在第一种方法中,有一个问题(或者说我们自己制造的一个问题):
        byte  [] by = new byte[1024];
        while((num = in.read(by)) != -1){
            if(num < by.length){//当读取的数据的长度小于by的长度时,认为读到了末尾了.
                 //do something
                 break;
            }
        }这里的问题是我们能不能判断出数据的长度刚好了by数组长度的倍数的情况呢.
    为是解决这个办法,应可能的用包装流来读写,好处是速度快,方便.