文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java Socket无法完全接收返回内容的解决方案

2024-04-02 19:55

关注

最近在使用Socket通讯时,遇到了接收内容不全(返回内容 = 4字节报文长度 + 内容主体)的问题:客户端发送请求数据,服务器明明返回了73个字节内容,但客户端有时能全部接收,但有时却只能返回4个字节。

一开始是怀疑服务器返回有问题,但使用调试工具连续测试了很多次,结果显示:服务器的确每次都返回了73个字节内容。那很明显了,问题出现在客户端代码上。

错误现象

再来看看调试工具结果:

让我们来看看客户端代码,调用方法如下:(该方法适用于返回报文前两个字节表示长度的情况:2字节报文长度 + 内容主体)


public static void test() {
		SocketClient client = new SocketClient();
		// 建立socket对象
		int iret = client.connect("192.168.1.105", 1234);
		if (iret == 0) {
			// 发送数据
			client.write("helloworld".getBytes());
			// 接收数据
			byte data[] = client.read();
			if ((data != null) && (data.length != 0)) {
				// 处理接收结果
				Utils.print("响应报文字节数组---->" + Arrays.toString(data));
			}
		}		
	}

SocketClient.java源码:


public class SocketClient { 
	// 存储接收数据
	private byte m_buffer[] = new byte[0x10000];
	private Socket m_socket;
	private InputStream m_inputstream;
	private OutputStream m_outputstream;
	private BufferedInputStream m_bufferedinputstream;
	private BufferedOutputStream m_bufferedoutputstream;
	private boolean connected; 
	public int connect(String host, int port) {
		try {
			SocketAddress socketAddress = new InetSocketAddress(host, port);
			m_socket = new Socket();
			m_socket.connect(socketAddress, 5000);
			m_socket.setSoTimeout(60000);
 
			m_inputstream = m_socket.getInputStream();
			m_bufferedinputstream = new BufferedInputStream(m_inputstream);
			m_outputstream = m_socket.getOutputStream();
			m_bufferedoutputstream = new BufferedOutputStream(m_outputstream);
		} catch (Exception e) {
			return -1;
		}
		connected = true;
		return 0;
	}
 
	
	public int write(byte data[]) {
		if (data == null || data.length == 0 || !connected) {
			return 0;
		}
		try {
			m_bufferedoutputstream.write(data, 0, data.length);
			m_bufferedoutputstream.flush();
		} catch (Exception e) {
			return -1;
		}
		return 0;
	}
	
	
	public byte[] read() {
		if (!connected) {
			return null;
		}
		int len = -1;
		try {
			// 长度不正确,有时返回4,有时返回73
			len = m_bufferedinputstream.read(m_buffer, 0, 0x10000);
		} catch (Exception e) {
			len = 0;
		}
		if (len != -1) {
			return null;
		} else {
			byte ret[] = new byte[len];
			for (int i = 0; i < len; i++) {
				ret[i] = m_buffer[i];
			}
			return ret;
		}
	}
}

通过代码调试,发现问题出现在inputsream.read方法上,java API对其描述如下:

int java. io. BufferedInputStream.read( byte[] buffer, int offset, int byteCount) throws IOException

Reads at most byteCount bytes from this stream and stores them in byte array buffer starting at offset offset. Returns the number of bytes actually read or -1 if no bytes were read and the end of the stream was encountered. If all the buffered bytes have been used, a mark has not been set and the requested number of bytes is larger than the receiver's buffer size, this implementation bypasses the buffer and simply places the results directly into buffer.

Overrides: read(...) in FilterInputStream
Parameters:
buffer the byte array in which to store the bytes read.
offset the initial position in buffer to store the bytes read from this stream.
byteCount the maximum number of bytes to store in buffer.
Returns:
the number of bytes actually read or -1 if end of stream.
Throws:
IndexOutOfBoundsException - if offset < 0 or byteCount < 0, or if offset + byteCount is greater than the size of buffer.
IOException - if the stream is already closed or another IOException occurs.

引起错误原因在于

客户端在发送数据后,过快地执行read操作,而这时服务端尚未完全返回全部内容,因此只能读到部分字节。于是换了个思路:


