I use netty as socket client:
public void run() {
isRunning = true;
EventLoopGroup group = new NioEventLoopGroup(EventLoopsPerGetter);
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(
new ProtobufVarint32FrameDecoder(),
ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP),
new ProtobufDecoder(Protocol.Packet.getDefaultInstance()),
new ProtobufVarint32LengthFieldPrepender(),
ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP),
new ProtobufEncoder(),
session
);
}
});
try {
while(isRunning) {
try {
b.connect(host, port).sync().channel().closeFuture().sync();
} catch(Exception e) {
if (e instanceof InterruptedException) {
throw e;
}
retryLogger.warn("try to connect to " + host + " : " + port + " , but", e);
}
if(isRunning) {
retryLogger.info("netty connection lost, retry!");
Thread.sleep(RetryInterval);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
group.shutdownGracefully();
}
}
The session code is very simple, send Get-packet to server, get response, write file, then send next Get-packet.
In this program, I start two netty client threads, but after running several days, one of them behaves like a zombie thread, that is, even if I kill the netty server, the zombie client prints no log while the other client prints the wanted logs. By the way, the jstack file shows both threads are live, not dead.
I am using netty 5.
You don't have any mechanism for a readtimeout, what happens is that there is no traffic for 10~ (depends on the router model) minutes and the NAT table in the router thinks the connection is done, and closes the connection.
You have multiple ways to solve this problem:
Using ReadTimeoutHandler
ReadTimeoutHandler closes the channel and throws a ReadTimeoutException if a timeout is detected. You can catch this exception if needed via the exceptionCaught. With your existing logic, you don't need to catch this.
This handler can also be used in combination with a WriteTimeoutHandler to write "ping" messages to the remote. However the following solution is better for this purpose.
Using IdleStateHandler
You can also use a IdleStateHandler for this purpose, this handler has 3 arguments that stand for readerIdleTime, writeIdleTime and allIdleTime. The advantage of this class is that it doesn't throw exceptions and uses the Netty userEventTriggered to dispatch its calls, while this makes the class harder to use, you can do more things with it.
For example, if you protocol supports ping messages, you can use this class to send to send those ping messages. Its really easy to use this class and can be used in handlers as the following:
public class MyChannelInitializer extends ChannelInitializer<Channel> {
#Override
public void initChannel(Channel channel) {
channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(60, 30, 0));
channel.pipeline().addLast("myHandler", new MyHandler());
}
}
// Handler should handle the IdleStateEvent triggered by IdleStateHandler.
public class MyHandler extends ChannelHandlerAdapter {
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
ctx.close();
} else if (e.state() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush(new PingMessage());
}
}
}
}
Related
To use the Interactive Brokers (IB) TWS API, I need to write a class that implements EWrapper. I am having trouble properly disconnecting my socket in that class, even when using the built-in eDisconnect() method provided by IB.
Whenever I write a class that implements EWrapper, calling eDisconnect() on the EClientSocket instance throws an Exception (which is passed to error(Exception e)...more details below). I would like to be able to disconnect properly, so that I can connect and disconnect serially within the same program.
Consider the following example:
public class ConnectionTest {
public static void main(String[] args) {
// Create Contract
Contract contract = new Contract();
contract.symbol("IBM");
contract.secType("STK");
contract.exchange("NYSE");
TWSCounter twsCounter = new TWSCounter(); // Class I wrote that simply keeps track of ID request numbers
APIClient apiClient = new APIClient(twsCounter);
apiClient.connect();
apiClient.reqContractDetails(contract);
//apiClient.disconnect(); // commented out for first example
}
}
public class APIClient implements EWrapper {
private TWSCounter twsCounter;
private EClientSocket clientSocket = null;
private EJavaSignal signal = new EJavaSignal();
private EReader reader;
// Constructor
public APIClient(TWSCounter twsCounter) {
this.twsCounter = twsCounter;
}
public void connect() {
// Create a new EClientSocket object
clientSocket = new EClientSocket (this, signal);
// Connect to the TWS
clientSocket.eConnect("127.0.0.1", 7497, 0);
// Pause here for connection to complete
try {
while (! (clientSocket.isConnected()));
}
catch (Exception e) {
e.printStackTrace();
System.err.println("Error trying to connect to TWS API");
System.exit(1);
}
reader = new EReader(clientSocket, signal);
reader.start();
// Based on IB sample code in Test.java
new Thread(() -> {
while (clientSocket.isConnected()) {
signal.waitForSignal();
try {
reader.processMsgs();
} catch(Exception e) {
error(e);
System.err.println("Error while trying reader.processMsgs()");
}
}
}).start();
}
public void disconnect() {
clientSocket.eDisconnect();
}
public void reqContractDetails(Contract contract) {
clientSocket.reqContractDetails(twsCounter.getNextNumber(), contract);
}
#Override
public void contractDetails(int reqId, ContractDetails contractDetails) {
System.out.println("conID: " + contractDetails.conid());
}
// More methods...
When I run ConnectionTest, I correctly get the output, conID: 8314. The program continues to run indefinitely, however, until I terminate it manually in eclipse by pushing the 'stop' button.
If I "uncomment" apiClient.disconnect() at the end of ConnectionTest, then the program disconnects, however an exception is thrown.
How one chooses to override the error methods in the APIClient affects what happens. If they are not overridden, the process terminates silently, despite the Exception.
If, however, public void error(Exception e) is overridden as follows, we get the following stack trace:
public void error(Exception e) {
e.printStackTrace();
}
java.net.SocketException: Socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.io.DataInputStream.readInt(Unknown Source)
at com.ib.client.EClientSocket.readInt(EClientSocket.java:233)
at com.ib.client.EReader.readSingleMessage(EReader.java:119)
at com.ib.client.EReader.putMessageToQueue(EReader.java:79)
at com.ib.client.EReader.run(EReader.java:57)
I believe issue is that the EReader (EReader extends Thread) calls putMessageToQueue() even after the socket is disconnected. From EReader:
#Override
public void run() {
try {
// loop until thread is terminated
while (!isInterrupted()) {
if (!putMessageToQueue())
break;
}
}
catch ( Exception ex ) {
//if (parent().isConnected()) {
if( ex instanceof EOFException ) {
eWrapper().error(EClientErrors.NO_VALID_ID, EClientErrors.BAD_LENGTH.code(),
EClientErrors.BAD_LENGTH.msg() + " " + ex.getMessage());
}
else {
eWrapper().error( ex);
}
parent().eDisconnect();
//}
}
m_signal.issueSignal();
}
Modifying the APIClient.disconnect() method to interrupt the EReader, doesn't seem to help however:
public void disconnect() {
reader.interrupt();
clientSocket.eDisconnect();
}
For the most part, I've been able to get by without solving this issue properly. What I would really like to be able to do, however, is connect and disconnect serially, as in this simplified example:
public class ConnectionTest {
public static void main(String[] args) {
// Create Contracts
Contract contract = new Contract();
contract.symbol("IBM");
contract.secType("STK");
contract.exchange("NYSE");
Contract contract2 = new Contract();
contract2.symbol("KO");
contract2.secType("STK");
contract2.exchange("NYSE");
TWSCounter twsCounter = new TWSCounter();
APIClient apiClient = new APIClient(twsCounter);
apiClient.connect();
apiClient.reqContractDetails(contract);
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
System.exit(1);
}
apiClient.disconnect();
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
}
apiClient.connect();
apiClient.reqContractDetails(contract2);
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
}
apiClient.disconnect();
}
}
Running this, as is, will sometimes correctly give conID: 8314 and conID: 8894 along with two stack traces the same as above, but will also sometimes give conID: 8314 and Connection Message: -1, 507, Bad Message Length null. Error message 507 is:
Bad Message Length (Java-only)
Indicates EOF exception was caught while reading from the socket. This can occur if there is an attempt >to connect to TWS with a client ID that is already in use, or if TWS is locked, closes, or breaks the >connection. It should be handled by the client application and used to indicate that the socket
connection is not valid.
IB Message Codes
For what it's worth, I've been completely frustrated by this problem off and on for two years. (I'm not a professional programmer.) I've searched for solutions online and even paid a programmer to help me, all to no avail (although I have learned much more java in the process). I've also downloaded the latest version of the IB API (v 9.76). The same problem occurs with multiple versions of the IB API.
I am building an application with several server and client HL7 connections managed by a CommunicationProcess class. Part of the application's functionality is to restart that process when new connections are added. Client connections do not pose a problem because, once the client side stops, there is nothing the server side can do to reconnect. For server connections however, I seem to be getting immediate reconnections from the (rather agressive) client side. This is the code I have to stop a server connection :
public void disconnect()
{
usageServer.getRemoteConnections().forEach((connection) -> connection.close());
usageServer.stopAndWait();
usageServer.getRemoteConnections().forEach((connection) -> connection.close());
}
public void stop()
{
running.set(false);
disconnect();
}
This is my implementation of connectionReceived :
#Override
public void connectionReceived(Connection theC)
{
if (running.get())
{
setStatus(ConnectionStatus.CONNECTED);
}
else
{
theC.close();
}
}
As you can see, the idea is to set a global AtomicBoolean to false when receiving the stop signal from the CommunicationProcess class, which denies any new connections, and stop the server. This, somehow, still allows the client to remain connected during this process. The client side is an application I'm not allowed to name but that has existed for well over a decade and I know for a fact it is not gonna be the issue because I've been supporting it as part of my day job for years and it simply does not behave like that.
Any idea why my code doesn't actually kill the connection? I feel like I've explored a lot of this API and I'm not finding a way to UNREGISTER a connection listener which would probably fix this. Also, there is no way that I can see to extend these server classes as everything is rather ferociously encapsulated and privatized.
Thanks
I was reviewing the code of the HAPI library.
The cause of the behaviour that you describe could be the following.
When the server starts, they creates a component named AcceptorThread. As it name implies, the responsability of this thread is initialize the ServerSocket that will be used to receive incoming client connections, and accept them.
This thread, as every Service abstraction proposed by the API, runs in a loop like this:
/**
* Runs the thread.
*
* #see java.lang.Runnable#run()
*/
public final void run() {
try {
afterStartup();
log.debug("Thread {} entering main loop", name);
while (isRunning()) {
handle();
startupLatch.countDown();
}
log.debug("Thread {} leaving main loop", name);
} catch (RuntimeException t) {
if (t.getCause() != null) {
serviceExitedWithException = t.getCause();
} else {
serviceExitedWithException = t;
}
log.warn("Thread exiting main loop due to exception:", t);
} catch (Throwable t) {
serviceExitedWithException = t;
log.warn("Thread exiting main loop due to exception:", t);
} finally {
startupLatch.countDown();
afterTermination();
}
}
When you invoke the method stopAndWait in the server, it will try to stop this thread also.
The stop process basically changes the boolean flag that controls whether the component ``ìsRunning()``` or not.
As you can see, although it sets the flag to false, the invocation of the method handle in the loop still must end.
This is the implementation of the AcceptorThread handle method:
#Override
protected void handle() {
try {
Socket s = ss.accept();
socketFactory.configureNewAcceptedSocket(s);
if (!queue.offer(new AcceptedSocket(s))) {
log.error("Denied enqueuing server-side socket {}", s);
s.close();
} else
log.debug("Enqueued server-side socket {}", s);
} catch (SocketTimeoutException e) { /* OK - just timed out */
log.trace("No connection established while waiting");
} catch (IOException e) {
log.error("Error while accepting connections", e);
}
}
As you can see, the method invokes ServerSocket.accept, thus allowing new incoming connections.
In order to disconnect this server side socket, we can call close from another thread.
In fact, this process is the one implemented by the AcceptorTread afterTermination method:
#Override
protected void afterTermination() {
try {
if (ss != null && !ss.isClosed())
ss.close();
} catch (IOException e) {
log.warn("Error during stopping the thread", e);
}
}
Unfortunally - you are right, the API is very close! - there is no a clear way to do that.
One possible solution could be implement your own HL7Service, name it, MySimpleServer, using the code of SimpleServer as a baseline, and just changing the implementation of the method afterTermination:
/**
* Close down socket
*/
#Override
protected void afterTermination() {
super.afterTermination();
// Terminate server side socket
acceptor.afterTermination();
// Terminate the acceptor thread itself
acceptor.close();
}
Please, pay attention: instead of call acceptor.stop() we invoke acceptor.afterTermination() to close directly the underlying server side socket.
To avoid the errors raised by the handle method in AcceptorThread, we can also implement a new class from the original one, or just trying to overwrite the handle method to take into account if the server side socket is closed:
#Override
protected void handle() {
try {
if (ss.isClosed()) {
log.debug("The server-side socket is closed. No new connections will be allowed.");
return;
}
Socket s = ss.accept();
socketFactory.configureNewAcceptedSocket(s);
if (!queue.offer(new AcceptedSocket(s))) {
log.error("Denied enqueuing server-side socket {}", s);
s.close();
} else
log.debug("Enqueued server-side socket {}", s);
} catch (SocketTimeoutException e) { /* OK - just timed out */
log.trace("No connection established while waiting");
} catch (IOException e) {
log.error("Error while accepting connections", e);
}
}
For testing, you can try something like this:
public static void main(String[] args) throws Exception {
HapiContext ctx = new DefaultHapiContext();
HL7Service server = new MySimpleServer(8888);
server.startAndWait();
Connection client1 = ctx.newClient("127.0.0.1", 8888, false);
server.getRemoteConnections().forEach((connection) -> connection.close());
server.stopAndWait();
try {
Connection client2 = ctx.newClient("127.0.0.1", 8888, false);
} catch (Throwable t) {
t.printStackTrace();
}
ctx.close();
System.exit(0);
}
So my question goes here. Now if my Server has over 20 clients, it also has 20 threads and my desktop with an ryzen CPU goes to 100% at usage at 30 Threads. Now I'd like to handle a mass-amount of clients by one server, but the CPU is just getting over-used. My wise is very simple how I do it, but there must be a better way; because I saw many good java servers so far yet. I don't know what I do wrong though. In the following I share my code, how I do it in principle.
while(this.isRunning()) {
ServerSocket server = new ServerSocket(8081);
Socket s = server.accept();
new Thread(new WorkerRunnable(s)).start();
//now here if e.g. over 25 users connect there are 25 threads. CPU is at 100%. Is there a better way to handle this?
The worker runnable is identifing the clients. After that they will get into a chat-room. Its like a group chat for e.g.
Edit: Relevant parts of my very unfinished code which is still very WIP
private boolean state;
private ServerSocket socket;
#Override
public void run() {
while(this.isRunning()==true) {
try {
if(this.socket==null) this.socket = new ServerSocket(this.getPort());
Socket connection = this.socket.accept();
IntroductionSession session = new IntroductionSession(this, connection);
new Thread(session).start();
//register timeout task for 3 secs and handle it async
System.out.println(ManagementFactory.getThreadMXBean().getThreadCount());
//this.handleIncomingConnection(connection);
} catch(Exception e) {
e.printStackTrace();
//System.exit(1);
}
}
}
private class IntroductionSession implements Runnable {
private boolean alive = true;
private BaseServer server;
private Socket socket;
private boolean introduced = false;
public IntroductionSession(BaseServer server, Socket socket) {
this.server = server;
this.socket = socket;
}
private void interrupt() {
System.out.println("Not mroe alive");
this.alive = false;
}
private void killConnection() {
this.killConnection("no_reason");
}
private void killConnection(String reason) {
try {
if(this.from_client!=null) this.from_client.close();
if(this.to_client!=null) this.to_client.close();
this.socket.close();
switch(reason) {
case "didnt_introduce":
System.out.println("Kicked connection, cause it didn't introduce itself");
break;
case "unknown_type":
System.out.println("Kicked unknown connection-type.");
break;
case "no_reason":
default:
//ignore
break;
}
} catch (IOException e) {
switch(reason) {
case "didnt_introduce":
System.out.println("Error at kicking connection, which didn't introduce itself");
break;
case "unknown_type":
System.out.println("Error at kicking unknown connection-type.");
break;
case "no_reason":
default:
System.out.println("Error occured at kicking connection");
break;
}
e.printStackTrace();
}
}
private ObjectInputStream from_client;
private ObjectOutputStream to_client;
#Override
public void run() {
while(this.alive==true) {
try {
if(this.to_client==null) {
this.to_client = new ObjectOutputStream(this.socket.getOutputStream());
//this.to_client.flush();
}
if(this.from_client==null) this.from_client = new ObjectInputStream(this.socket.getInputStream());
//Time runs now, if socket is inactive its getting kicked
new Timer().schedule(new java.util.TimerTask() {
#Override
public void run() {
if(IntroductionSession.this.introduced==false) {
IntroductionSession.this.killConnection("didnt_introduce");
Thread.currentThread().interrupt();
IntroductionSession.this.interrupt();
}
}
}, 5000
);
Object obj = this.from_client.readObject();
while(obj!=null) {
if(obj instanceof IntroductionPacket) {
IntroductionPacket pk = (IntroductionPacket) obj;
introduced = true;
if(isCompatible(pk)==false) {
try {
this.to_client.writeObject(new DifferentVersionKickPacket(BaseServer.version));
this.to_client.close();
this.from_client.close();
IntroductionSession.this.socket.close();
System.out.println("Kicked socket, which uses another version.");
} catch(Exception e) {
Thread.currentThread().interrupt();
//ignore
System.out.println("Error at kicking incompatible socket.");
e.printStackTrace();
}
} else {
this.server.handleIncomingConnection(this.socket, this.from_client, this.to_client);
}
Thread.currentThread().interrupt();
}
}
} catch(StreamCorruptedException e) {
//unknown client-type = kick
this.killConnection("unknown_type");
} catch (IOException|ClassNotFoundException e) {
e.printStackTrace();
this.killConnection("no_reason");
}/* catch(SocketException e) {
}*/
}
Thread.currentThread().interrupt();
}
}
Extending class, which is an actual server:
#Override
public void handleIncomingConnection(Socket connection, ObjectInputStream from_client, ObjectOutputStream to_client) {
new AuthenticationSession(connection, from_client, to_client).run();
}
private class AuthenticationSession implements Runnable {
private Socket socket;
private ObjectInputStream from_client;
private ObjectOutputStream to_client;
public AuthenticationSession(Socket socket, ObjectInputStream from_client, ObjectOutputStream to_client) {
this.socket = socket;
this.to_client = to_client;
this.from_client = from_client;
}
//TODO: Implement app id for access tokens
#Override
public void run() {
try {
while(this.socket.isConnected()==true) {
/*ObjectOutputStream to_client = new ObjectOutputStream(socket.getOutputStream()); //maybe cause problems, do it later if it does
ObjectInputStream from_client = new ObjectInputStream(socket.getInputStream());*/
Object object = from_client.readObject();
while(object!=null) {
if(object instanceof RegisterPacket) {
RegisterPacket regPacket = (RegisterPacket) object;
System.out.println("Username:" + regPacket + ", password: " + regPacket.password + ", APP-ID: " + regPacket.appId);
} else {
System.out.println("IP " + this.socket.getInetAddress().getHostAddress() + ":" + this.socket.getPort() + " tried to send an unknown packet.");
this.socket.close();
}
}
}
}/* catch(EOFException eofe) {
//unexpected disconnect
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
/*catch(Exception e) {
//e.printStackTrace();
Thread.currentThread().interrupt();
}*/
}
}
Please dont look at its very bad formatting and stuff I did in hope to fix it, the tasks dont die whyever though.
Generally, in production grade server code, we don't work with direct creation of socket and handling of requests. It's a nightmare to work with low level sockets, close connections and prevent leaks. Rather, we rely on production grade frameworks such as Java Spring Framework or Play Framework.
My question is, why aren't you using any server-side frameworks such as the ones I listed above?
If you're wondering how these frameworks handle thousands of concurrent requests, look into design patterns such as Thread Pool. These frameworks abstract away the complexities and handle the thread pool for you.
If the clients aren't expected to receive an immediate response, you could also look into introducing messaging queue such as Kafka. The server will pick the messages one by one from the queue and process them. However, bear in mind that this is asynchronous and may not meet your requirements.
If you're not just restricted to one server, you could look into deploying your server code to Azure or AWS VMSS (Virtual machine scale set). Based on CPU load rules you configure, the system will autoscale and dynamically manage resources for you.
I would suggest reading upon system design principles related to servers to reinforce your understanding.
Don't reinvent the wheel.
Since you are doing a Chat Application you need to think of doing a Single Threaded Event Loop.
You can Keep a Map of String (Client id) and Socket (Client socket).
Map<String, Socket> clientSockets;
You Server thread will accept new Client Sockets and will just put it in the above map. Then there will be another Thread which will do the Event Loop and whenever there is data in any of the Client Socket in InputStream it should send that data to all other Client Sockets (Group Chat). This should happen infinitely with a Sleep interval.
Recently, I find some BlockingOperationException in my netty4 project.
Some people said that when using the sync() method of start netty's ServerBootstrap can cause dead lock, because sync() will invoke await() method, and there is a method called 'checkDeadLock' in await().
But I don't think so. ServerBootstrap use the EventLoopGroup called boosGroup, and Channel use the workerGroup to operation IO, I don't think they will influence each other, they have different EventExecutor.
And in my practice, Deadlock exception doesn't appear in the Netty startup process, most of which occurs after the Channel of the await writeAndFlush.
Analysis source code, checkDeadLock, BlockingOperationException exception thrown is when the current thread and executor thread of execution is the same.
My project code is blow:
private void channelWrite(T message) {
boolean success = true;
boolean sent = true;
int timeout = 60;
try {
ChannelFuture cf = cxt.write(message);
cxt.flush();
if (sent) {
success = cf.await(timeout);
}
if (cf.isSuccess()) {
logger.debug("send success.");
}
Throwable cause = cf.cause();
if (cause != null) {
this.fireError(new PushException(cause));
}
} catch (LostConnectException e) {
this.fireError(new PushException(e));
} catch (Exception e) {
this.fireError(new PushException(e));
} catch (Throwable e) {
this.fireError(new PushException("Failed to send message“, e));
}
if (!success) {
this.fireError(new PushException("Failed to send message"));
}
}
I know Netty officials advise not to use sync() or await() method, but I want to know what situation will causes deadlocks in process and the current thread and executor thread of execution is the same.
I change my project code.
private void pushMessage0(T message) {
try {
ChannelFuture cf = cxt.writeAndFlush(message);
cf.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws PushException {
if (future.isSuccess()) {
logger.debug("send success.");
} else {
throw new PushException("Failed to send message.");
}
Throwable cause = future.cause();
if (cause != null) {
throw new PushException(cause);
}
}
});
} catch (LostConnectException e) {
this.fireError(new PushException(e));
} catch (Exception e) {
this.fireError(new PushException(e));
} catch (Throwable e) {
this.fireError(new PushException(e));
}
}
But I face a new problem, I can't get the pushException from the ChannelHandlerListener.
BlockingOperationException will be throw by netty if you call sync*or await* on a Future in the same thread that the EventExecutor is using and to which the Future is tied to. This is usually the EventLoop that is used by the Channel itself.
Can not call await in IO thread is understandable. However, there are 2 points.
1. If you call below code in channel handler, no exception will be reported, because the the most of the time the check of isDone in await returns true, since you are in IO thread, and IO thread is writing data synchronously. the data has been written when await is called.
ChannelPromise p = ctx.writeAndFlush(msg);
p.await()
If add a handler in different EventExecutorGroup, this check is not necessary, since that executor is newly created and is not the same one with the channel's IO executor.
guys! i wrote simple server client (udp) application. Now i am trying to make server that accepts many clients. As, i understood, i need to create functions, that accepts and handle clients, but i am confused about apps structure. Can u check if i have right skeleton for my app? Mayb u can give me some hint or example. All advice appreciated! :)
class MultiServer {
private DatagramSocket serversocket;
public MultiServer() {
try {
this.serversocket = new DatagramSocket(6789);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void start() throws IOException {
while(true) {
DatagramSocket serversock = serversocket.accept();
new Thread(new ClientHandler(serversock)).start();
}
}
public static void main(String[] args) {
Server1 server = new Server1();
try {
server.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ClientHandler implements Runnable {
private final DatagramSocket clientsocket;
ClientHandler(DatagramSocket sock) {
this.clientsocket = sock;
}
#Override
public void run() {
//receive packet, send msg, get ip, get portnumber ?
}
}
}
So you want your server to be able to operate with multiple requests at the same time? Good, it's how most web-servers work. You have to understand the basic concepts of multi-threading and concurrency.
A simple server can only handle ONE thing at a time. What happens if another request is received while the server is dealing with something else? Nothing, so the application isn't very efficient and not scalable at all.
If you haven't used multiple threads in your applications yet and don't know much about concurrency, it's a great time to have a go, read the Oracle Concurrency Lesson, or find a tutorial online, there are plenty.
Now, once (or if) you know how threading works make sure you break down your functions as much as possible and see what functions can happen at the same time. An example of a Web Server that i can think of is this:
A separate thread to listen on the port for requests. Once a request is received, place it in the 'request pool' and queue it to be processed
A separate thread (or multiple threads/thread pool) that process the request
Your structure looks like you have both receive and process in the same Runnable. Anyway, this is just an idea, you'll have to see what's more applicable to your application. Also, have a look at the Concurrency tools that newer Java versions provide, Java 6 and 7 provide a lot of tools you can use that are very effective (but also quite hard to understand and use in my opinion).
Good luck!
You are looking for a Threadpooled Server. The way you started is good. Now you simply implement a Java execution Service to Handle the requests. The threadpool has a fixed of thread. It does take your requests and put those in a queue and if a request is done it takes the next request. So you normaly dont lose any requests.
Here is a small example i made:
public class PoolServer implements Runnable {
private static final int DEFAULT_PORT = 8080;
private static final String CONFIG = "config.xml";
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread = null;
protected ExecutorService threadPool = Executors.newFixedThreadPool(100);
protected int serverPort;
public PoolServer() {
// getting the port from the XML
this.serverPort = getPortFromXML();
}
public void run() {
synchronized (this) {
this.runningThread = Thread.currentThread();
}
openServerSocket();
// accepting loop
while (!isStopped()) {
Socket clientSocket = null;
try {
// accept the client
clientSocket = this.serverSocket.accept();
clientSocket.setSoTimeout(2000);
} catch (IOException e) {
if (isStopped()) {
return;
}
throw new RuntimeException("Error accepting client connection",
e);
}
this.threadPool.execute(new ThreadHandler(clientSocket));
}
// loop end
// server stopped shut down the ThreadPool
this.threadPool.shutdown();
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port " + this.serverPort, e);
}
}
At this point this.threadPool.execute(new ThreadHandler(clientSocket)); i do execute the request if a thread is free. Else it get into the queue of the Threadpool.
You can even change it from a Fixed to some other Threadpools! Just take a look at the Executors and take what you need. Executors
Hope this helps!