J'avais développé un serveur Java en utilisant Java nio
sockets. C'est le code de mon application:
public class EchoServer {
static final org.Apache.log4j.Logger logger = org.Apache.log4j.Logger.getLogger(Main.class);
private static final int BUFFER_SIZE = 1024;
private final static int DEFAULT_PORT = 4664;
private InetAddress hostAddress = null;
private int port;
private String ipAddress = "my ip";
private Selector selector;
// The buffer into which we'll read data when it's available
private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
int timestamp = 1;
HashMap<Integer, String> connectedClients = new HashMap<Integer, String>();
HashMap<String, Integer> clientIds= new HashMap<String,Integer>();
HashMap<String, String> messageToClients = new HashMap<String, String>();
public EchoServer() {
this(DEFAULT_PORT);
}
public EchoServer(int port) {
try{
this.port = port;
hostAddress = InetAddress.getByName(ipAddress);
selector = initSelector();
loop();
}catch(Exception ex){
logger.error("Exception Accoured:",ex);
}
}
private Selector initSelector() {
try{
Selector socketSelector = SelectorProvider.provider().openSelector();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(hostAddress, port);
serverChannel.socket().bind(isa);
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
return socketSelector;
}catch(Exception ex){
logger.error("Exception Accoured:",ex);
return null;
}
}
private void loop() {
while (true) {
try {
// Do defined operations for clients
// ------------------------------
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
.iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
logger.warn(key.hashCode() + "- is invalid");
continue;
}
// Check what event is available and deal with it
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if (key.isWritable()) {
write(key);
}
}
// Fetch List from server
// -----------------------------------------
try {
ResultSet resultset = DataBase.getInstance()
.getQueryResult();
boolean flag = false;
while (resultset.next()) {
String mobileNumber = resultset.getString("MobileNo");
String message = resultset.getInt("IsMessage") + ","
+ resultset.getInt("IsDeliver") + ","
+ resultset.getInt("IsGroup") + ","
+ resultset.getInt("IsSeen");
messageToClients.put(mobileNumber, message);
}
} catch (Exception ex) {
//ex.printStackTrace();
logger.error("Exception Accoured:",ex);
}
// Wait for 1 second
// -----------------------------------------------
Thread.sleep(1000);
timestamp++;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
private void accept(SelectionKey key) {
try{
// Initialize the connection ------------------------------------------
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key
.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
logger.info("New client accepted");
// Fire read for reading phone number --------------------------------
socketChannel.register(selector, SelectionKey.OP_READ);
}catch(Exception ex){
logger.error("Exception Accoured:",ex);
}
}
private void read(SelectionKey key) {
try{
// Initialize Socket -----------------------------------------------------
SocketChannel socketChannel = (SocketChannel) key.channel();
// Reading Client Number -------------------------------------------------
readBuffer.clear();
int numRead;
try {
numRead = socketChannel.read(readBuffer);
} catch (IOException e) {
logger.error("Forceful shutdown");
key.cancel();
return;
}
// read was not successful
if (numRead == -1) {
logger.error("Graceful shutdown");
key.cancel();
return;
}
// read was successful and now we can write it to String
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
String number = new String(bytes);
number = number.replace("\r\n", "");
number = number.trim();
// Update Connect Clients Status -----------------------------------------
Integer clientId=clientIds.get(number);
if ( clientId == null) {
connectedClients.put(key.hashCode(), number);
clientIds.put(number, key.hashCode());
logger.error(number + "- (" + key.hashCode() + ") has Connected");
}else{
connectedClients.remove(clientId);
connectedClients.put(key.hashCode(), number);
clientIds.put(number, key.hashCode());
logger.error(number + "- (" + key.hashCode() + ") REconnected");
}
//System.err.println("All clients number are:" + connectedClients.size());
logger.error("All clients number are:" + connectedClients.size());
// Fire Write Operations -------------------------------------------------
socketChannel.register(selector, SelectionKey.OP_WRITE);
}catch(Exception ex){
//ex.printStackTrace();
logger.error("Exception Accoured:",ex);
}
}
private void write(SelectionKey key) {
try {
//Check channel still alive ----------------------------------------------
String clientNumber = connectedClients.get(key.hashCode());
if(clientNumber == null){
key.cancel();
return;
}
// Get Channel -----------------------------------------------------------
SocketChannel socketChannel = (SocketChannel) key.channel();
// Send Message if client number have new message ------------------------
if (messageToClients.get(clientNumber) != null) {
logger.info(clientNumber + "-" + key.hashCode()
+ "- Sent write message");
String timeStamp = String.valueOf(timestamp);
String message = messageToClients.get(clientNumber);
ByteBuffer dummyResponse = ByteBuffer.wrap((message + "\r\n").getBytes("UTF-8"));
socketChannel.write(dummyResponse);
messageToClients.remove(clientNumber);
}
// Fire new write state --------------------------------------------------
socketChannel.register(selector, SelectionKey.OP_WRITE);
} catch (IOException iox) {
logger.error("Exception Accoured:",iox);
String number = connectedClients.get(key.hashCode());
clientIds.remove(number);
connectedClients.remove(key.hashCode());
key.cancel();
} catch(Exception ex){
logger.error("Exception Accoured:",ex);
}
}
}
Lorsque je teste avec 2-3 clients, cela fonctionne bien, mais lorsque je commence à le tester avec environ 100 à 300 clients, je reçois plusieurs fois l'exception ci-dessous (en fait, cela se produit sur la méthode et la ligne write()
et la ligne socketChannel.write(dummyResponse);
:
Java.io.IOException: An established connection was aborted by the software in your Host machine
at Sun.nio.ch.SocketDispatcher.write0(Native Method)
at Sun.nio.ch.SocketDispatcher.write(Unknown Source)
at Sun.nio.ch.IOUtil.writeFromNativeBuffer(Unknown Source)
at Sun.nio.ch.IOUtil.write(Unknown Source)
at Sun.nio.ch.SocketChannelImpl.write(Unknown Source)
at net.behboodi.testserver.EchoServer.write(EchoServer.Java:274)
at net.behboodi.testserver.EchoServer.loop(EchoServer.Java:106)
at net.behboodi.testserver.EchoServer.<init>(EchoServer.Java:56)
at net.behboodi.testserver.EchoServer.<init>(EchoServer.Java:47)
at net.behboodi.testserver.Main.main(Main.Java:44)
puis je ne peux pas recevoir de messages du serveur.
Lorsque vous obtenez la fin du flux (read()
renvoie -1), vous ne fermez pas le canal, vous fuyez donc les canaux. Vous devez fermer le canal et notez que cela annulera automatiquement la clé. L'annulation seule de la clé n'est pas suffisante. De même lorsque vous annulez la clé car clientNumber == null
Ou vous obtenez un IOException
.
NB: vous ne devez pas enregistrer OP_WRITE
Sauf si vous venez de recevoir zéro d'un appel à write()
, et vous devez le désinscrire chaque fois que vous n'obtenez pas ce zéro.