Please help .
When you first start the server, create a thread pool in advance (5 threads) and create 5 selectors to read, pass one selector to each Runnable, and immediately send the submit function to the thread pool.
Then, create a selector that accepts the user and run it on the main thread.
The problem here is that the Selector in the main thread detects the user's request and registers the returned SocketChannel in one of the 5 Read Selectors.
And when the connection is successful, the user sends text data every second, but there is no response from the Read Selector.
I don't know why this problem occurs.. ㅜㅜ
Main.java
public class Main {
public static void main(String[] args) {
Server server = new Quarter();
}
}
Server.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public abstract class Server {
protected volatile boolean isStart = true;
// 192.168.0.11 company
// 192.168.75.35 house
// 192.168.75.97 my notebook
private static final String ADDRESS = "192.168.75.35";
private static final int PORT = 1111;
private static final int BACKLOG = 1024;
protected Selector acceptSelector = null;
/** 하나의 스레드에서만 접근 할것이기 때문에 동기화 처리는 필요 없음. */
protected List<Selector> readSelectors = new ArrayList<Selector>();
protected ServerSocketChannel serverSocketChannel = null;
protected InetSocketAddress inetSocketAddress = null;
private final int nThreads = 5;
protected ExecutorService threadpool = null;
/** 현재 연결된 클라이언트 수 */
protected static int connectionCount = 0;
// volatile 캐시 메모리 문제는 아닌가?
// 뭐가 문제지?
protected void init() {
try {
this.threadpool = Executors.newFixedThreadPool(this.nThreads);
for (int i = 1; i <= this.nThreads; i++) {
Selector sel = Selector.open();
this.readSelectors.add(sel);
threadpool.submit(new SubSelector(sel));
}
this.acceptSelector = Selector.open();
this.inetSocketAddress = new InetSocketAddress(ADDRESS, PORT);
this.serverSocketChannel = ServerSocketChannel.open();
// non - blocking 으로 설정.
this.serverSocketChannel.configureBlocking(false);
System.out.println("serverSocketChannel is non-blocking.");
this.serverSocketChannel.bind(this.inetSocketAddress, BACKLOG);
System.out.println("binding is success.");
this.serverSocketChannel.register(this.acceptSelector, SelectionKey.OP_ACCEPT);
System.out.println("initing is success.");
} catch (AlreadyBoundException abe) {
// 이미 사용중인 포트에 바인딩하려면 에러
System.err.println(abe.getMessage());
this.exit();
} catch (IOException e) {
System.err.println(e.getMessage());
this.exit();
}
}
protected synchronized void exit() {
try {
this.isStart = false;
this.serverSocketChannel.close();
this.acceptSelector.close();
this.threadpool.shutdown();
for (int i = 0; i < this.readSelectors.size(); i++) {
this.readSelectors.get(i).close();
}
} catch (IOException e) {
System.err.print(e.getMessage());
}
}
protected synchronized void connectCountAdd() {
connectionCount++;
}
protected synchronized void connectCountMinus() {
connectionCount--;
}
}
Quarter.java
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
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;
public class Quarter extends Server {
public Quarter() {
init();
this.initQuarter();
}
private void initQuarter() {
while (isStart) {
try {
int ready = acceptSelector.select();
if (ready <= 0) continue;
Set<SelectionKey> set = acceptSelector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isAcceptable()) this.accept(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void accept(SelectionKey key) {
try {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if (channel == null) return;
channel.configureBlocking(false);
int targetIndex = connectionCount % readSelectors.size();
Selector tarSel = readSelectors.get(targetIndex);
channel.register(tarSel, SelectionKey.OP_READ);
connectCountAdd();
System.out.println("Connected to: " + channel.getRemoteAddress());
System.out.println("Current Connection Count: " + connectionCount);
}
catch (ClosedChannelException cce) {
// 채널이 닫힌 경우
System.err.println(cce.getMessage());
key.cancel();
}
catch (ClosedSelectorException cse) {
// 셀렉터가 닫힌 경우
System.err.println(cse.getMessage());
key.cancel();
exit();
}
catch (IOException e) {
System.err.println(e.getMessage());
key.cancel();
exit();
}
}
}
SubSelector.java
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class SubSelector extends Server implements Runnable {
private Selector selector = null;
Charset charset = Charset.forName("UTF-8");
public SubSelector(Selector selector) {
this.selector = selector;
}
#Override
public void run() {
while (isStart) {
try {
int ready = selector.select();
if (ready <= 0) return;
Set<SelectionKey> set = this.selector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isReadable()) this.read(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void read(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
int readCount = channel.read(buffer);
if (readCount <= 0) return;
buffer.flip();
String result = charset.decode(buffer).toString();
System.out.println("client ip address: " + channel.getRemoteAddress());
System.out.println("client send message: " + result);
}
catch (NotYetConnectedException nyce) {
// 채널이 아직 연결되지 않은 경우
System.err.println(nyce.getMessage());
}
catch (SocketException se) {
System.err.println(se.getMessage());
this.disConnect(key);
}
catch (IOException e) {
// 기타 에러
System.err.println(e.getMessage());
}
}
private void disConnect(SelectionKey key) {
key.cancel();
selector.wakeup();
connectCountMinus();
System.out.println("Current Connection Count: " + connectionCount);
}
}
Client.java
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("192.168.75.35", 1111));
System.out.println("is Connected: " + socket.isConnected());
while (true) {
OutputStream os = socket.getOutputStream();
os.write(Thread.currentThread().getName().getBytes());
os.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Related
The class Server:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server{
private int port;
private Socket client;
private ServerSocket server;
public Server(int port) {
try {
this.port = port;
setServer(new ServerSocket(this.port));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.start();
}
public void start() {
ArrayList<ClientHandler> clients = new ArrayList<>();
ExecutorService executor = Executors.newFixedThreadPool(20);
System.out.println("Server started...");
while (true) {
try {
setClient(null);
setClient(getServer().accept());
if (getClient() != null) {
ClientHandler handler = new ClientHandler(getClient());
System.out.println("Client connected...");
clients.add(handler);
executor.execute(handler);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server server = new Server(4567);
}
public Socket getClient() {
return client;
}
public void setClient(Socket client) {
this.client = client;
}
public ServerSocket getServer() {
return server;
}
public void setServer(ServerSocket server) {
this.server = server;
}
}
Now, my Question is, is there a Problem in the class, which causes the UnknownHostException? I already tried to debug, but the problem lies in the class Client
The Class Client:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client{
private String kennung;
private Socket server;
private String host;
private int port;
public Client(String kennung, String host, int port){
setKennung(kennung);
setHost(host);
setPort(port);
try {
setServer(new Socket(this.host, this.port));
//Scanner reader = new Scanner(this.server.getInputStream());
PrintWriter writer = new PrintWriter(getServer().getOutputStream(), true);
Scanner sc = new Scanner(System.in);
writer.println("The Client");
/*while(true){
boolean next = reader.hasNext();
if (next) {
String line = reader.nextLine();
if(!line.equals(null)){
System.out.println("From another one: " + line);
}
}
writer.println(sc.next());
}*/
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if( server != null){
try {
server.close();
} catch (IOException e) {
//ignore -> Server getting closed
}
}
}
}
public String getKennung() {
return kennung;
}
public void setKennung(String kennung) {
this.kennung = kennung;
}
public Socket getServer() {
return server;
}
public void setServer(Socket server) {
this.server = server;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public static void main(String[] args) {
Client testClient = new Client("testClient", "127.0.0.1", 4567);
System.out.println("Client created");
}
}
And Last, but not least: The Class ClientHandler that implements Runnable and runs a Thread, where I read the input from the client and write it to the server.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
public class ClientHandler implements Runnable {
public ArrayBlockingQueue<String> messsages;
Socket client;
public ClientHandler(Socket client) {
this.messsages = new ArrayBlockingQueue<>(20);
this.client = client;
}
#Override
public void run() {
PrintWriter writer = null;
Scanner reader = null;
try {
reader = new Scanner(this.client.getInputStream());
writer = new PrintWriter(this.client.getOutputStream(), true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String line = "";
while (!line.equals(".bye")) {
//System.out.println("Got something? " + reader.hasNext());
line = reader.nextLine();
if (reader.hasNext()) {
if (!line.equals(null)) {
System.out.println("Vom Client: " + line);
}
}
//writer.println("Admin-Message"); // Zum Testen
}
}
}
Thanks to everybody, who helps me with this problem.
I currently have a desktop server written using the NIO java library in non-blocking mode. You can find the full server project here. I have also created a non-blocking NIO client for testing purposes. You can find that project here. In the end, the server is supposed to be used for an android instant messaging application. I have based both the client and server on the idea of sending 'Packets' to communicate. What i mean by this is, references of the Packet class are packed into byte buffers and sent through socket channels. They are then de-serialized on the other side and executed.
My current issue:
It seems that my test client drops it's connection when it connects to the server. I know the problem is client sided because when I connect to my server through command line using telnet, the connection does not drop. The weird part is that the server is printing out that a connection to it has been established but never says a connection has been lost/terminated when the client drops the connection.
This is the Client class that handles all the nio networking...
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.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.baiocchi.enigma.client.test.Engine;
import org.baiocchi.enigma.client.test.databundle.DataBundle;
import org.baiocchi.enigma.client.test.packet.Packet;
import org.baiocchi.enigma.client.test.ui.LogType;
import org.baiocchi.enigma.client.test.ui.Logger;
public class Client extends Thread {
private boolean running;
private final int port;
private SocketChannel connection;
private final ByteBuffer buffer;
private Selector selector;
private List<DataBundle> pendingDataBundleQue;
public Client(int port) {
this.port = port;
pendingDataBundleQue = new LinkedList<DataBundle>();
buffer = ByteBuffer.allocate(8192);
try {
selector = initiateSelector();
connection = initiateConnection();
} catch (IOException e) {
e.printStackTrace();
}
}
private Selector initiateSelector() throws IOException {
return SelectorProvider.provider().openSelector();
}
private SocketChannel initiateConnection() throws IOException {
SocketChannel connection = SocketChannel.open();
connection.configureBlocking(false);
connection.connect(new InetSocketAddress("localhost", port));
connection.register(selector, SelectionKey.OP_CONNECT);
return connection;
}
public SocketChannel getConnection() {
return connection;
}
#Override
public void start() {
running = true;
super.start();
}
public void addToPendingDataBundleQue(DataBundle bundle) {
synchronized (pendingDataBundleQue) {
pendingDataBundleQue.add(bundle);
}
}
#Override
public void run() {
while (running) {
System.out.println("loop");
try {
synchronized (pendingDataBundleQue) {
System.out.println("Checking for que changes.");
if (!pendingDataBundleQue.isEmpty()) {
System.out.println("Found que change.");
SelectionKey key = connection.keyFor(selector);
key.interestOps(SelectionKey.OP_WRITE);
}
}
System.out.println("Selecting keys");
selector.select();
System.out.println("Creating selected keys list.");
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
System.out.println("scrolling through list");
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
System.out.println("invalid");
continue;
} else if (key.isConnectable()) {
System.out.println("connect");
establishConnection(key);
} else if (key.isReadable()) {
System.out.println("read");
readData(key);
} else if (key.isWritable()) {
System.out.println("write");
writeData(key);
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
System.out.println("Broke loop");
}
private void writeData(SelectionKey key) throws IOException {
synchronized (pendingDataBundleQue) {
SocketChannel connection = (SocketChannel) key.channel();
for (DataBundle bundle : pendingDataBundleQue) {
System.out.println("sent packet");
connection.write(bundle.getBuffer());
}
pendingDataBundleQue.clear();
if (pendingDataBundleQue.isEmpty()) {
Logger.write("All packets sent.", LogType.CLIENT);
connection.keyFor(selector).interestOps(SelectionKey.OP_READ);
}
}
}
private void readData(SelectionKey key) throws IOException, ClassNotFoundException {
buffer.clear();
int byteCount;
try {
byteCount = connection.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction closed.", LogType.CLIENT);
connection.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Attempting to terminate connection.", LogType.CLIENT);
key.channel().close();
key.cancel();
}
Engine.getInstance().getPacketProcessor().processData(buffer);
}
private void establishConnection(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
try {
if (channel.finishConnect()) {
Logger.write("Connection established.", LogType.CLIENT);
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
Logger.write("Failed to establish connection.", LogType.CLIENT);
key.channel().close();
key.cancel();
return;
}
}
}
The server class that handles all the server networking is here.
package org.baiocchi.enigma.server.network;
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.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.Iterator;
import org.baiocchi.enigma.server.Engine;
import org.baiocchi.enigma.server.databundle.DataBundle;
import org.baiocchi.enigma.server.ui.components.logger.LogType;
import org.baiocchi.enigma.server.ui.components.logger.Logger;
public class Server extends Thread {
private boolean running;
private final int port;
private ServerSocketChannel server;
private final ByteBuffer buffer;
private Selector selector;
private ArrayList<DataBundle> pendingDataBundleQue;
public Server(int port) {
this.port = port;
buffer = ByteBuffer.allocate(8192);
pendingDataBundleQue = new ArrayList<DataBundle>();
try {
server = ServerSocketChannel.open().bind(new InetSocketAddress("localhost", port));
server.configureBlocking(false);
selector = SelectorProvider.provider().openSelector();
server.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void start() {
running = true;
super.start();
}
public void terminateConnection(SocketChannel channel) {
SelectionKey key = channel.keyFor(selector);
try {
key.channel().close();
key.cancel();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addToPendingPacketQue(DataBundle bundle) {
synchronized (pendingDataBundleQue) {
pendingDataBundleQue.add(bundle);
}
}
#Override
public void run() {
while (running) {
try {
synchronized (pendingDataBundleQue) {
if (!pendingDataBundleQue.isEmpty()) {
for (DataBundle bundle : pendingDataBundleQue) {
SelectionKey key = bundle.getChannel().keyFor(selector);
key.interestOps(SelectionKey.OP_WRITE);
}
}
}
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
} else if (key.isAcceptable()) {
acceptConnection(key);
} else if (key.isReadable()) {
readData(key);
} else if (key.isWritable()) {
writeData(key);
}
}
} catch (IOException | ClassNotFoundException e) {
Logger.writeException("Internal server error.", LogType.SERVER);
Logger.writeException(e.getMessage(), LogType.SERVER);
}
}
}
private void writeData(SelectionKey key) throws IOException {
DataBundle bundle = null;
for (DataBundle b : pendingDataBundleQue) {
if (b.getChannel().equals((SocketChannel) key.channel())) {
bundle = b;
break;
}
}
if (bundle == null) {
Logger.writeException("Couldn't find out bound packet in list.", LogType.SERVER);
return;
}
SocketChannel connection = bundle.getChannel();
connection.write(bundle.getBuffer());
connection.keyFor(selector).interestOps(SelectionKey.OP_READ);
pendingDataBundleQue.remove(bundle);
}
private void readData(SelectionKey key) throws IOException, ClassNotFoundException {
SocketChannel channel = (SocketChannel) key.channel();
buffer.clear();
int byteCount;
try {
byteCount = channel.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction terminated.", LogType.SERVER);
channel.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Terminating connection.", LogType.SERVER);
key.channel().close();
key.cancel();
return;
}
Engine.getInstance().getPacketProcessor().processData(buffer, channel);
}
private void acceptConnection(SelectionKey key) throws IOException {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel connection = channel.accept();
connection.configureBlocking(false);
connection.register(selector, SelectionKey.OP_READ);
Logger.write("Connection established.", LogType.SERVER);
}
}
Thank you in advance!
There is no evidence here that the client has dropped the connection. If it had, one of these blocks would have executed at the server:
try {
byteCount = channel.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction terminated.", LogType.SERVER);
channel.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Terminating connection.", LogType.SERVER);
key.channel().close();
key.cancel();
return;
}
I conclude that the client isn't dropping the connection at all.
I note that you have a method called Logger.writeException() which you are never using to log an exception, and that your log messages are back to front: catch (IOException ) indicates a connection error, on which you should log the actual exception, and readBytes == -1 indicates 'connection terminated', which is not an error.
I also note a missing return in the corresponding code in the client.
NB Closing the channel cancels the key. You don't need to cancel it yourself.
I have code which works with one client connection. What I need is ability for the server to handle multiple client requests using multithreaded approach.
I found some solutions, but it's not meet my requirements, like this, or this
Server.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends User {
private Socket clientSocket;
private ServerSocket serverSocket;
public Server() {
super();
}
private void createConnection() {
try {
InetAddress locIP = InetAddress.getByName("127.0.0.1");
serverSocket = new ServerSocket(9999, 0, locIP);
// serverSocket = new ServerSocket(4444, 4444, InetAddress.getByName("192.168.0.101"));
} catch (IOException e) {
System.err.println("Could not listen on port: 9999 ." + e);
System.exit(1);
}
}
private void closeConnection() {
try {
serverSocket.close();
} catch (IOException e) {
System.err.println(e);
}
}
#Override
public void connect() {
createConnection();
//Socket clientSocket=null;
try {
clientSocket = serverSocket.accept();
System.out.println("Client connected! "
+ "IP: "
+ clientSocket.getInetAddress()
+ ", port: "
+ clientSocket.getPort());
} catch (IOException e) {
System.err.println("Accept failed. " + e);
System.exit(1);
}
}
#Override
public void disconnect() {
try {
clientSocket.close();
} catch (IOException e) {
System.err.println(e);
}
closeConnection();
}
#Override
public Socket getSocket() {
return clientSocket;
}
#Override
public String toString() {
return new String("Server");
}
}
Client.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client extends User {
private Socket socket;
public Client() {
super();
}
#Override
public Socket getSocket() {
return socket;
}
#Override
public void connect() {
try {
InetAddress locIP = InetAddress.getByName("127.0.0.1");
// socket = new Socket(9999, 0, locIP);
// socket = new Socket("localhost", 9999); oryginalny
socket = new Socket(locIP, 9999);
} catch (UnknownHostException e) {
System.err.println("The host not found! " + e);
System.exit(1);
} catch (IOException e) {
System.err.println("Can't find connection! " + e);
System.exit(1);
}
}
#Override
public void disconnect() {
try {
socket.close();
} catch (IOException e) {
System.err.println(e);
}
}
#Override
public String toString() {
return new String("Client");
}
}
SendButton.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JTextPane;
#SuppressWarnings("serial")
public class SendButton extends JButton {
private JTextPane incomingMessages;
private JTextPane messageToSend;
private User user;
public SendButton(User user, JTextPane incomingMessages, JTextPane messageToSend) {
super("Send!");
this.user = user;
this.incomingMessages = incomingMessages;
this.messageToSend = messageToSend;
this.addActionListener(new SendListener());
}
public class Write {
private PrintStream out;
public Write() {
try {
out = new PrintStream(new BufferedOutputStream(
user.getSocket().getOutputStream(), 1024), false);
} catch (IOException e) {
System.err.println(e);
}
}
public void send(String message) {
if (message != null) {
out.println(message);
out.flush();
incomingMessages.setText(new String(incomingMessages.getText() + "\nMe: " + message));
}
}
}
public class SendListener implements ActionListener {
private Write write = new Write();
private String toSend;
#Override
public void actionPerformed(ActionEvent event) {
toSend = messageToSend.getText();
if (toSend != null || event.getActionCommand() == "\n") {
write.send(toSend);
}
messageToSend.setText(new String(""));
}
}
}
You need to create a new Runnable class, whose data members consist of a Socket and its input and output streams. This class is used on the server side. Its run() method is responsible for all I/O to that client. Then your accept() loop just looks like this:
while (true)
{
new Thread(new ConnectionHandler(serverSocket.accept())).start();
}
where ConnectionHandler implements Runnable as above.
simply what you need to do is after accepting the request from the client (Using main thread), then the request pass to a new thread with the client socket and process the request inside the new thread. So the main thread is free to accept new requests.
I'm trying to play around with a simple client-server program, eventually aiming to make it a two-way communication. For some reason when I just instantiate the client class to make it connect to the server (without sending any data), it doesn't work, and throws Unable to establish loopback connection exception. When I put a while loop of reading through stdin, it works then. I need to establish a connection first, and then once in a while send a message to the server. How can I fix it?
Client code:
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;
public class ClientCore {
SelectionKey selkey;
Selector sckt_manager ;
SocketChannel sc;
public ClientCore() {
try {
sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(new InetSocketAddress(8888));
// should not proceed until connect is finished
while (!sc.finishConnect()) {
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sc != null) {
try {
sc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
void send(String message) {
try {
if (!message.equalsIgnoreCase("end")) {
System.out.println("Sending a request to the server ...");
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
sc.write(buffer);
}
} catch (IOException e) {
}
}
public static void main(String[] args) throws Exception {
ClientCore cl = new ClientCore();
cl.send("hello");
}
}
Server code:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.spi.SelectorProvider;
public class ServerCore extends Thread {
SelectionKey selkey = null;
Selector sckt_manager = null;
public void run() {
try {
coreServer();
} catch (Exception ej) {
ej.printStackTrace();
}
}
private void coreServer() {
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
try {
ssc.socket().bind(new InetSocketAddress(8888));
while (true) {
sckt_manager = SelectorProvider.provider().openSelector();
ssc.configureBlocking(false);
SocketChannel sc = ssc.accept();
register_server(ssc, SelectionKey.OP_ACCEPT);
if (sc == null) {
} else {
System.out
.println("Received an incoming connection from "
+ sc.socket().getRemoteSocketAddress());
printRequest(sc);
System.err.println("testing 1");
String HELLO_REPLY = "Sample Display";
ByteBuffer buffer = ByteBuffer.wrap(HELLO_REPLY .getBytes());
System.err.println("testing 2");
sc.write(buffer);
System.err.println("testing 3");
sc.close();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ssc != null) {
try {
ssc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception E) {
System.out.println("Ex in servCORE " + E);
}
}
private static void printRequest(SocketChannel sc) throws IOException {
ReadableByteChannel rbc = Channels.newChannel(sc.socket()
.getInputStream());
WritableByteChannel wbc = Channels.newChannel(System.out);
ByteBuffer b = ByteBuffer.allocate(1024); // read 1024 bytes
while (rbc.read(b) != -1) {
b.flip();
while (b.hasRemaining()) {
wbc.write(b);
System.out.println();
}
b.clear();
}
}
public void register_server(ServerSocketChannel ssc, int selectionkey_ops)
throws Exception {
ssc.register(sckt_manager, selectionkey_ops);
}
public static void main(String[] args) {
ServerCore st = new ServerCore();
st.coreServer();
}
}
I'm doing an exercise requires making a server - client chat program using Java Non-Blocking IO. At the moment, the way the program works is simple: when a client send a message to the server, the server (which already keep track of all the clients) echo the message back to all the clients.
This is my some parts of my server-side code:
public static ByteBuffer str_to_bb(String msg) {
try {
return encoder.encode(CharBuffer.wrap(msg));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
private static void broadcastMessage(String nickname, String message) {
System.out.println(">clientSocketChannels size " + clientSocketChannels.size());
Iterator clientSocketChannelsIterator = clientSocketChannels.iterator();
while (clientSocketChannelsIterator.hasNext()) {
SocketChannel sc = (SocketChannel) clientSocketChannelsIterator.next();
try {
ByteBuffer bb = str_to_bb(message);
System.out.println("bufferRemaining: " + bb.remaining()); // returns 2048
int writeResult = sc.write(bb);
System.out.println("writeResult: " + writeResult); // returns 2048
} catch (IOException e) {
e.printStackTrace();
}
}
}
The following is my client-side code:
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* Created by ThaiSon on 7/6/2015.
*/
public class ChatRoomClientGUI {
private JTextArea textAreaMessages;
private JTextField textFieldMessage;
private JButton buttonSendMsg;
private JPanel jPanel1;
private JLabel txtFieldInfo;
private static InetAddress inetAddress;
private static final int PORT = 1234;
private static Socket socket = null;
private static Scanner input = null;
private static PrintWriter output = null;
private static ChatRoomClientGUI singleton;
public ChatRoomClientGUI() {
singleton = this;
buttonSendMsg.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (e.getButton() == MouseEvent.BUTTON1) {
String message = textFieldMessage.getText();
output.println(message);
textFieldMessage.setText("");
}
}
});
}
public static void main(String[] args) {
JFrame promptFrame = new JFrame();
Object nickname = JOptionPane.showInputDialog(promptFrame, "Enter your nickname:");
promptFrame.dispose();
JFrame frame = new JFrame("ChatRoomClientGUI");
frame.setContentPane(new ChatRoomClientGUI().jPanel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
System.out.println("> Client with nickname " + nickname);
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
accessServer(nickname.toString());
}
private static void accessServer(String nickname) {
try {
socket = new Socket(inetAddress, PORT);
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname); // Register nickname with the server
//TODO update the txtFieldInfo content
// Create a new thread to listen to InputStream event
InputStreamEvent inputStreamEvent = new InputStreamEvent(socket);
inputStreamEvent.start();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleInputStream(){
String response = input.nextLine();
System.out.println("TODO " + response);
singleton.textAreaMessages.append(response + "\n");
}
static class InputStreamEvent extends Thread{
Socket socket;
public InputStreamEvent(Socket socket){
this.socket = socket;
}
public void run(){
try {
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[2048];
int read;
while (true){
if(inputStream.available() > 0){
handleInputStream();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The problem I'm facing with now is that when I use a client (which works well with the old multithreaded server) to send message to the server, the client only get return the first message it sends. All the next responses from the server is empty (the server does send back, but only an empty message).
So my attempts to debug includes:
check if the messages from the client has reach the server or not. They does.
log the buffer.remaining() and socketChannel.write(buffer) result as shown above, all the log result seems to be normal to me.
Hope you guys can help me with this.
This:
if(inputStream.available() > 0){
Get rid of this test. With it, your client is smoking the CPU. Without it, it will block in readLine() as God intended.
Are you sure your server is still sending lines? with line terminators? If it isn't, readLine() will block forever looking for one, until end of stream or an exception occurs.
I referred the code explain by EJP on this link Java NIO Server/Client Chat App - sending data only by closing the socket
it solves my problem. use this code
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Scanner;
import java.nio.channels.SelectionKey;
import java.net.InetSocketAddress;
public class Client {
public static void main(String args[]) {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
Scanner scanner = new Scanner(System.in);
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
boolean isConnected = socketChannel.connect(new InetSocketAddress("localhost", 5000));
if(isConnected) {
System.out.println("Connected, de-registering OP_CONNECT");
}
new Thread(new Runnable(){
private SocketChannel socketChannel;
private Selector selector;
public Runnable init(SocketChannel socketChannel, Selector selector) {
this.socketChannel = socketChannel;
this.selector = selector;
return this;
}
public void run() {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
while(!Thread.interrupted()) {
int keys = selector.select();
if(keys > 0) {
for(SelectionKey key : selector.selectedKeys()) {
if(key.isConnectable()) {
boolean finishConnectResult = socketChannel.finishConnect();
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
System.out.println("Finished Connect : " + finishConnectResult);
}
if(key.isReadable()) {
int bytesRead = 0;
while((bytesRead = socketChannel.read(buf)) > 0) {
buf.flip();
while(buf.hasRemaining()) {
System.out.print((char)buf.get());
}
buf.clear();
}
if(bytesRead == -1) {
key.channel().close();
}
}
}
}
Thread.sleep(10);
}
} catch(IOException e) {
e.printStackTrace();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}.init(socketChannel, selector)).start();
while(true) {
while(scanner.hasNextLine()) {
buf.clear();
buf.put(scanner.nextLine().getBytes());
buf.flip();
socketChannel.write(buf);
buf.flip();
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
I have done the mistake setting this flag
key.interestOps(SelectionKey.OP_READ);
)
instead of below.
use this
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);