i use pseudo code to describe the problem first,and i'll paste the whole code in the follow,which can run in the local.
1.selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
2.while(true){
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
3. if(!key.isAcceptable()){
continue;
}
4. ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
...
}
}
the exception occured in the step 4,then i do a check in the step 3,but it can not pass the acceptable check and go into a dead loop.
occasionally ,it can receive and response normally and i have not make any change,it's too strange to me.
here i paste the code and hope someone can help me. thanks.
package io.Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import io.util.IOUtil;
public class NioServer extends Thread{
private int port;
private Selector selector;
private ServerSocketChannel serverChannel;
public NioServer(int port){
this.port = port;
}
#Override
public void run() {
try{
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch(IOException e){
IOUtil.close(serverChannel);
}
System.out.println("server start:" + port);
while(true){
try {
selector.select();
} catch (ClosedSelectorException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(!key.isValid()){
key.cancel();
IOUtil.close(key.channel());
IOUtil.close(key.selector());
System.out.println(IOUtil.now() + "clear a invalid key.");
continue;
}
// i put a check here,if is not Acceptable,then continue, but it's a dead loop
if(!key.isAcceptable()){
System.out.println("not Acceptable");
continue;
}
try {
//Exception here: SocketChannelImpl cannot be cast to ServerSocketChannel
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if(channel == null){
continue;
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
// if (key.isReadable()){
// System.out.println("not read");
// }
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (channel.read(buffer) > 0) {
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String expression = new String(byteArray, "UTF-8");
System.out.println(IOUtil.now() + "receive request:" + expression);
String result = null;
response(channel, result);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void shutdown(){
IOUtil.close(selector);
IOUtil.close(serverChannel);
}
private void response(SocketChannel channel, String response) throws IOException {
response = "hello response";
System.out.println(IOUtil.now() + "send response:"+ response);
byte[] bytes = response.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
channel.write(buffer);
}
public static void main(String[] args) {
new NioServer(IOUtil.DEFAULT_PORT).start();
}
}
package io.Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import io.util.IOUtil;
public class NioClient extends Thread{
private volatile CountDownLatch connectLatch;
private String ip;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private NioClient(String ip, int port) {
this.ip = ip;
this.port = port;
connectLatch = new CountDownLatch(1);
}
public static NioClient open(String ip, int port){
NioClient client = new NioClient(ip,port);
client.start();
return client;
}
#Override
public void run(){
try{
long begin = System.currentTimeMillis();
System.out.println(IOUtil.now() + "start client");
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
System.out.println(IOUtil.now() + "cost time:" + (System.currentTimeMillis() - begin) + "ms");
connectLatch.countDown();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while(true){
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
if(!key.isValid() || !key.isReadable()){
continue;
}
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
if(channel.read(buffer) > 0){
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String response = new String(byteArray,"UTF-8");
System.out.println(IOUtil.now() + "receive response:" + response);
}
}
}
}catch(IOException e){
e.printStackTrace();
}
}
public void request(String request) {
try {
connectLatch.await();
} catch (InterruptedException e) {
System.out.println(IOUtil.now() + "interrupted" + e.getMessage());
}
try {
byte[] bytes = request.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
socketChannel.register(selector, SelectionKey.OP_READ);
//TODO
System.out.println(IOUtil.now() + "send request:" + request);
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) throws InterruptedException {
NioClient client = NioClient.open(IOUtil.DEFAULT_HOST, IOUtil.DEFAULT_PORT);
client.request("hello");
// while(true){
// sleep(500);
// String request = IOUtil.buileRequest(1991);
// client.request(request);
// }
}
}
package io.util;
import java.io.Closeable;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class IOUtil {
public static final String DEFAULT_HOST = "127.0.0.1";
public static final int DEFAULT_PORT = 8080;
public static final String operators[] = {"+", "-", "*", "/"};
public static final int CLIENNT_NUM = 10;
public static final boolean CLIENT_CLOSEABLE = true;
public static String now(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS ").format(new Date());
}
public static String buileRequest(int seed){
Random random = new Random(seed);
return random.nextInt(10) + IOUtil.operators[random.nextInt(4)] + (random.nextInt(10) + 1);
}
public static void close(Closeable io) {
if (io != null) {
try {
io.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The only way the exception you describe could occur is if you attempt that typecast on a channel which is not a ServerSocketChannel, which could happen if its key is ready but not 'acceptable'. Clearly you didn't have your step 2 when you got this exception, so you processed a 'readable' channel as though it was an 'acceptable' channel.
So the code you posted doesn't actually exhibit that problem, but it does have a large number of others. You don't need to close the selector just because a key is invalid, and if it is invalid it is already cancelled, so you don't need to cancel it, and anyway closing the channel cancels the key. Why aren't you interested in OP_READ/isReadable() on the channels you have accepted and registered for OP_READ? And why are you trying to read from a channel you have just accepted, without waiting for OP_READ?
Throw it away and have a good look at the Java NIO tutorial.
I do want to comment on one particular piece of nonsense in the client:
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
Here you are performing the equivalent of a blocking-mode connect in non-blocking mode. It can all be replaced by:
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false);
Then:
socketChannel.register(selector, SelectionKey.OP_CONNECT);
Here you are registering for an event that has already happened. Truly bizarre. You will never get OP_CONNECT from an already-connected socket. Remove.
Related
I need to send a message from the ServerSocket to the client when the key is writable, I think that the message is being sent in the right way but when I try to read it on the client I get this error:
java.io.StreamCorruptedException: invalid stream header: 48656C6C
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:866)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:358)
at MyTcpClient.main(MyTcpClient.java:22)
How can I read the message correctly in the client?
These are my files:
MyAsyncProcessor.java
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyAsyncProcessor {
HashMap<Integer, MyTask> hashMap = new HashMap<>();
ExecutorService pool;
public MyAsyncProcessor() {
}
public static void main(String[] args) throws IOException {
new MyAsyncProcessor().process();
}
public void process() throws IOException {
pool = Executors.newFixedThreadPool(2);
InetAddress host = InetAddress.getByName("localhost");
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(host, 9876));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey key;
System.out.println("AsyncProcessor ready");
while (true) {
if (selector.select() > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> i = selectedKeys.iterator();
while (i.hasNext()) {
key = i.next();
if (!key.isValid()) {
key.cancel();
continue;
}
if (key.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
System.out.println("Channel hashCode: " + socketChannel.hashCode());
MyTask task = new MyTask(selector, socketChannel);
hashMap.put(socketChannel.hashCode(), task);
pool.execute(task);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
MyTask task = hashMap.get(socketChannel.hashCode());
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
try {
socketChannel.read(byteBuffer);
String result = new String(byteBuffer.array()).trim();
String[] words = result.split(" ");
task.timeToRead = Integer.parseInt(words[words.length - 2]) * 1000;
task.timeToWrite = Integer.parseInt(words[words.length - 1]) * 1000;
System.out.println(Thread.currentThread().getName() + " reads for " + task.timeToRead + " mills");
try {
Thread.sleep(task.timeToRead);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ended reading");
} catch (Exception e) {
e.printStackTrace();
return;
}
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
if (key.isWritable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
MyTask task = hashMap.get(socketChannel.hashCode());
task.readyToWrite();
hashMap.remove(socketChannel.hashCode());
System.out.println(Thread.currentThread().getName() + " writes for " + task.timeToWrite + " mills");
try {
Thread.sleep(task.timeToWrite);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ended writing");
CharsetEncoder enc = StandardCharsets.US_ASCII.newEncoder();
String response = "Hello!\n";
socketChannel.write(enc.encode(CharBuffer.wrap(response)));
key.cancel();
}
i.remove();
}
}
}
}
}
MyTcpClient.java
import java.io.*;
import java.net.Socket;
import java.util.Random;
public class MyTcpClient {
public static void main(String[] args) {
Random rand = new Random();
int secondsToRead = rand.nextInt(5);
int secondsToWrite = secondsToRead + 1;
String message = "Seconds for the task to be read and written: " + secondsToRead + " " + secondsToWrite;
System.out.println(message);
Socket socket;
ObjectInputStream ois;
try {
socket = new Socket("127.0.0.1", 9876);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
printWriter.println(message);
System.out.println("Sending message");
ois = new ObjectInputStream(socket.getInputStream());
String response = (String) ois.readObject();
System.out.println("Response: " + response);
ois.close();
} catch (IOException e) {
System.out.println("Error in Socket");
e.printStackTrace();
System.exit(-1);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Thank you in advance
Here on the server you are sending raw (text) data
socketChannel.write(enc.encode(CharBuffer.wrap(response)));
And here on the client you are trying to read this as an Object
ois = new ObjectInputStream(socket.getInputStream());
String response = (String) ois.readObject();
System.out.println("Response: " + response);
For your ObjectInputStream to work, you need to be using an ObjectOutputStream on the other side. I would recommend against using the Object streams, as these are Java-only and can fail if the two sides are using different versions of the serialized object. Instead, just use raw streams.
Better yet, use WebSockets - WebSockets handle a lot of the plumbing that sockets come with in a more elegant way. You can definitely find an implementation of WebSockets for Java on GitHub
I have solved it by using on the client to receive:
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
server.read(readBuffer);
String result = new String(readBuffer.array()).trim();
In the server to send:
CharsetEncoder enc = StandardCharsets.US_ASCII.newEncoder();
String response = "Task completed in " + (this.timeToWrite+this.timeToRead) + " mills \n";
I want to serialize 'Message' object, I can successfully transfer it as bytes array through socketChannel. After that, I change the object's properties (so that it may have larger size), and then there's a problem in sending object back to the client.
Once I try to obtain the object on the client side, I get an exception, it occurs when I deserealize Message obj in getResponse() method:
org.apache.commons.lang3.SerializationException: java.io.StreamCorruptedException: invalid stream header: 00000000
But, somehow, this applies only for the first client (After the exception is thrown, connection with the first client is over) and when I start a new client (not closing server) I can successfully transfer the object back and forth, furthermore, it works for any new clients.
This is my minimal debuggable version:
import org.apache.commons.lang3.SerializationUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class Client {
private SocketChannel server;
public void start() throws IOException {
try {
server = SocketChannel.open(new InetSocketAddress("localhost", 5454));
server.configureBlocking(false);
} catch (IOException e) {
System.err.println("Server isn't responding");
System.exit(0);
}
Scanner scRequest = new Scanner(System.in);
Scanner scState = new Scanner(System.in);
System.out.println("Enter request:");
String request = scRequest.nextLine();
while (!request.equals("exit")) {
try {
// In my actual project class Person is a way different (But it's still a POJO)
// I included it here to make sure I can get it back after sending to the server
System.out.println("Enter a number:");
Person person = new Person(scState.nextInt());
sendRequest(request, person);
System.out.println("\nEnter request:");
request = scRequest.nextLine();
} catch (Exception e) {
e.printStackTrace();
}
}
stop();
}
public void sendRequest(String sMessage, Person person) {
Message message = new Message(sMessage, person);
ByteBuffer requestBuffer = ByteBuffer.wrap(SerializationUtils.serialize(message));
try {
server.write(requestBuffer);
requestBuffer.clear();
getResponse();
} catch (Exception e) {
System.out.println(e.getMessage());
System.err.println("Connection lost");
System.exit(0);
}
}
public void getResponse() throws Exception {
ByteBuffer responseBuffer = ByteBuffer.allocate(1024 * 1024 * 64);
int read = server.read(responseBuffer);
responseBuffer.clear();
if(read == -1) {
throw new Exception();
}
byte[] bytes = new byte[responseBuffer.limit()];
responseBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
System.out.println(message);
}
public void stop() throws IOException {
server.close();
}
public static void main(String[] args) throws IOException {
Client client = new Client();
client.start();
}
}
import org.apache.commons.lang3.SerializationUtils;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
public void start() throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress("localhost", 5454));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started");
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
register(selector, serverSocket);
}
if (key.isReadable()) {
try {
getRequest(key);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
iter.remove();
}
}
}
private void getRequest(SelectionKey key) throws Exception {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer requestBuffer = ByteBuffer.allocate(1024 * 1024);
int read = client.read(requestBuffer);
requestBuffer.clear();
if(read == -1) {
key.cancel();
throw new Exception("Client disconnected at: " +
((SocketChannel) key.channel()).socket().getRemoteSocketAddress());
}
byte[] bytes = new byte[requestBuffer.limit()];
requestBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
sendResponse(client, message);
}
private void sendResponse(SocketChannel client, Message message) throws IOException {
message.setResult("Some result");
ByteBuffer responseBuffer = ByteBuffer.wrap(SerializationUtils.serialize(message));
while (responseBuffer.hasRemaining()) {
client.write(responseBuffer);
}
responseBuffer.clear();
}
private void register(Selector selector, ServerSocketChannel serverSocket) throws IOException {
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("New client at: " + client.socket().getRemoteSocketAddress());
}
public static void main(String[] args) throws Exception {
new Server().start();
}
}
I try to send this object as a bytes array:
import java.io.Serializable;
import java.util.Formatter;
public class Message implements Serializable {
private String command;
private Person person;
private String result;
public Message(String command, Person person) {
this.command = command;
this.person = person;
}
public String getCommand() {
return command;
}
public void setCommand(String executedCommand) {
this.command = executedCommand;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
#Override
public String toString() {
return new Formatter()
.format("Command: %s\nAttached object: %s\nResult: %s",
command, person, result)
.toString();
}
}
I include instance of this class inside Message obj:
public class Person implements Serializable {
private final int state;
public Person(int state) {
this.state = state;
}
#Override
public String toString() {
return "Person state: " + state;
}
}
I have no idea what is going wrong, hope for your help.
UPD: I used 'org.apache.commons:commons-lang3:3.5' dependency to serialize an object into bytes array
I have never used Java NIO channels before, so I am not an expert. But I found out several things:
General:
In order to debug your code, it is helpful to use e.printStackTrace() instead of just System.out.println(e.getMessage()).
Client:
SocketChannel server in the client should be configured as blocking, otherwise it might read 0 bytes because there is no server response yet, which causes your problem.
You should always call ByteBuffer.clear() before reading something, not afterwards.
After reading, the position in the byte buffer has to be reset to 0 via responseBuffer.position(0) before calling get(byte[]), otherwise it will read undefined bytes after the ones just read.
You should size your byte arrays according to the number of bytes read, not the byte buffer size. It might work the other way around, but it is inefficient.
Server:
You should always call ByteBuffer.clear() before reading something, not afterwards.
After reading, the position in the byte buffer has to be reset to 0 via responseBuffer.position(0) before calling get(byte[]), otherwise it will read undefined bytes after the ones just read.
When catching exceptions during getRequest(key) calls, you should close the corresponding channel, otherwise after a client disconnects the server will indefinitely try to read from it, spamming your console log with error messages. My modification handles that case and also prints a nice log message telling which client (remote socket address) was closed.
Caveat: There is nothing in your code dealing with the situation that a request or response written into the channel on the one side is bigger than the maximum ByteBuffer size on the other side. Similarly, in theory a (de)serialised byte[] could also end up being bigger than the byte buffer.
Here are my diffs:
Index: src/main/java/de/scrum_master/stackoverflow/q65890087/Client.java
===================================================================
--- a/src/main/java/de/scrum_master/stackoverflow/q65890087/Client.java (revision Staged)
+++ b/src/main/java/de/scrum_master/stackoverflow/q65890087/Client.java (date 1612321383172)
## -15,7 +15,7 ##
public void start() throws IOException {
try {
server = SocketChannel.open(new InetSocketAddress("localhost", 5454));
- server.configureBlocking(false);
+ server.configureBlocking(true);
}
catch (IOException e) {
System.err.println("Server isn't responding");
## -56,22 +56,24 ##
getResponse();
}
catch (Exception e) {
- System.out.println(e.getMessage());
+ e.printStackTrace();
+// System.out.println(e.getMessage());
System.err.println("Connection lost");
System.exit(0);
}
}
public void getResponse() throws Exception {
- ByteBuffer responseBuffer = ByteBuffer.allocate(1024 * 1024 * 64);
+ ByteBuffer responseBuffer = ByteBuffer.allocate(1024 * 1024);
+ responseBuffer.clear();
int read = server.read(responseBuffer);
- responseBuffer.clear();
if (read == -1) {
- throw new Exception();
+ throw new Exception("EOF, cannot read server response");
}
- byte[] bytes = new byte[responseBuffer.limit()];
+ byte[] bytes = new byte[read];
+ responseBuffer.position(0);
responseBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
Index: src/main/java/de/scrum_master/stackoverflow/q65890087/Server.java
===================================================================
--- a/src/main/java/de/scrum_master/stackoverflow/q65890087/Server.java (revision Staged)
+++ b/src/main/java/de/scrum_master/stackoverflow/q65890087/Server.java (date 1612323386278)
## -35,7 +35,11 ##
getRequest(key);
}
catch (Exception e) {
- System.err.println(e.getMessage());
+ e.printStackTrace();
+// System.err.println(e.getMessage());
+ SocketChannel client = (SocketChannel) key.channel();
+ System.err.println("Closing client connection at: " + client.socket().getRemoteSocketAddress());
+ client.close();
}
}
iter.remove();
## -45,15 +49,16 ##
private void getRequest(SelectionKey key) throws Exception {
SocketChannel client = (SocketChannel) key.channel();
- ByteBuffer requestBuffer = ByteBuffer.allocate(1024 * 1024 * 64);
+ ByteBuffer requestBuffer = ByteBuffer.allocate(1024 * 1024);
+ requestBuffer.clear();
int read = client.read(requestBuffer);
- requestBuffer.clear();
if (read == -1) {
key.cancel();
throw new Exception("Client disconnected at: " +
((SocketChannel) key.channel()).socket().getRemoteSocketAddress());
}
- byte[] bytes = new byte[requestBuffer.limit()];
+ byte[] bytes = new byte[read];
+ requestBuffer.position(0);
requestBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
sendResponse(client, message);
Just for completeness' sake, here are the full classes after I changed them:
import org.apache.commons.lang3.SerializationUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class Client {
private SocketChannel server;
public void start() throws IOException {
try {
server = SocketChannel.open(new InetSocketAddress("localhost", 5454));
server.configureBlocking(true);
}
catch (IOException e) {
System.err.println("Server isn't responding");
System.exit(0);
}
Scanner scRequest = new Scanner(System.in);
Scanner scState = new Scanner(System.in);
System.out.println("Enter request:");
String request = scRequest.nextLine();
while (!request.equals("exit")) {
try {
// In my actual project class Person is a way different (But it's still a POJO)
// I included it here to make sure I can get it back after sending to the server
System.out.println("Enter a number:");
Person person = new Person(scState.nextInt());
sendRequest(request, person);
System.out.println("\nEnter request:");
request = scRequest.nextLine();
}
catch (Exception e) {
e.printStackTrace();
}
}
stop();
}
public void sendRequest(String sMessage, Person person) {
Message message = new Message(sMessage, person);
ByteBuffer requestBuffer = ByteBuffer.wrap(SerializationUtils.serialize(message));
try {
server.write(requestBuffer);
requestBuffer.clear();
getResponse();
}
catch (Exception e) {
e.printStackTrace();
// System.out.println(e.getMessage());
System.err.println("Connection lost");
System.exit(0);
}
}
public void getResponse() throws Exception {
ByteBuffer responseBuffer = ByteBuffer.allocate(1024 * 1024);
responseBuffer.clear();
int read = server.read(responseBuffer);
if (read == -1) {
throw new Exception("EOF, cannot read server response");
}
byte[] bytes = new byte[read];
responseBuffer.position(0);
responseBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
System.out.println(message);
}
public void stop() throws IOException {
server.close();
}
public static void main(String[] args) throws IOException {
Client client = new Client();
client.start();
}
}
import org.apache.commons.lang3.SerializationUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
public void start() throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress("localhost", 5454));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server started");
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
register(selector, serverSocket);
}
if (key.isReadable()) {
try {
getRequest(key);
}
catch (Exception e) {
e.printStackTrace();
// System.err.println(e.getMessage());
SocketChannel client = (SocketChannel) key.channel();
System.err.println("Closing client connection at: " + client.socket().getRemoteSocketAddress());
client.close();
}
}
iter.remove();
}
}
}
private void getRequest(SelectionKey key) throws Exception {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer requestBuffer = ByteBuffer.allocate(1024 * 1024);
requestBuffer.clear();
int read = client.read(requestBuffer);
if (read == -1) {
key.cancel();
throw new Exception("Client disconnected at: " +
((SocketChannel) key.channel()).socket().getRemoteSocketAddress());
}
byte[] bytes = new byte[read];
requestBuffer.position(0);
requestBuffer.get(bytes);
Message message = SerializationUtils.deserialize(bytes);
sendResponse(client, message);
}
private void sendResponse(SocketChannel client, Message message) throws IOException {
message.setResult("Some result");
ByteBuffer responseBuffer = ByteBuffer.wrap(SerializationUtils.serialize(message));
while (responseBuffer.hasRemaining()) {
client.write(responseBuffer);
}
responseBuffer.clear();
}
private void register(Selector selector, ServerSocketChannel serverSocket) throws IOException {
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("New client at: " + client.socket().getRemoteSocketAddress());
}
public static void main(String[] args) throws Exception {
new Server().start();
}
}
I am new to NIO and NIO2. I have been playing with an Echo server sample code, which works flawlessly. I started to write a similar client. My goal is to have multiple client socket connections running off the one main-thread.
I do get the OP_CONNECT, then after that the selector does not return and times out from the:
while(Selector.select(10000)>0) {
....
}
If I ignore the selector and start reading the data with socketChannel.read(...), I can read the data. So, the data is ready to be read, but I just do not get Selector.select(10000) to return with some keys.
Here is the complete source code and I really would appreciate any help:
package com.maker.webscraping.nio;
import java.io.IOException;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EchoClient2 {
private static final Logger logger = LoggerFactory.getLogger(EchoClient2.class);
private static final Map<SocketChannel, EchoClient2> mapClients = new ConcurrentHashMap<>();
private static final int DEFAULT_PORT = 5555;
private static final String IP = "127.0.0.1";
private final int clientID;
private final ByteBuffer buffer;
private final SocketChannel socketChannel;
private final CharsetDecoder decoder;
public int getClientID() {
return clientID;
}
public ByteBuffer getBuffer() {
return buffer;
}
public CharsetDecoder getDecoder() {
return decoder;
}
//private static Selector selector = null;
public static void main(String[] args) {
Selector selector = null;
try {
selector = Selector.open();
if (!selector.isOpen())
throw new RuntimeException("Selector closed!");
EchoClient2[] clients = new EchoClient2[2];
clients[0] = new EchoClient2(0, selector);
// wait for incomming events
while (selector.select(10000)>0) {
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
try (SocketChannel socketChannel = (SocketChannel) key.channel()) {
if (key.isConnectable()) {
// connected
logger.info("Client:{} connected!", clients[0].getClientID());
key.interestOps(SelectionKey.OP_READ); // <-- desprete tries
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); // <-- desprete tries
logger.info("R:{}, W:{}, C:{}, validOps:{}", SelectionKey.OP_READ, SelectionKey.OP_WRITE, SelectionKey.OP_CONNECT, socketChannel.validOps());
// close pending connections
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
read(key,selector); // <-- desprete tries
if (key.isReadable()) {
read(key,selector);
}// else if (key.isWritable()) {
// this.writeOP(key);
//}
}
} catch (IOException e) {
logger.error("SocketChannel Exception!", e);
}
}
}
} catch (IOException e) {
logger.error("Selector IOException!", e);
} finally {
try {
selector.close();
} catch (IOException e) {}
}
}
public EchoClient2(int clientID, Selector selector) throws IOException {
this.clientID = clientID;
buffer = ByteBuffer.allocateDirect(2 * 1024);
Charset charset = Charset.defaultCharset();
decoder = charset.newDecoder();
// if (selector==null)
// selector = Selector.open();
socketChannel = SocketChannel.open();
if ((socketChannel.isOpen()) && (selector.isOpen())) {
// configure non-blocking mode
socketChannel.configureBlocking(false);
// set some options
socketChannel.setOption(StandardSocketOptions.SO_RCVBUF,
128 * 1024);
socketChannel.setOption(StandardSocketOptions.SO_SNDBUF,
128 * 1024);
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE,
true);
socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//socketChannel.register(selector, SelectionKey.OP_CONNECT);
// connect to remote host
socketChannel.connect(new java.net.InetSocketAddress(IP, DEFAULT_PORT));
// add it to the map
mapClients.put(socketChannel, this);
} else
throw new RuntimeException("Channel or Selector closed!");
}
// isReadable returned true
private static int read(SelectionKey key, Selector selector) {
try {
SocketChannel socketChannel = (SocketChannel) key.channel();
EchoClient2 client = mapClients.get(socketChannel);
ByteBuffer buffer = client.getBuffer();
buffer.clear();
int numRead = -1;
try {
numRead = socketChannel.read(buffer);
} catch (IOException e) {
System.err.println("Cannot read error!");
}
if (numRead == -1) {
mapClients.remove(socketChannel);
System.out.println("Connection closed by: " + socketChannel.getRemoteAddress());
socketChannel.close();
key.cancel();
return 1;
}
if (numRead == 0)
throw new RuntimeException("numRead is 0!!!");
buffer.flip();
CharBuffer charBuffer = client.getDecoder().decode(buffer);
System.out.println("server says:" + charBuffer.toString());
if (buffer.hasRemaining()) {
buffer.compact();
} else {
buffer.clear();
}
int r = new Random().nextInt(100);
//if (r == 50) {
// System.out.println("50 was generated! Close the socket channel!");
// return 1;
//}
ByteBuffer randomBuffer = ByteBuffer.wrap("Random number:".concat(String.valueOf(r))
.getBytes("UTF-8"));
socketChannel.write(randomBuffer);
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); // <-- desprete tries
} catch (IOException e) {
logger.error("IOException inside read!", e);
}
return 0;
}
}
Thanks,
while (selector.select(10000)>0)
This code is already wrong. It will stop selecting the first time a select timeout occurs. It should be:
while (selector.isOpen())
{
if (selector.select(10000) > 0)
{
// ...
}
}
There are other problems.
You should only assume the connection is complete if finishConnect() returns true.
Reading when the connection is complete isn't valid. You should only read when key.isReadable() is true. You need a separate test for that, not one piggybacked onto the isConnectable() case.
Similarly you need a separate case for isWritable().
You shouldn't register OP_WRITE until you have something to write, or better still after you get a zero-length return when you try to write something. OP_WRITE signals that the socket send buffer isn't full. It is almost always true.
You need to call keys.remove() after keys.next(), otherwise you will keep getting the same selected keys over and over again. The selector doesn't clear the selected-key set.
Your comment // close pending connections is completely incorrect.
The following isConnectionPending() call is redundant. Of course it's pending, that's why you got the OP_CONNECT.
I got a problem when using Java nio to do networking.
I start a server in a thread and register read operationsin the selector. and another thread connect to the server.
client write a byte to the server
server reads the datas and back a byte to the client
client get the response and write a byte to the server again.
server loop step 2.
but server cannot do the step 4 successfully because I can't get any read Key from selector any more. why ?? I have registered it before, and client has written data.
there is my code below :
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class JavaNIO {
public static void main(String[] args) throws Exception {
final Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(5555));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
new Thread() {
public void run() {
while(true) {
try {
long count = selector.select();
if(count == 0) continue;
Iterator<SelectionKey> itKey = selector.selectedKeys().iterator();
while(itKey.hasNext()) {
SelectionKey key = itKey.next();
itKey.remove();
if(key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel)key.channel();
SocketChannel socket = channel.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable()) {
ByteBuffer buffer= ByteBuffer.allocate(1);
SocketChannel channel = (SocketChannel)key.channel();
while(true) {
buffer.flip();
int len = channel.read(buffer);
if(len == 0) break;
buffer.clear();
}
channel.read(buffer);
channel.register(selector, SelectionKey.OP_WRITE, null);
}
if(key.isWritable()) {
SocketChannel channel = (SocketChannel) key.channel();
channel.write(ByteBuffer.wrap("ce".getBytes()));
key.cancel();
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
try {
Socket socket = new Socket("localhost", 5555);
byte[] reads = new byte[1024];
for(int i = 0; i < 1000; i++) {
socket.getOutputStream().write(new byte[]{1});
socket.getInputStream().read(reads);
System.out.println(new String(reads));
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
You re-registered the channel for OP_WRITE, so its interestOps ceased to include OP_READ. If you want both, change accordingly. 'Or' them together.
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
public class EchoServer {
private InetAddress addr;
private int port;
private Selector selector;
private Map<SocketChannel,List<byte[]>> dataMap;
public EchoServer(InetAddress addr, int port) throws IOException {
this.addr = addr;
this.port = port;
dataMap = new HashMap<SocketChannel,List<byte[]>>();
startServer();
}
private void startServer() throws IOException {
// create selector and channel
this.selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// bind to port
InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
serverChannel.socket().bind(listenAddr);
serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
log("Echo server ready. Ctrl-C to stop.");
// processing
while (true) {
// wait for events
this.selector.select();
// wakeup to work on selected keys
Iterator keys = this.selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
// this is necessary to prevent the same key from coming up
// again the next time around.
keys.remove();
if (! key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
}
else if (key.isReadable()) {
this.read(key);
}
else if (key.isWritable()) {
this.write(key);
}
else if (key.isConnectable()) {
this.doConnect(key);
}
}
}
}
private void doConnect(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.finishConnect()) {
/* success */
System.out.println("Connected");
} else {
/* failure */
System.out.println("failure");
}
}
public void connect(String hostname, int port) throws IOException {
SocketChannel clientChannel = SocketChannel.open();
clientChannel.configureBlocking(false);
clientChannel.connect(new InetSocketAddress(hostname,port));
clientChannel.register(selector,SelectionKey.OP_CONNECT);
clientChannel.write(ByteBuffer.wrap(("$Hello "+UserInfo[0]+"|").getBytes("US-ASCII")));
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
// write welcome message
channel.write(ByteBuffer.wrap("Welcome, this is the echo server\r\n".getBytes("US-ASCII")));
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr);
dataMap.put(channel, new ArrayList<byte[]>()); // register channel with selector for further IO
channel.register(this.selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(8192);
int numRead = -1;
try {
numRead = channel.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
}
if (numRead == -1) {
this.dataMap.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr);
channel.close();
key.cancel();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
log("Got: " + new String(data, "US-ASCII"));
doEcho(key, data); // write back to client
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
Iterator<byte[]> items = pendingData.iterator();
while (items.hasNext()) {
byte[] item = items.next();
items.remove();
channel.write(ByteBuffer.wrap(item));
}
key.interestOps(SelectionKey.OP_READ);
}
private void doEcho(SelectionKey key, byte[] data) {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
pendingData.add(data);
key.interestOps(SelectionKey.OP_WRITE);
}
private static void log(String s) {
System.out.println(s);
}
public static void main(String[] args) throws Exception {
new EchoServer(null, 8989);
}
}
The program works with incoming connections. But when I make an outgoing connection, the program does not work. I need to make some connections in a row through the connect (String hostname, int port) and receive data in a method read(). The program stops working on the line clientChannel.register(...)
You need to check for a connectable key, e.g.
if (key.isConnectable()) {
this.doConnect(key);
}
...
private void doConnect(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.finishConnect()) {
/* success */
} else {
/* failure */
}
}
Use SocketChannel.finishConnect to determine whether the connection was established successfully.
This is my NIO Client example Ive been using. It gives a timed out open/write/read functions and is suitable for request-response messaging.
Tricky part is always how do parties recognize a packet is fully received. This example assumes
Server gets one_line_command+newline (client->server packet)
Client receives 1..n lines with ">>" terminator line without trailing newline in a terminator line (server->client packet)
you could specify terminator to be ">>\n" but readUntil needs small fix in a newline parser
You could write 4-byte length header, fixed size packet splitter or delimiter 0x27 byte but make sure it cannot be a value of data payload. NIO read() or write() never assumes you receive a full packet in one call. Or may read two or more packet bytes in one read() buffer. It is up to us make a packet parser not loosing bytes.
import java.util.*;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
public class DPSocket {
private boolean debug;
private String host;
private int port;
private String charset;
private ByteArrayOutputStream inBuffer;
private ByteBuffer buf;
private Selector selector;
private SocketChannel channel;
public DPSocket(String host, int port, String charset) {
this.charset = charset==null || charset.equals("") ? "UTF-8" : charset;
this.host = host;
this.port = port;
}
public boolean isDebug() { return debug; }
public void setDebug(boolean b) { debug=b; }
public void open(long timeout) throws IOException {
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress(host, port));
inBuffer = new ByteArrayOutputStream(1024);
buf = ByteBuffer.allocate(1*1024);
long sleep = Math.min(timeout, 1000);
while(timeout > 0) {
if (selector.select(sleep) < 1) {
timeout-=sleep;
continue;
}
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid() || !key.isConnectable()) continue;
SocketChannel channel = (SocketChannel)key.channel();
if (channel.isConnectionPending()) {
channel.finishConnect();
channel.configureBlocking(false);
if (debug) System.out.println("finishConnect");
return; // we are ready to receive bytes
}
}
}
throw new IOException("Connection timed out");
}
public void close() {
try { if(channel!=null) channel.close(); } catch(Exception ex) { }
try { if(selector!=null) selector.close(); } catch(Exception ex) { }
inBuffer=null;
buf=null;
}
public void write(String data, long timeout) throws IOException {
write(data.getBytes(charset), timeout);
}
public void write(byte[] bytes, long timeout) throws IOException {
ByteBuffer outBuffer = ByteBuffer.wrap(bytes);
channel.register(selector, SelectionKey.OP_WRITE);
long sleep = Math.min(timeout, 1000);
while(timeout > 0) {
if (selector.select(sleep) < 1) {
timeout-=sleep;
continue;
}
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid() || !key.isWritable()) continue;
SocketChannel channel = (SocketChannel)key.channel();
if (debug) System.out.println("write remaining="+outBuffer.remaining());
channel.write(outBuffer);
if (debug) System.out.println("write remaining="+outBuffer.remaining());
if (outBuffer.remaining()<1)
return;
}
}
throw new IOException("Write timed out");
}
public List<String> readUntil(String terminator, long timeout, boolean trimLines) throws IOException {
return readUntil(new String[]{terminator}, timeout, trimLines);
}
public List<String> readUntil(String[] terminators, long timeout, boolean trimLines) throws IOException {
List<String> lines = new ArrayList<String>(12);
inBuffer.reset();
// End of packet terminator strings, line startsWith "aabbcc" string.
byte[][] arrTerminators = new byte[terminators.length][];
int[] idxTerminators = new int[terminators.length];
for(int idx=0; idx < terminators.length; idx++) {
arrTerminators[idx] = terminators[idx].getBytes(charset);
idxTerminators[idx] = 0;
}
int idxLineByte=-1;
channel.register(selector, SelectionKey.OP_READ);
long sleep = Math.min(timeout, 1000);
while(timeout>0) {
if (selector.select(sleep) < 1) {
timeout-=sleep;
continue;
}
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid() || !key.isReadable()) continue;
SocketChannel channel = (SocketChannel)key.channel();
buf.clear();
int len = channel.read(buf);
if (len == -1) throw new IOException("Socket disconnected");
buf.flip();
for(int idx=0; idx<len; idx++) {
byte cb = buf.get(idx);
if (cb!='\n') {
idxLineByte++;
inBuffer.write(cb);
for(int idxter=0; idxter < arrTerminators.length; idxter++) {
byte[] arrTerminator = arrTerminators[idxter];
if (idxLineByte==idxTerminators[idxter]
&& arrTerminator[ idxTerminators[idxter] ]==cb) {
idxTerminators[idxter]++;
if (idxTerminators[idxter]==arrTerminator.length)
return lines;
} else idxTerminators[idxter]=0;
}
} else {
String line = inBuffer.toString(charset);
lines.add(trimLines ? line.trim() : line);
inBuffer.reset();
idxLineByte=-1;
for(int idxter=0; idxter<arrTerminators.length; idxter++)
idxTerminators[idxter]=0;
}
}
}
}
throw new IOException("Read timed out");
}
// **************************
// *** test socket client ***
// **************************
public static void main(String[] args) throws Exception {
String NEWLINE = "\n";
int TIMEOUT=5000;
DPSocket dps = new DPSocket("myserver.com", 1234, "UTF-8");
dps.setDebug(true);
try {
List<String> lines;
dps.open(15000);
dps.write("Command1 arg1 arg2"+NEWLINE, TIMEOUT);
lines = dps.readUntil(">>", TIMEOUT, true);
dps.write("Command2 arg1 arg2"+NEWLINE, TIMEOUT);
lines = dps.readUntil(">>", TIMEOUT, true);
} catch (Exception ex) {
String msg = ex.getMessage();
if (msg==null) msg = ex.getClass().getName();
if (msg.contains("timed out") || msg.contains("Invalid command ")) {
System.out.println("ERROR: " + ex.getMessage());
} else {
System.out.print("ERROR: ");
ex.printStackTrace();
}
} finally {
dps.close();
}
}
}