public class SocketClient { 
	private Socket m_socket;
	private InputStream m_inputstream;
	private OutputStream m_outputstream;
	private BufferedInputStream m_bufferedinputstream;
	private BufferedOutputStream m_bufferedoutputstream;
	private boolean connected; 
	public int connect(String host, int port) {
		try {
			SocketAddress socketAddress = new InetSocketAddress(host, port);
			m_socket = new Socket();
			m_socket.connect(socketAddress, 5000);
			m_socket.setSoTimeout(60000);
 
			m_inputstream = m_socket.getInputStream();
			m_bufferedinputstream = new BufferedInputStream(m_inputstream);
			m_outputstream = m_socket.getOutputStream();
			m_bufferedoutputstream = new BufferedOutputStream(m_outputstream);
		} catch (Exception e) {
			return -1;
		}
		connected = true;
		return 0;
	}
 
	
	public int write(byte data[]) {
		if (data == null || data.length == 0 || !connected) {
			return 0;
		}
		try {
			m_bufferedoutputstream.write(data, 0, data.length);
			m_bufferedoutputstream.flush();
		} catch (Exception e) {
			return -1;
		}
		return 0;
	}
	
	
	public byte[] read() {
		if (!connected) {
			return null;
		}
		try {
			return readStream(m_bufferedinputstream);
		} catch (Exception e) {
			return null;
		}
	}
	
	
	public static byte[] readStream(InputStream inStream) throws Exception {
		ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = -1;
		while ((len = inStream.read(buffer)) != -1) {
			outSteam.write(buffer, 0, len);
		}
		outSteam.close();
		inStream.close();
		return outSteam.toByteArray();
	}
	
	public static void test() {
		SocketClient client = new SocketClient();
		// 建立socket对象
		int iret = client.connect("192.168.1.105", 1234);
		if (iret == 0) {
			// 发送数据
			client.write("helloworld".getBytes());
			// 接收数据
			byte data[] = client.read();
			if ((data != null) && (data.length != 0)) {
				// 处理接收结果
				Utils.print("响应报文字节数组---->" + Arrays.toString(data));
			}
		}		
	}
}

测试通过.....

可参考以下解决思路


protected byte[] readMessage(BufferedInputStream is) throws IOException {
		//        MyLog.d(TAG,"=======>readMessage--inputStream=" );
		        int offset = 0;
		        int messageStartOffset = -1;
		        int wait = 0;
		        int messageEndOffset = -1;
		        int findStartOffset = -1;
 
		        while(messageEndOffset==-1||(messageEndOffset+2)>offset){
		            if(is.available()==0){
		                try {
		                    Thread.sleep(MESSAGE_WAIT_INTERVAL);
		                    wait += MESSAGE_WAIT_INTERVAL;
		                } catch (InterruptedException ex) {
		                }
		                if(wait>=MESSAGE_OVERTIME){
		                //超时错误
		                    throw new RuntimeException(EXCEPTION_TIMEOUT);
		                }
		                continue;
		            }
		            
		            offset += is.read(messageBuffer, offset, is.available());//读出数据
		            TestMessage.showBytes(messageBuffer, 0, offset, "MESSAGE");
		            if(messageStartOffset==-1){ //未找到报文头
		                if(findStartOffset<0)
		                    findStartOffset = 0;
		                messageStartOffset = findStartOffset(messageBuffer, findStartOffset, offset);//查找报文头
		                MyLog.e(TAG, "messageStartOffset="+messageStartOffset);
		                if(messageStartOffset>=0){//找到报文头
		                    if(messageStartOffset<2){
		                        //报文错误
		                        throw new RuntimeException(EXCEPTION_MSG_PARSE_ERROR);
		                    }else{
		                        int iMessageLength = ((messageBuffer[messageStartOffset-2]&0xff)<<8)+
		                         (messageBuffer[messageStartOffset-1]&0xff);
		//                        MyLog.e(TAG, "iMessageLength="+iMessageLength);
		                        int ignoreInvalidLength = messageStartOffset-4;
		                        messageEndOffset = iMessageLength + ignoreInvalidLength;
		//                        MyLog.e(TAG, "messageStartOffset="+messageStartOffset);
		                        MyLog.e(TAG, "messageEndOffset="+messageEndOffset);

如果想要让程序保证读取到count个字节,最好用以下代码:


int count = 100;  
byte[] b = new byte[count];  
int readCount = 0; // 已经成功读取的字节的个数  
while (readCount < count) {  
    readCount += inStream.read(b, readCount, count - readCount);  
}  

这样就能保证读取100个字节,除非中途遇到IO异常或者到了数据流的结尾情况!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