I currently have a problem while working with Mina. I am able to create a NIOAcceptor and Connector and connect the client to the server. Upon session being created in the Server, it sends the handshake packet which in turn validates and sends back validation to the server to see if files are up-to-date, etc. The server receives this validation and correctly deciphers the packet and sends the packet to the Client to display the game window. However, after this initial connection, I can no longer send packets to the server via the Client.
ServerHandler:
#Override
public void sessionOpened(IoSession session) {
log.info("[Login] to [" + GameConstants.GAME_NAME + ": IoSession with {} opened", session.getRemoteAddress());
Client c = new Client(session);
connectedClients.add(session.getRemoteAddress().toString());
session.setAttribute(Client.KEY, c);
c.write(PacketCreator.getHandshake());
// c.write(PacketCreator.getPing());
}
#Override
public void messageReceived(IoSession session, Object message) {
PacketReader reader = new PacketReader((byte[]) message);
Client c = (Client) session.getAttribute(Client.KEY);
short header = reader.readShort();
PacketHandler handler = PacketProcessor.getHandler(header);
System.out.println("Received opcode: 0x" + Integer.toHexString(header).toUpperCase());
if (handler != null) {
handler.handlePacket(reader, c);
} else {
log.info("Received opcode: 0x" + Integer.toHexString(header).toUpperCase() + " with no handler.");
}
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
System.out.println("session error");
}
#Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("Session closing: " + session.getRemoteAddress().toString());
connectedClients.remove(session.getRemoteAddress().toString());
Client c = (Client) session.getAttribute(Client.KEY);
if (c != null) {
c.disconnect();
c.dispose();
} else {
log.warn("Client is null in sessionClosed for ip {} when it shouldn't be", session.getRemoteAddress());
}
super.sessionClosed(session);
}
ClientHandler:
#Override
public void sessionOpened(IoSession session) {
System.out.println("Session opened: " + session);
Server s = new Server(session);
session.setAttribute(Server.KEY, s);
s.write(PacketCreator.getPong());
}
#Override
public void messageReceived(IoSession session, Object message) {
PacketReader reader = new PacketReader((byte[]) message);
Server s = (Server) session.getAttribute(Server.KEY);
short header = reader.readShort();
PacketHandler handler = PacketProcessor.getHandler(header);
if (handler != null) {
handler.handlePacket(reader, s);
} else {
log.info("Received opcode: 0x" + Integer.toHexString(header).toUpperCase() + " with no handler.");
}
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
System.out.println("session error");
log.error("Exception caught in Server Handler: ", cause);
}
#Override
public void sessionClosed(IoSession session) throws Exception {
// TODO
System.out.println("session closed");
super.sessionClosed(session);
}
Client (NIOConnection class):
public static void connectToServer() throws Throwable {
NioSocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(1000 * 30); // 30 seconds
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
connector.setHandler(new ClientHandler());
IoSession session;
long startTime = System.currentTimeMillis();
for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress("127.0.0.1", 9494)); // 24.7.142.74
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
log.error("Failed to connect", e);
Thread.sleep(5000);
}
}
session.getCloseFuture().awaitUninterruptibly();
}
Server (NIOAcceptor class):
private static void initializeLoginServer() {
PacketProcessor.registerHandlers();
acceptor = new NioSocketAcceptor();
// acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));// TODO: encoding/decoding packets
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
acceptor.getSessionConfig().setTcpNoDelay(true);
acceptor.setHandler(new ServerHandler(1));
try {
acceptor.bind(new InetSocketAddress(GameConstants.SERVER_PORT));
} catch (Exception e) {
log.error("Could not bind. ", e);
}
log.info("Login Server: Listening on port " + GameConstants.SERVER_PORT);
}
Related
I am migrating an enterprise-level application based on JBOSS EAP 7.3 using the WebSocket Servlets to IBM WebSphere Application Server 9.0.5.6 (WAS). The problem is that when I try to connecting to WebSocket endpoint it does nothing. In WebSphere, I have configured all virtual hosts and ports accordingly and my WebSocket endpoint class looks like below.
#ServerEndpoint("/WebSocketServices")
public class ClientConnectorWebSocketCore {
private static final OMSLogHandlerI logger = new Log4j2HndlAdaptor("ClientConnectorWebSocketCore");
private ClientConnectorFacadeWrapperRemote clientConnectorFacadeRemote;
private EJBServiceLocatorI ejbServiceLocatorI;
#OnOpen
public void onConnectionOpen(Session session) {
session.setMaxIdleTimeout(120000); //todo //milli seconds
getEjbServiceLocatorI();
logger.elkLog("29", LogEventsEnum.WSOCKET_SESSION_CONNECT, " Session Create:" + session.getId());
}
#OnMessage
public String onMessage(String message) {
return handleJSONRequest(message);
}
#OnClose
public void onConnectionClose(Session session) {
logger.elkLog("42", LogEventsEnum.WSOCKET_SESSION_CLOSE, " Session Close:" + session.getId());
}
#OnError
public void onConnectionError(Session session, Throwable t) {
// logger.info("LN:47", session.getId(), LogEventsEnum.WEB_SOCKET_ONERROR, "WebSocket OnException" + t.getMessage());
logger.elkLog("48", LogEventsEnum.WEB_SOCKET_ONERROR, " Session error:" + session.getId() + ", Msg:" + t.getMessage());
}
public void msgBroadCast(Session session, String msg) {
for (Session session1 : session.getOpenSessions()) {
if (session1.isOpen()) {
session1.getAsyncRemote().sendText(msg);
}
}
}
private EJBServiceLocatorI getEjbServiceLocatorI(){
if (ejbServiceLocatorI == null){
ejbServiceLocatorI =
(EJBServiceLocatorI) SpringBeanFactoryLoader.getInstance().getBeanLoader().getBean(EJBServiceLocatorI.class);
}
return ejbServiceLocatorI;
}
private ClientConnectorFacadeWrapperRemote getClientConnectFacade() {
if (clientConnectorFacadeRemote == null) {
try {
ejbServiceLocatorI = getEjbServiceLocatorI();
clientConnectorFacadeRemote =
(ClientConnectorFacadeWrapperRemote) ejbServiceLocatorI.contextLookupConnectorFacadeRemote(ClientConnectorFacadeWrapperRemote.class);
} catch (Exception e) {
logger.error("LN:66", "Error in Creating Client connector " + e.getMessage(), e);
}
}
return clientConnectorFacadeRemote;
}
private String handleJSONRequest(String jsonRequest) {
ClientConnectorFacadeWrapperRemote clientConnector = getClientConnectFacade();
String response = null;
if (clientConnector != null) {
response = clientConnector.processMessage(jsonRequest);
}
return response;
}
#OnMessage
public void pongMessage(Session session, PongMessage msg) {
msg.getApplicationData().toString();
}
public void setEjbServiceLocatorI(EJBServiceLocatorI ejbServiceLocatorI) {
this.ejbServiceLocatorI = ejbServiceLocatorI;
}
}
Any advice is highly appreciated.
If your requests flow through a web server with WebSphere plugin, you need to ensure plugin is at level 9.0.5.6 or higher due to bug (PH27966) which can block websocket traffic.
I have the following class. I'm trying to have the WebSocket reconnect in case of failure
public class WebSocketClient extends WebSocketListener {
volatile OkHttpClient client;
volatile WebSocket webSocket;
volatile Boolean isConnected = false;
public WebSocketClient() {
Proxy proxy = null;
if (Main.useProxy) {
tinder.CustomProxy proxyCustom = ProxyManager.GetStaticProxy(ThreadLocalManager.account.get().getProxyId());
proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
}
client = new OkHttpClient.Builder().proxy(proxy).readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
isConnected = true;
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("Text MESSAGE: " + text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
System.out.println("CLOSE: " + code + " " + reason);
isConnected = false;
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
isConnected = false;
AnsiConsole.out
.println(Ansi.ansi().fg(Ansi.Color.RED).a("Socket connection failed! will try to reconnect").reset());
while (!isConnected) {
try {
AnsiConsole.out
.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Waiting to try socket connection!").reset());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
if (isConnected) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
}
}
public void close() {
if (webSocket != null) {
webSocket.close(1000, "Connection closed");
}
client.dispatcher().executorService().shutdown();
}
}
The problem is if it takes a few attempts to reconnect, then the onFailure method will get called multiple times. Causing multiple web socket connections instead of one.
How can I have the single connection reconnect when the websocket disconnects?
public class WebSocketClient extends WebSocketListener {
volatile OkHttpClient client;
volatile WebSocket webSocket;
volatile Boolean isConnected = false;
public WebSocketClient() {
Proxy proxy = null;
if (Main.useProxy) {
tinder.CustomProxy proxyCustom = ProxyManager.GetStaticProxy(ThreadLocalManager.account.get().getProxyId());
proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
}
client = new OkHttpClient.Builder().proxy(proxy).readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
// First Change
client.connectionPool.evictAll();
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
AnsiConsole.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Socket connection successful").reset());
isConnected = true;
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("Text MESSAGE: " + text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
System.out.println("CLOSE: " + code + " " + reason);
isConnected = false;
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// Second Change
webSocket.close(1000, null);
close();
Thread.sleep(10000);
Request request = new Request.Builder().url("wss://echo.com/ws")
.addHeader("Accept-Language", "en").build();
webSocket = client.newWebSocket(request, this);
}
public void close() {
if (webSocket != null) {
webSocket.close(1000, "Connection closed");
}
}
}
For multiple idle connection client provide a connectionPool
client.connectionPool().evictAll();
the evictAll() method evicts all the connections.
I read the documentation of IdleStateHandlerand from my Server I implemented it same as from the documentation,
but I don't understand on how can I exactly tell if the Client become disconnected for example the Client loses the Wifi Connectivity.
from my understanding, inside my Handler, the method channelInactive() was trigger when the client become disconnected,
then using IdleStateHandler, the IdleState.READER_IDLE will be triggered when no read was performed for the specified period of time,
then after 3 seconds of no read from the client I closed the channel and was expecting that the channelInactive will be trigger but it's not, why?.
Initializer
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
String TAG = "LOG: ";
#Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println(TAG + "Starting ServerInitializer class...");
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("idleStateHandler", new IdleStateHandler(6, 3, 0, TimeUnit.SECONDS));
pipeline.addLast("handler", new ServerHandler());
}
}
Handler
public class ServerHandler extends ChannelInboundHandlerAdapter {
private String TAG = "LOG: ";
public ServerHandler(){}
#Override
public void channelActive(ChannelHandlerContext ctx) {
Log.w(TAG,"New Client become connected, Sending a message to the Client. Client Socket is: " + ctx.channel().remoteAddress().toString());
List<String> msg = new ArrayList<>();
msg.add(0,"sample message 1");
msg.add(1,"sample message 2");
sendMessage(ctx, msg);
}
public void sendMessage(ChannelHandlerContext ctx, List message){
ctx.write(message);
ctx.flush();
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
Log.w(TAG,"A Client become disconnected. Client Socket is: " + ctx.channel().remoteAddress().toString() + " id: " + (String.valueOf(ctx.channel().hashCode())));
//COnnection id dead, do something here...
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object object) { // (2)
Log.w(TAG, "CLIENT: "+ ctx.channel().remoteAddress().toString() + " SAYS: " + object);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection for that client when an exception is raised.
Log.e(TAG,"Something's wrong, CLIENT: "+ ctx.channel().remoteAddress().toString() + " CAUSE: " + cause.toString());
ctx.close();
}
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
Log.w(TAG,"LOLO");
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
ctx.close(); //Closed the Channel so that the `channelInactive` will be trigger
} else if (e.state() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush("ping\n"); //Send ping to client
}
}
}
}
Anyone can help me out
IdleStateHandler should always be the first handler in your pipeline.
Use the ReadTimeoutHandler instead of IdleStateHandler and override the exceptionCaught method.
I have a test which should connect and subscribe multiple users to a websocket endpoint. The test connects the user but doesn't subscribe the users.
This is how I try to connect the users:
List<ConsumerStompSessionHandler> consumers = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_USERS; i++) {
consumers.add(new ConsumerStompSessionHandler(BROADCAST_MESSAGE_COUNT, connectLatch, subscribeLatch,
messageLatch, disconnectLatch, failure));
stompClient.connect(stompUrl, consumers.get(i), host, port);
}
if (failure.get() != null) {
throw new AssertionError("Test failed", failure.get());
}
if (!connectLatch.await(5000, TimeUnit.MILLISECONDS)) {
fail("Not all users connected, remaining: " + connectLatch.getCount());
}
if (!subscribeLatch.await(5000, TimeUnit.MILLISECONDS)) {
fail("Not all users subscribed, remaining: " +
subscribeLatch.getCount());
}
stopWatch.stop();
logger.debug("Finished: " + stopWatch.getLastTaskTimeMillis() + " millis");
The afterConnect method, which I think it is not executed:
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders) {
this.connectLatch.countDown();
session.setAutoReceipt(true);
session.subscribe("/topic/greetings", new StompFrameHandler() {
public Type getPayloadType(StompHeaders headers) {
return String.class;
}
public void handleFrame(StompHeaders headers, Object payload) {
if (messageCount.incrementAndGet() == expectedMessageCount) {
messageLatch.countDown();
disconnectLatch.countDown();
session.disconnect();
}
}
}).addReceiptTask(new Runnable() {
#Override
public void run() {
subscribeLatch.countDown();
}
});
}
I also tried to put a Thread.sleep(5000) in the for after the connection but it didn't work.
Can anyone tell me why it doesn't subscribe.
The subscribe url is the same from #Controller -> #SendTo("/topic/greetings").
This is the piece of code I am facing issues with. When the post construct gets called..the source gets set .. But when I receive a message from server and the processBinaryMessage method gets called..the source turns out to be null.
I don't understand the problem...Any help is appreciated..
#ClientEndpoint
#Component
public class MyClientEndpoint {
#Inject
private Source Source;
private boolean postConstructCalled = false;
#PostConstruct
public void init() {
postConstructCalled = true;
System.out.println("Post construct called ... ");
}
#OnOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getBasicRemote());
SessionUtil.setSession(session);
try {
System.out.println("Checking the established connection........");
session.getBasicRemote().sendText("Checking the connection...");
} catch (IOException ex) {
Logger.getLogger(MyClientEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}
#OnMessage
public void processMessage(String message) {
System.out.println("Received string message in client: " + message);
}
#OnMessage
public void processBinaryMessage(byte[] bytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byte[] type = new byte[3];
byteBuffer.get(type);
String msgType = new String(type);
//System.out.println("Read type : " + msgType);
if(msgType.compareTo("MAX") == 0) {
int maxLimit = byteBuffer.getInt();
System.out.println("Read max blocks : " + maxLimit);
ClientDataTransfer.setMaxTransferLimit(maxLimit);
}
else if(msgType.compareTo("NXT") == 0) {
ClientDataTransfer.incMaxTransferLimit();
}
else if(msgType.compareTo("MAP") == 0) {
int length = byteBuffer.getInt();
byte[] data = new byte[length];
byteBuffer.get(data);
source.getClientDataTransfer().addToDataRequestQueue(data);
}
}
#OnError
public void processError(Throwable t) {
t.printStackTrace();
}
#OnClose
public void onClose(Session session) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Closing connection to endpoint: " + session.getBasicRemote());
}
}
Thanks,
Sreeja