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

目录
  • 错误现象
  • 引起错误原因在于
  • 可参考以下解决思路

最近在使用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;
	}

	/**
	 * 发送请求数据
	 *
	 * @param data
	 * @param start
	 * @param end
	 * @return
	 */
	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;
	}

	/**
	 * 读取返回数据
	 *
	 * @return
	 */
	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;
	}

	/**
	 * 发送请求数据
	 *
	 * @param data
	 * @param start
	 * @param end
	 * @return
	 */
	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;
	}

	/**
	 * 读取返回数据
	 *
	 * @return
	 */
	public byte[] read() {
		if (!connected) {
			return null;
		}
		try {
			return readStream(m_bufferedinputstream);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * @功能 读取流
	 * @param inStream
	 * @return 字节数组
	 * @throws Exception
	 */
	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异常或者到了数据流的结尾情况!

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

(0)

相关推荐

  • Java Socket通信介绍及可能遇到的问题解决

    前言 本文主要给大家介绍了关于Java中Socket通信的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. Java中基于TCP协议实现网络通信的两个类:客户端的Socket和服务器端的ServerSocket. Socket通信模型如图所示: 不管Socket通信的功能有多复杂,任何socket通信过程的基本结构都是一样的. 其基本步骤为: ①分别在客户端和服务器端创建Socket和ServerSocket实例:服务器端通过.accept()方法等待请求并阻塞.请求收

  • Java socket通讯实现过程及问题解决

    这篇文章主要介绍了Java socket通讯实现过程及问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本来是打算验证java socket是不是单线程操作,也就是一次只能处理一个请求,处理完之后才能继续处理下一个请求.但是在其中又发现了许多问题,在编程的时候需要十分注意,今天就拿出来跟大家分享一下. 首先先建立一个服务端代码,运行时也要先启动此程序. package com.test.some.Socket; import java.i

  • java使用Socket类接收和发送数据

    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类.通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据.在本文及后面的数篇文章中将详细讨论Socket类的使用,内容包括Socket类基础.各式各样的连接方式.get和set方法.连接过程中的超时以及关闭网络连接等. 在本文中,我们将讨论使用Socket类的基本步骤和方法.一般网络客户端程序在连接服务程序时要进行以下三步操作. 连接服务器 发送和接收数据 关闭网络连接 一.连接服务器 在

  • Java基于Socket的文件传输实现方法

    本文实例讲述了Java基于Socket的文件传输实现方法.分享给大家供大家参考,具体如下: 1. Java代码如下: package sterning; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.Ser

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

    目录 错误现象 引起错误原因在于 可参考以下解决思路 最近在使用Socket通讯时,遇到了接收内容不全(返回内容 = 4字节报文长度 + 内容主体)的问题:客户端发送请求数据,服务器明明返回了73个字节内容,但客户端有时能全部接收,但有时却只能返回4个字节. 一开始是怀疑服务器返回有问题,但使用调试工具连续测试了很多次,结果显示:服务器的确每次都返回了73个字节内容.那很明显了,问题出现在客户端代码上. 错误现象 再来看看调试工具结果: 让我们来看看客户端代码,调用方法如下:(该方法适用于返回报

  • C# Socket 发送&接收&返回 简单应用实例

    好久没有写过博客了,最近因项目需求,需要用到Socket来进行通信,简单写了几个例子,记录一下,代码很简单,无非就是接收与发送,以及接收到数据后返回一个自定义信息,也可以定义为发送. 接收端因为需求要监听某个端口,则在一开始判断一下,要使用的端口是否被占用,定义一个处理方法,以下为处理代码: public static bool PortIsUse(int port) { bool isUse = false; IPGlobalProperties ipProperties = IPGlobal

  • Java发送报文与接收报文的实例代码

    报文(message)是网络中交换与传输的数据单元,即站点一次性要发送的数据块.报文包含了将要发送的完整的数据信息,其长短很不一致,长度不限且可变. 个人理解:从客户端把字符串写入字节数组流中传达至服务端,但是此字符串是XML格式的,然后到了服务端,使用字节数组进行获取该字符串,再获取该字符串的document对象(因为字符串是xml格式的),然后解析获取数据即可. 发送报文 先创建生成报文的方法,添加了xml数据 /** * @desc 生成xml报文且转换为字符串 * @return */

  • Java Socket通信(一)之客户端程序 发送和接收数据

    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类.通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据. 对于Socket通信简述,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容.Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容. 例1:客户端的简略写法(一). Socket clien

  • Java Socket聊天室编程(二)之利用socket实现单聊聊天室

    在上篇文章Java Socket聊天室编程(一)之利用socket实现聊天之消息推送中我们讲到如何使用socket让服务器和客户端之间传递消息,达到推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯. 其实就是建立一个一对一的聊天通讯. 与上一篇实现消息推送的代码有些不同,在它上面加以修改的. 如果没有提到的方法或者类则和上一篇一模一样. 1,修改实体类(服务器端和客户端的实体类是一样的) 1,UserInfoBean 用户信息表 public class UserInfoB

  • Java Socket编程笔记_动力节点Java学院整理

    对于即时类应用或者即时类的游戏,HTTP协议很多时候无法满足于我们的需求.这会,Socket对于我们来说就非常实用了.下面是本次学习的笔记.主要分异常类型.交互原理.Socket.ServerSocket.多线程这几个方面阐述. 异常类型 在了解Socket的内容之前,先要了解一下涉及到的一些异常类型.以下四种类型都是继承于IOException,所以很多之后直接弹出IOException即可. UnkownHostException:    主机名字或IP错误 ConnectException

  • Java Socket+mysql实现简易文件上传器的代码

    最近跟着某网站学习了一个小项目,因为白天有课,所以都是晚上写的,今天把它完成了. 项目主要是实现一个文件上传器,通过客户端的登陆,把本地文件上传到服务器的数据库(本地的). 首先建两个表如下: 一个文件信息表 CREATE TABLE `fileinfo` ( `Fname` char(50) NOT NULL, `FInfo` blob NOT NULL, `FId` int(10) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`FId`) ) ENGINE=I

  • 深入理解Java Socket通信

    简述 Java中Socket分为普通Socket和NioSocket两种,这里介绍Socket. 我们可以把Socket比作两个城市间的交通工具,有了它可以在两城之间来回穿梭,交通工具有很多种,每种交通工具也有相应的交通规则.Socket也一样,也有多种.大多情况下使用的是TCP/IP的流套接字,它是一种稳定的通信协议.(TCP/IP与UDP的对比) Java中的网络通信是通过Socket实现的,Socket分为ServerSocket和Socket两大类,ServerSocket用于服务端,通

  • Java Socket编程简介_动力节点Java学院整理

    对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信.这样就有两个Socket了,客户端和服务端各一个. 对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以

随机推荐