从socket读东东,对方不给出长度说明,我怎样高效准确的读出他通过socket传给我的东东?
现要和一个系统进行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




