关于Socket使用中读取响应消息的问题

一:关于Socket使用中读取响应消息的方法
使用Socket进行通信会涉及到读取服务端的响应消息。读取的方法可分为2类三种。

类1:一次性全部读取。

代码:
方法一:public String getResultStr(Socket sourceSocket)
{

String resultStr = null;

InputStream in;
try {
in = sourceSocket.getInputStream();
int readIndex = 5 * 1024 * 1024;
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in), readIndex);
char[] charArray = new char[readIndex];
int read_rst = bufferedReader.read(charArray);
resultStr = new String(charArray, 0, read_rst);
bufferedReader.close();

} catch (IOException e) {
e.printStackTrace();
}

return resultStr;
}
类2:非一次性读取

方法二:一行行读
public String getResultStr(Socket sourceSocket)
{
String resultStr = null;

InputStream in;

try {
in = sourceSocket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in));
StringBuffer responseBuffer = new StringBuffer();
String line = bufferedReader.readLine();
while (null != line)
{
responseBuffer.append(line);
line = bufferedReader.readLine();
}

bufferedReader.close();

resultStr = responseBuffer.toString();

} catch (IOException e) {
e.printStackTrace();
}

return resultStr;
}

方法三:一字节一字节读
public String getResultStr(Socket sourceSocket)
{
String resultStr = null;

InputStream in = null;

try {
in = sourceSocket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in));

int read_rst = bufferedReader.read();

StringBuffer readBuffer = new StringBuffer();
while (-1 != read_rst)
{
char singleChar = (char) read_rst;

readBuffer.append(singleChar);
}

bufferedReader.close();

resultStr = readBuffer.toString();
} catch (IOException e) {
e.printStackTrace();
}

return resultStr;
}

其中方法一的优点是读取速度快,且不用被超时所扰。缺陷是,只能读取一定量的字节,虽然BufferedReader的构造方法提供了设置缓冲区大小的功能,但是不管设多大,都只能读取一定量的字节,据项目中的情况来看,是65536个。如果响应消息有几MB的话,这种方法是肯定不行的。但是如果读取的消息很小,在65536个字节以内,则可以使用。

方法二的优点是便于做读取后的算法操作,速度嘛也挺快。缺陷是不能精确读取。因为readLine的方法读不出’\n’,’\r\n’,所以读出来的内容其长度与实际长度有出入。

方法三是最优解。(以上只是三种方法的原型,具体业务还要具体实现)


三:关于解决Socket读取响应消息超时的分析及解决方法:

分析
在使用Socket的过程中会遇到读取响应消息超时的问题,这是为什么呢?就我现在的理解,一句话:在服务端还没有关闭连接前,客户端读取响应消息就会一直等待,直到超时。

解决方法:
1.Socket提供的setSoTimeout(int timeout) 方法
在获得Socket的实例后,设置下超时时间,然后当read或是readLine完最后一个字节或是字符串后,会抛一个InterruptedIOException,在catch里做你想做的事情,或break,或关掉连接。

2.如果服务端也是你设计的话(就是响应消息也是你拼接的),请看。
可以在服务端里拼接响应消息的方法里为响应消息加上一个‘\r\n‘,注意一定要加在消息末尾。然后用“二:关于Socket使用中读取响应消息的方法”里第三种方法来读取响应消息,注意,通信消息头中肯定会有“Content-Length“一项,先取出其值(Content-Length表示消息主体的字节长度),接着找出主体消息的起始位置(主体消息中的最前方一定要是个固定的内容),开始计算响应消息的实际字节长度,最后将取出的Content-Length的值与实际计算出的长度进行比较,如果相等就break掉,这样,就不会读到会引起超时消息末尾。

其方法1的优点时易于实现,缺陷是不安定因素太大。setSoTimeout里总要设个值,设多少呢?假设在网络正常情况下读一个1MB的响应消息需要1s,那么如果网络阻塞呢?如果你设一个很大的值,就会影响使用。
方法2的优点是不受网络因素的影响。缺陷是,如果服务端的中文字符编码与流中的中文字符编码不一致,会导致乱码,进而会影响计算的准确度。一旦计算的不准,就会超时。
不过,方法2仍是最优解。

 


我在写 PHPRPC 客户端时,就是用这种方法来跟服务器通讯的,实践证明是很有效的。

1 为什么网络流不建议用字节读取行 line = bufferreader.readLine()
2 起初我也为如何判断读取完成而恼火,读取文件流可以用(line = br.readLine()) != null(按字节读取用-1判断),但是在网络流里面这个怎么也读不对,后来别人告诉我在服务器发送完信息后用socket.shutdownOutput(),之后!= null可以判断了


你用它读网络流它仍然是按照它的 Buffer 大小来读入字节数的,所以读多了就会有麻烦了。
< 你每次都读取<="size的数据 " 不要按行去读取,你可以定义一个缓冲区byte[size] 这12个字节就表示你要接收的数据大小.如果读满了这12个字节的数据那么就读完了. 建议你先固定读取12个字节(包头定义)>(不用mina的时候项目中就这么做。)

1 缓存中有多于一行(\r\n)的纪录,那么readLine()读取一行,buff里还剩下一些
2 缓存中有不到一行的记录,那么readLine()
  (1)是阻塞,直到缓存中出现\r\n为止 ?
  (2)现在缓存中有多少就认为是一行?

我觉得很可能是第2个情况,因为一个网络流很可能没有换行,那么不可能直到超时才继续执行。

情况一:你只是希望你文件所有的内容都传到服务器上而已,那么一行读取是否为实际真的一行就不重要了,因为最终拼成的文件是完整的就够了。

情况二:你的客户端基于服务器段传输信息格式,比如说第一行是命令,第二行是版本号,那么你需要明确知道读多少是一行,那可能buff就有你说的问题了,总之仅仅需要传输完整的文件,无所谓读多读少问题。
< 命令用4个字节 令牌用12个字节  发送长度用4个字节  比如 包头可以用固定字节  你的发送包数据格式和回复包数据格式 以上连接你可以定义出 注明该次任务情况等等   就是断开连接 第三 你可以进行多次的交互 这个步骤是不确定 要编码的编码等等 要分包的分包 这个和你具体业务有关  发送请求 第二次 服务端返回任务令牌  请求连接 第一 大概可以分为以下几个步骤 你可以靠令牌去实现这个.>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值