几周前,我发布了以下问题,因为我在使用readObject从ObjectInputStream中读取对象时出现问题:
Continuously read objects from an ObjectInputStream in Java
随着我的回应,我想我能够理解出了什么问题 - >我在循环中调用readObject,即使没有数据已发送,因此我收到EOFException。
但是,因为我真的想要一种机制,我继续从输入流中读取,我正在寻找这个问题的解决方案。
我尝试使用以下方法创建一种机制,我只在有可用数据时调用readObject:
if(mObjectIn.available() > 0)
mObjectIn.readObject()
但不成文,mObjectIn.available()总是返回0.
任何人都可以让我朝着好的方向前进。是否有可能实现我想要的东西?
答案 0 :(得分:1)
您可以通过int
发送ObjectOutputStream
,让对方知道您何时停止发送对象。
例如:
public static void main(String[] args) {
//SERVER
new Thread(new Runnable() {
@Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
while (ois.readInt() != -1) {//Read objects until the other side sends -1.
System.out.println(ois.readObject());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//CLIENT
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeInt(1);//Specify that you are still sending objects.
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeInt(-1);//Let the other side know that you've stopped sending object.
}
} catch (Exception e) {
e.printStackTrace();
}
}
或者你可以在最后写一个null
对象,让对方知道你不会再发送任何对象。仅当您确定无需发送的任何对象为null
时,此方法才有效。
new Thread(new Runnable() {
@Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
String obj;
while ((obj = (String) ois.readObject()) != null) {
System.out.println(obj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeObject(null);
}
} catch (Exception e) {
e.printStackTrace();
}
答案 1 :(得分:0)
之前我用Java编写了客户端/服务器tcp代码。流可能很棘手。当您使用流时,您无法假设读取时所有数据都存在。相反,您必须从流中提取字节,如果达到某个msg分隔符,则获取所有获取的字节并处理它们。在您的情况下,您将把它们变成一个对象。
以下是我用来从套接字中提取msg的三个类。
首先获取的输入字节的MsgExtractor基本上来自套接字。它会检查msg是否已到达(通过分隔符)。
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.text.ParseException;
import java.util.Arrays;
import java.util.MissingResourceException;
import java.util.concurrent.BlockingQueue;
/**
* This class parses the data retrieved from the TCP socket streams for new messages. All new messages found are inserted into the shared message queue.
*
* @author jmartinez
*/
public class MsgExtractor {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
private static final Logger logger = Logger.getLogger(MsgExtractor.class);
///////////////////////////////////////////////////////////////////////
// CONSTANTS
private final int INIT_BUFFER_SIZE = 10000;
// <buffer variables>
private byte[] bufferedMsg = new byte[INIT_BUFFER_SIZE];
private int bufferSize = INIT_BUFFER_SIZE;
private int curBufferPos = 0; // ...current position on the buffered message
private int curMsgSize = 0; // ...current amount of buffered chars
///////////////////////////////////////////////////////////////////////
// VARIABLES
private final byte[] delimiter;
private final int delimiterSize;
private final BlockingQueue msgQueue;
private final int maxMsgSize;
// </>
////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new MsgExtractor.
*
* @param msgService
*/
public MsgExtractor(MessageService msgService) {
ServerProperties properties = ServerProperties.getInstance();
if (properties == null) {
throw new MissingResourceException("unable to obtain properties", MsgExtractor.class.getName(),
"ServerProperties");
}
this.maxMsgSize = Integer.parseInt(properties.getProperty(ServerProperties.MAX_MESSAGE_SIZE));
this.delimiter = Arrays.copyOf(msgService.getMsgHandler().getMessageDelmiter(),
msgService.getMsgHandler().getMessageDelmiter().length);
this.delimiterSize = delimiter.length;
this.msgQueue = msgService.getSharedMsgQueue();
}
/**
* Inserts new chars into the message buffer. It then extracts any messages found in the buffer by checking for any occurrences of the message delimiter. Extracted messages are removed from the buffer, converted to String, and inserted into the ManagedQueue.
*
* @param cbuf - An array containing the new chars that need to be added to the message buffer.
* @param offset - Array offset from where on the array to start collecting the new chars.
* @param length - The number of chars that need to be collected.
* @throws java.lang.InterruptedException
* @throws java.text.ParseException
*/
public void insertNewChars(byte[] cbuf, int offset, int length) throws InterruptedException, ParseException {
// ...check if the message buffer has enough room to add the new chars... if not, increase the buffer size.
if (bufferSize < curMsgSize + length) {
increaseBufferSize();
}
// ...add the new chars to the buffer one at a time
for (int i = 0; i < length; i++) {
bufferedMsg[curMsgSize++] = cbuf[i + offset];
}
// ...keep checking for new messages as long as they are being found
boolean rv;
do {
rv = checkForNewMsg();
} while (rv == true);
if (curMsgSize > maxMsgSize) {
throw new ParseException("max message size reached and still not found delimiter", curMsgSize);
}
}
/**
* Doubles the message buffer size.
*/
private void increaseBufferSize() {
bufferSize *= 2;
byte[] temp = new byte[bufferSize];
System.arraycopy(bufferedMsg, 0, temp, 0, curMsgSize);
bufferedMsg = temp;
}
/**
* Checks if the delimiter is found in the currently buffered message.
* checkForNewMsg starts its search where it last left off at.
*
* Performance can be improved if this method checks for all occurrences of the message delimiter, instead of one.
*
* @return true if delimiter was found in buffer, else false
*/
private boolean checkForNewMsg() throws InterruptedException {
while (curBufferPos <= curMsgSize - delimiterSize) {
boolean delimitterFound = true;
for (int i = 0; i < delimiterSize; i++) {
if (delimiter[i] != bufferedMsg[i + curBufferPos]) {
delimitterFound = false;
break;
}
}
if (delimitterFound) {
extractNewMsg(curBufferPos);
return true;
} else {
curBufferPos++;
}
}
return false;
}
/**
* A new message is located at index = 0 through delimiterPos - 1. the method extracts that message and inserts it into a local String array.
*
* Performance can be improved if this method extracted a messages for all occurrences of the message delimiter, instead of one.
*
* @param delimiterPos - The position where the delimiter was found.
*/
private void extractNewMsg(int delimiterPos) throws InterruptedException {
try {
msgQueue.put(new String(bufferedMsg, 0, delimiterPos - 1));
} catch (InterruptedException ie) {
logger.error("Interrupted while putting message to ManagedQueue", ie);
throw ie;
} catch (Exception e) {
logger.error("Unable to put message to ManagedQueue", e);
}
// <reset the message buffer and corresponding variables>
byte[] tmpBuffer = new byte[this.bufferSize];
int tmpMsgSize = 0;
for (int i = delimiterPos + this.delimiterSize; i < curMsgSize; i++) {
tmpBuffer[tmpMsgSize++] = bufferedMsg[i];
}
curBufferPos = 0;
bufferedMsg = tmpBuffer;
curMsgSize = tmpMsgSize;
// </>
}
}
这是ConnectionHandler,它管理连接并为MsgExtractor提供字节:
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class handles all new connections. It reads data from the socket stream
* and sends it to a MsgExtractor for further processing.
*
* This class in Runnable. use the run method to start it up and interrupt it to
* shut it down.
*
* @author Jose
*/
public class ConnectionHandler implements Runnable {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
// ...log4j's Logger is thread safe
private static final Logger logger = Logger.getLogger(ConnectionHandler.class);
private final static AtomicBoolean isRunning = new AtomicBoolean(false);
/////////////////////////////////////////////////////////////////////////
// Constants
private final int BUFFER_SIZE = 8000;
private final byte[] rcvdChars = new byte[BUFFER_SIZE];
////////////////////////////////////////////////////////////////////////
// INSTANCE VARIABLES
private final Socket socket;
private final MsgExtractor msgExtractor;
/////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new ConnectionHandler.
*
* @param socket - The socket that this object is to read from.
* @param msgService - The MessageService that is used to create the
* MsgExtractor object that this object uses.
*/
public ConnectionHandler(Socket socket, MessageService msgService) {
this.socket = socket;
logger.info("ConnectionHandler thread ID:" + Thread.currentThread().getId() + " instanctiated for listen port "
+ socket.getLocalPort());
msgExtractor = new MsgExtractor(msgService);
}
/**
* Starts the ConnectionHandler. Creates an input stream from this objects
* socket to read data from. all read data is sent to a MsgExtractor. The
* MSgExtractor will extract messages from the read data and will add any
* messages to this objects ManagedQueue. This method continues operating
* till the thread is interrupted or the socket is no longer available for
* providing input. Returns right away if validation of this object fails.
*/
public void run() {
// ...if validation fails, return
if (isValid() == false) {
return;
}
// ...if already running, return
if (!isRunning.compareAndSet(false, true)) {
logger.warn("ConnectionHandler thead ID:" + Thread.currentThread().getId()
+ " is already running, not going to run again.");
return;
}
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is starting up.");
// <get input reader from socket>
InputStream inputReader;
try {
inputReader = socket.getInputStream();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to get socket input stream in ParserThread.run", ex);
return;
}
// </>
// ...bytes read from the socket
int bytesRead;
try {
// ...stops when the thread is interrupted or the socket no longer provides input
while ((socket.isInputShutdown() == false) || (Thread.interrupted() == false)) {
try {
// ...get data from socket stream
bytesRead = inputReader.read(rcvdChars, 0, BUFFER_SIZE);
} catch (IOException e) { // ... catch any exception and call it a day for this thread
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered error reading from socket, could be a closed connection.", e);
break;
}
try {
msgExtractor.insertNewChars(rcvdChars, 0, bytesRead);
} catch (ParseException pe) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered parsing error, closing connection.", pe);
break;
} catch (InterruptedException ex) {
break;
}
}
} finally {
// ...close the socket if it is still open
if (socket.isClosed() == false) {
try {
socket.close();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to close socket.", ex);
}
}
isRunning.set(false);
} // end of: finally
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is shutting down.");
}
/**
* Used by the run() method to validate this object. If validation fails,
* the run() method returns right away.
*
* @return - Returns true is this object is valid, else false.
*/
private boolean isValid() {
if (socket == null) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", validation failed, Socket is null");
return false;
}
return true;
}
}
完成此处的是上面代码中弹出的MsgService:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* The Model Object, or DTO, of the CMS server. It contains information that is needed by just about every component of the CMS server.
*
* There is a separate instance of this class for each messaging service that the CMS server is configured for.
*
* @author Jose
*/
public class MessageService {
/**
* the shared message queue where new messages are inserted into by the MsgExtractor, and taken by the MessageQueueWorker.
*/
private final BlockingQueue<byte[]> sharedMsgQueue = new LinkedBlockingQueue<byte[]>();
/**
* the TCP listen port
*/
private final int port;
/**
* the MessageHandler that will process new messages
*/
private final MessageHandler msgHandler;
/**
* optional. max number of TCP connections
*/
private final int maxConnections;
/**
* optional. max number of worker threads that will be used to process new messages
*/
private final int maxWorkerThreads;
/**
* Creates new message service object. Sets max connections and max worker threads to 0.
*
* @param port - TCP port this message service will listen on.
* @param msgHandler - the MessageHandler that will be called to process messages received for this message service.
*/
public MessageService(int port, MessageHandler msgHandler) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = 0;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object. Sets max worker threads to 0.
*
* @param port - TCP port this message service will listen on.
* @param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* @param connections - max concurrent connections available for this service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object.
*
* @param port - TCP port this message service will listen on.
* @param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* @param connections - max concurrent connections available for this service.
* @param workerThreads - max worker threads that will process messages for this message service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections, int workerThreads) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = workerThreads;
}
/**
*
* @return this object's MessageHandler
*/
public MessageHandler getMsgHandler() {
return msgHandler.getNewInstance();
}
/**
*
* @return the TCP port this MessageService will listen on
*/
public int getPort() {
return port;
}
/**
*
* @return the BlockingQueue used to store new messages.
*/
public BlockingQueue<byte[]> getSharedMsgQueue() {
return sharedMsgQueue;
}
/**
*
* @return max concurrent connections available for this service
*/
public int getMaxConnections() {
return maxConnections;
}
/**
*
* @return max worker threads that will process messages for this message service
*/
public int getMaxWorkerThreads() {
return this.maxWorkerThreads;
}
}