I am testing simple Apache Mina SSHD server using Jsch library. Junit test looks like:
package com.ssh;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.ssh.util.EchoShellFactory;
public class SshServiceTest {
#Rule
public TemporaryFolder testFolder = new TemporaryFolder();
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private static final String HOSTNAME = "localhost";
private SshServer sshd;
#Before
public void prepare() throws IOException {
setupSSHServer();
}
private void setupSSHServer() throws IOException {
sshd = SshServer.setUpDefaultServer();
sshd.setPort(22);
sshd.setKeyPairProvider(
new SimpleGeneratorHostKeyProvider(testFolder.newFile("hostkey.ser").getAbsolutePath()));
List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
userAuthFactories.add(new UserAuthPassword.Factory());
sshd.setUserAuthFactories(userAuthFactories);
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
public boolean authenticate(String username, String password, ServerSession session) {
return USERNAME.equals(username) && PASSWORD.equals(password);
}
});
sshd.setShellFactory(new EchoShellFactory());
sshd.start();
}
#Test
public void testConnection() throws Exception {
System.out.println("test started");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
Session session = jsch.getSession(USERNAME, HOSTNAME, 22);
session.setPassword(PASSWORD);
session.setConfig(config);
session.connect();
System.out.println("Connected");
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand("pwd");
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
System.out.print(new String(tmp, 0, i));
}
if (channel.isClosed()) {
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
session.disconnect();
System.out.println("DONE");
}
#After
public void cleanup() throws InterruptedException {
try {
sshd.stop(true);
} catch (Exception e) {
System.out.println(e);
}
}
}
And my shell factory looks like:
package com.ssh.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import org.apache.sshd.common.Factory;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
public class EchoShellFactory implements Factory<Command> {
public Command create() {
return new EchoShell();
}
public static class EchoShell implements Command, Runnable {
private InputStream in;
private OutputStream out;
private OutputStream err;
private ExitCallback callback;
private Environment environment;
private Thread thread;
public InputStream getIn() {
return in;
}
public OutputStream getOut() {
return out;
}
public OutputStream getErr() {
return err;
}
public Environment getEnvironment() {
return environment;
}
public void setInputStream(InputStream in) {
this.in = in;
}
public void setOutputStream(OutputStream out) {
this.out = out;
}
public void setErrorStream(OutputStream err) {
this.err = err;
}
public void setExitCallback(ExitCallback callback) {
this.callback = callback;
}
public void start(Environment env) throws IOException {
environment = env;
thread = new Thread(this, "EchoShell");
thread.start();
}
public void destroy() {
thread.interrupt();
}
public void run() {
BufferedReader r = new BufferedReader(new InputStreamReader(in));
try {
for (;;) {
String s = r.readLine();
if (s == null) {
return;
}
out.write(("executed " + s + "\r\n").getBytes());
out.flush();
if ("exit".equals(s)) {
return;
}
}
} catch (InterruptedIOException e) {
// Ignore
} catch (Exception e) {
System.out.println("Error executing EchoShell...");
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
// ignore
}
}
callback.onExit(0);
}
}
}
}
In this case value of in.available() is always "0" and while look runs forever. I have to stop the process manually. When SSHD server is up, I can connect to it through putty and execute dummy commands. Only when I try to connect to this server using Jsch lib, input stream as shown above is always 0. I searched at many places, but couldn't find whats wrong. Please let me know, if you have any idea on how to make it work.
Thanks in advance.
I solved this problem. Because the "exec" mode is not interactive, the program thinks the execution is not finished. So you need to use the "shell" mode instead of "exec", but you need to add "exit" or "quit" command after your command to end the interaction.
Related
When I'm running a Java WebSocketStompClient, I got below error:
org.eclipse.jetty.websocket.api.MessageTooLargeException: Text message size [73728] exceeds maximum size [65536]
Sample code:
import org.apache.log4j.Logger;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class HelloClient {
private static Logger logger = Logger.getLogger(HelloClient.class);
StompSession session;
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
public ListenableFuture<StompSession> connect() {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
long[] hb = stompClient.getDefaultHeartbeat();
boolean en = stompClient.isDefaultHeartbeatEnabled();
long timeout = stompClient.getReceiptTimeLimit();
String url = "https://www.test.com";
return stompClient.connect(url, headers, new MyHandler());
}
public void subscribeMsg(StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe("/topic/test", new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
logger.info("Received message " + new String((byte[]) o));
String response = new String((byte[]) o);
}
});
}
private class MyHandler extends StompSessionHandlerAdapter {
public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {
logger.info("Now connected");
session = stompSession;
}
}
public boolean isConnected() {
try {
Thread.sleep(500);
return session != null && session.isConnected();
} catch (Exception e) {
logger.warn("Error happens when checking connection status, ", e);
return false;
}
}
public static void main(String[] args) throws Exception {
HelloClient helloClient = new HelloClient();
ListenableFuture<StompSession> f = helloClient.connect();
StompSession stompSession = f.get();
helloClient.subscribeMsg(stompSession);
while (true) {
if (!helloClient.isConnected()) {
logger.info("wss diconnected ");
logger.info("need re-create ");
}
}
}
}
How to increase the limitation for a Java stomp websocket client? I found some not related answers How can I set max buffer size for web socket client(Jetty) in Java which are not suitable for stomp websocket client.
Also tried stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE); which doesn't work.
I am trying to read some packets from a port 2020 in my server. I have run tcpdump -r eth1 port 2020 and can find that udp packets are coming into the port. Following is the screenshot :
But when i try to read those packets from the java code, i do not get any data. Following is my java code :
package packetreceiver;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import main.Main;
import propertyfilereader.PropertyFileReader;
import org.apache.log4j.Logger;
import com.sun.jmx.snmp.SnmpMessage;
import alarmprocessor.AlarmProcessor;
public class UDPPacketReceiver extends Thread {
private DatagramSocket socket = null;
private Logger logger = Logger.getLogger(UDPPacketReceiver.class);
public boolean dynamicKey = false;
private UDPPacketReceiver() {
try {
logger.debug(" port : " + Main.orgPort);
logger.debug(" ip : " + Main.orgBindIPStr);
logger.debug("creating socket");
socket = new DatagramSocket(null);
//socket = new DatagramSocket(38567);
InetSocketAddress address = new InetSocketAddress(2020);
socket.bind(address);
logger.debug("Receiver Socket created successfully");
} catch (Exception e) {
logger.fatal("Exception at creating socket ", e);
logger.fatal("Exiting successfully ");
System.exit(0);
}
this.running = true;
}
static UDPPacketReceiver instance = null;
public static UDPPacketReceiver getInstance() {
if (instance == null) {
createInstance();
}
return instance;
}
boolean running = false;
private static synchronized UDPPacketReceiver createInstance() {
if (instance == null) {
instance = new UDPPacketReceiver();
}
return instance;
}
public void run() {
byte[] data = new byte[2048];
DatagramPacket packet = new DatagramPacket(data, data.length);
while (this.running) {
try {
logger.debug("waiting for received data");
this.socket.receive(packet);
int length = packet.getLength();
logger.debug("length:"+length);
logger.debug("data:"+new String(packet.getData()));
} catch (Exception e) {
if ((this.socket == null) || (this.socket.isClosed())) {
this.running = false;
} else {
this.logger.fatal("Error in receiving UDP packet:", e);
}
}
}
}
public void shutdown() {
this.running = false;
try {
if ((this.socket != null) && (!this.socket.isClosed())) {
this.socket.close();
}
} catch (Exception localException) {
}
this.socket = null;
}
}
I have searched in stackeoverflow and other posts but could not find any solution. Can you please help me.
Regards,
Tanvir
Check with the netstat command to see whether your application is actually bound to the correct IP/interface:
> sudo netstat -tunlp | grep 2020
works in linux.
You can also change this line:
InetSocketAddress address = new InetSocketAddress(172.22.49.116, 2020);
I want to create a server that can accept multiple connections and then bind 2 clients as a pair and forward the data between these 2 clients. But it is about multiple pairs of clients. I already have multithread server that can create a new thread for each new connected client. The problem for me is that these threads dont know of each other and somehow I have to connect 2 clients to a connection pair.
For now I just create these pair connection as this: I wait for the first client, then I wait for the second client and then open a thread for the input of client 1 that gets forwarded to client 2 and the other way around. This is not usable for multiple clients.
How can I do this decent?
The way I see it, a client would need to
establish a TCP(?) connection with your server,
identify itself
give the ID of the other client it wishes to talk to
The first that connects would have to be kept on hold (in some global table in your server) until the second client connects.
Once a pair of clients would have been recognized as interlocutors, you would create a pair of threads to forward the data sent by each client to the other one.
UPDATE: Example
ClientSocket.java
package matchmaker;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ClientSocket implements Closeable {
private final Socket socket;
private final InputStream in;
private final OutputStream out;
private final String ownId;
private final String peerId;
public ClientSocket(Socket socket) throws IOException {
this.socket = socket;
this.in = socket.getInputStream();
this.out = socket.getOutputStream();
DataInputStream din = new DataInputStream(in);
this.ownId = din.readUTF();
this.peerId = din.readUTF();
}
public ClientSocket(String server, int port, String ownId, String peerId)
throws IOException {
this.socket = new Socket(server, port);
this.socket.setTcpNoDelay(true);
this.in = socket.getInputStream();
this.out = socket.getOutputStream();
this.ownId = ownId;
this.peerId = peerId;
DataOutputStream dout = new DataOutputStream(out);
dout.writeUTF(ownId);
dout.writeUTF(peerId);
}
public String getOwnId() {
return ownId;
}
public String getPeerId() {
return peerId;
}
public InputStream getInputStream() {
return in;
}
public OutputStream getOutputStream() {
return out;
}
#Override
public void close() throws IOException {
socket.close();
}
}
Matchmaker.java: the server
package matchmaker;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Matchmaker extends Thread {
private static final Logger LOG
= Logger.getLogger(Matchmaker.class.getName());
private final int port;
private final Map<ClientPair,ClientSocket> waiting = new HashMap<>();
public static void main(String[] args) {
try {
int port = 1234;
int st = 0;
for (String arg: args) {
switch (st) {
case 0:
switch (arg) {
case "-p":
st = 1;
break;
default:
System.out.println("Unknown option: " + arg);
return;
}
break;
case 1:
port = Integer.parseInt(arg);
st = 0;
break;
}
}
Matchmaker server = new Matchmaker(port);
server.start();
server.join();
} catch (InterruptedException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
private Matchmaker(int port) {
this.port = port;
setDaemon(true);
}
#Override
public void run() {
try {
ServerSocket server = new ServerSocket(port);
while (true) {
ClientSocket socket = new ClientSocket(server.accept());
ClientPair pair = new ClientPair(
socket.getOwnId(), socket.getPeerId());
ClientSocket other;
synchronized(this) {
other = waiting.remove(pair.opposite());
if (other == null) {
waiting.put(pair, socket);
}
}
if (other != null) {
LOG.log(Level.INFO, "Establishing connection for {0}",
pair);
establishConnection(socket, other);
} else {
LOG.log(Level.INFO, "Waiting for counterpart {0}", pair);
}
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
private void establishConnection(ClientSocket socket, ClientSocket other)
throws IOException {
Thread thread = new StreamCopier(
socket.getInputStream(), other.getOutputStream());
thread.start();
thread = new StreamCopier(
other.getInputStream(), socket.getOutputStream());
thread.start();
}
}
StreamCopier.java: a thread that reads from an InputStream and writes to an OutputStream
package matchmaker;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class StreamCopier extends Thread {
private static final Logger LOG
= Logger.getLogger(StreamCopier.class.getName());
private final InputStream in;
private final OutputStream out;
public StreamCopier(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
setDaemon(true);
}
#Override
public void run() {
LOG.info("Start stream copier");
try {
for (int b = in.read(); b != -1; b = in.read()) {
out.write(b);
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
} finally {
LOG.info("End stream copier");
try {
out.close();
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
}
ClientPair.java: a pair of client IDs
package matchmaker;
public class ClientPair {
private final String client1;
private final String client2;
public ClientPair(String client1, String client2) {
this.client1 = client1;
this.client2 = client2;
}
public String getClient1() {
return client1;
}
public String getClient2() {
return client2;
}
public ClientPair opposite() {
return new ClientPair(client2, client1);
}
#Override
public int hashCode() {
int hash = 5;
hash = 73 * hash + client1.hashCode();
hash = 73 * hash + client2.hashCode();
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ClientPair other = (ClientPair) obj;
return client1.equals(other.client1) && client2.equals(other.client2);
}
#Override
public String toString() {
return "[" + client1 + "," + client2 + "]";
}
}
ReaderClient.java: a sample client that reads from the socket and writes to standard output
package matchmaker;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ReaderClient {
private static final Logger LOG = Logger.getLogger(ReaderClient.class.getName());
public static void main(String[] args) {
try (ClientSocket client
= new ClientSocket("localhost", 1234, "reader", "writer")) {
Reader reader
= new InputStreamReader(client.getInputStream(), "UTF-8");
BufferedReader in = new BufferedReader(reader);
for (String s = in.readLine(); s != null; s = in.readLine()) {
System.out.println(s);
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
WriterClient.java: a sample client that writes to the socket
package matchmaker;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WriterClient {
private static final Logger LOG = Logger.getLogger(ReaderClient.class.getName());
public static void main(String[] args) {
try (ClientSocket client
= new ClientSocket("localhost", 1234, "writer", "reader")) {
Writer writer
= new OutputStreamWriter(client.getOutputStream(), "UTF-8");
PrintWriter out = new PrintWriter(writer);
for (int i = 0; i < 30; ++i) {
out.println("Message line " + i);
}
out.flush();
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
I am using httpcore libraries 4.3.2 to make a custom http server to communicate with some clients (android devices).
The clients manage to communicate (post an xml) with the server but the response of the server is very slow.
My code is based on the ElementalHttpServer of apache samples. Can i find somewhere else better samples for the httpcore libraries?
Also i can not find how to set the parameteres or to finetune the httpserver because the HttParams lib is deprecated.
If i use the com.sun.net.httpserver.HttpServer httpserver my clients are connecting without problem. It is better to stay with suns libs or to use apaches?
Thanks in advance.
This is the code i used:
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.ssl.SSLServerSocketFactory;
import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpConnectionFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpServerConnection;
import org.apache.http.HttpStatus;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultBHttpServerConnection;
import org.apache.http.impl.DefaultBHttpServerConnectionFactory;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpProcessorBuilder;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.protocol.UriHttpRequestHandlerMapper;
import org.apache.http.util.EntityUtils;
public class Thread_Main_httpcore2 implements Runnable {
public Thread_Main_httpcore2() {
}//Thread_Main()
#Override
public void run() {
int port = 20010;
// Set up the HTTP protocol processor
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new ResponseDate())
.add(new ResponseServer("Test/1.1"))
.add(new ResponseContent())
.add(new ResponseConnControl()).build();
// Set up request handlers
UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper();
//// Set up the HTTP service
HttpService httpService = new HttpService(httpproc, reqistry);
reqistry.register("*", new Thread_Main_httpcore2.HttpReqHandler());
SSLServerSocketFactory sf = null;
try {
Thread t = new RequestListenerThread(port, httpService, sf);
t.setDaemon(false);
t.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
static class HttpReqHandler implements HttpRequestHandler {
public HttpReqHandler() {
super();
}
public void handle(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
String xml_stream="";
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
byte[] entityContent;
if (entity == null) {
entityContent = new byte[0];
} else {
entityContent = EntityUtils.toByteArray(entity);
}
//byte[] entityContent = EntityUtils.toByteArray(entity);
System.out.println("Incoming entity content (bytes): " + entityContent.length);
xml_stream = new String(entityContent);
}
StringBuilder resp_xml = new StringBuilder();
resp_xml.append("Returns an xml to the client");
StringEntity entity;
try {
response.setStatusCode(HttpStatus.SC_OK);
entity = new StringEntity(resp_xml.toString(), ContentType.create("text/html", "UTF-8"));
response.setEntity(entity);
} catch (Exception ex_send_resp) {
System.out.println("HttpFileHandler.ex_send_resp "+ ex_send_resp.toString());
}
}
}
static class RequestListenerThread extends Thread {
private final HttpConnectionFactory<DefaultBHttpServerConnection> connFactory;
private final ServerSocket serversocket;
private final HttpService httpService;
public RequestListenerThread(
final int port,
final HttpService httpService,
final SSLServerSocketFactory sf) throws IOException {
this.connFactory = DefaultBHttpServerConnectionFactory.INSTANCE;
this.serversocket = sf != null ? sf.createServerSocket(port) : new ServerSocket(port);
this.httpService = httpService;
}
#Override
public void run() {
System.out.println("Listening on port " + this.serversocket.getLocalPort());
while (!Thread.interrupted()) {
try {
// Set up HTTP connection
Socket socket = this.serversocket.accept();
System.out.println("Incoming connection from " + socket.getInetAddress());
HttpServerConnection conn = this.connFactory.createConnection(socket);
// Start worker thread
Thread t = new WorkerThread(this.httpService, conn);
t.setDaemon(true);
t.start();
} catch (InterruptedIOException ex) {
break;
} catch (IOException e) {
System.err.println("I/O error initialising connection thread: "
+ e.getMessage());
break;
}
}
}
}
static class WorkerThread extends Thread {
private final HttpService httpservice;
private final HttpServerConnection conn;
public WorkerThread(
final HttpService httpservice,
final HttpServerConnection conn) {
super();
this.httpservice = httpservice;
this.conn = conn;
}
#Override
public void run() {
System.out.println("New connection thread");
HttpContext context = new BasicHttpContext(null);
try {
while (!Thread.interrupted() && this.conn.isOpen()) {
this.httpservice.handleRequest(this.conn, context);
}
} catch (ConnectionClosedException ex) {
System.err.println("Client closed connection");
} catch (IOException ex) {
System.err.println("I/O error: " + ex.getMessage());
} catch (HttpException ex) {
System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
} finally {
try {
this.conn.shutdown();
} catch (IOException ignore) {
}
}
}
}
}
I am working on a socket server for a MMORPG I am creating, and I am unsure how to organize commands and handlers for readability.
I will be getting the command from data[0], such as ban, kick, identify, etc. I have read that you can use a HashMap and have an Interface Command, and create a new class for each handler/command that implements Command. You'd then create a HashMap and go from there. I have also read that you can reflection. I want to do what an experienced programmer would do.
Client Class:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client implements Runnable {
private Socket socket;
private Server server;
private boolean isConnected;
private BufferedReader in;
private PrintWriter out;
public Client(Socket socket, Server server) {
this.socket = socket;
this.server = server;
this.isConnected = true;
this.in = null;
this.out = null;
}
#Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
while (isConnected) {
String recv = in.readLine().trim();
if (recv != null) {
String[] data = recv.split("%");
}
}
} catch (IOException e) {}
}
public synchronized void send(String data) {
out.println(data);
}
}
What you want is to use the Command Pattern.
Here is how I would use the Command Pattern for handling commands from a client.
/* Command Exception. */
public class ClientCommandException extends Exception {
public ClientCommandException(String msg) {
super(msg);
}
}
/* The ClientCommand interface */
public interface ClientCommand {
void execute(Client client, String[] params) throws ClientCommandException;
}
import java.util.HashMap;
import java.util.Arrays;
/* Container for commands */
public class Commands implements ClientCommand {
private HashMap<String, ClientCommand> cmds;
public Commands() {
cmds = new HashMap<String, ClientCommand>();
}
public void addCommand(ClientCommand cmd, String name) {
cmds.put(name, cmd);
}
public void execute(Client client, String[] params) throws ClientCommandException {
ClientCommand cmd = cmds.get(params[0]);
if(cmd != null) {
cmd.execute(client, Arrays.copyOfRange(params, 1, params.length));
} else {
throw new ClientCommandException("Unknown Command: " + params[0]);
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class Client implements Runnable {
private boolean isConnected;
private Commands cmds;
private BufferedReader in;
private PrintWriter out;
private class EchoCommand implements ClientCommand {
public void execute(Client client, String[] params) throws ClientCommandException {
StringBuilder b = new StringBuilder("Echo back:");
int len = params.length;
for(int i = 0; i < len; i++) {
b.append(' ');
b.append(params[i]);
}
client.send(b.toString());
}
}
private class DisconnectCommand implements ClientCommand {
public void execute(Client client, String[] params) throws ClientCommandException {
client.close();
}
}
public Client() {
cmds = new Commands();
cmds.addCommand(new EchoCommand(), "echo");
cmds.addCommand(new DisconnectCommand(), "disconnect");
/* sub-commands */
Commands server = new Commands();
server.addCommand(new EchoCommand(), "print");
cmds.addCommand(server, "server");
isConnected = true;
}
public void addCommand(ClientCommand cmd, String name) {
cmds.addCommand(cmd, name);
}
public void close() {
isConnected = false;
}
#Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(System.in));
out = new PrintWriter(System.out, true);
while (isConnected) {
String recv = in.readLine().trim();
if (recv != null) {
String[] data = recv.split("%");
try {
cmds.execute(this, data);
} catch(ClientCommandException e) {
/* Return some error back to the client. */
out.println(e.toString());
}
}
}
} catch (IOException e) {}
}
public synchronized void send(String data) {
out.println(data);
}
public static void main(String[] args){
Client client = new Client();
System.out.println("Start Client.");
client.run();
}
}