I wrote a quite a few of these. Good practice for learning networking.
I would suggest using Java NIO though, why? Because socket programming requires a thread to be created for each user (Multi-threaded) which creates a lot of overhead.
Java NIO only uses one thread because it uses selectors to manage its channels. Which makes more scalable.
https://github.com/7winds/Java-NIO-N...ree/master/src
Java NIO
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A basic Chat Server using the Java NIO library.
*
* The server listens for incoming text messages sent from the Client
* and reads the messages to the console.
*
* @author SeVen
*/
public class Server implements Runnable {
public static final Logger logger = Logger.getLogger(Server.class.getName());
/**
* The address that the {@link ServerSocketChannel} binds on.
*/
public static final String ADDRESS = "localhost";
/**
* The port that the {@link ServerSocketChannel} binds on.
*/
public static final int PORT = 3000;
/**
* The maximum time to wait in milliseconds. (10 seconds)
*/
public static final int TIME_OUT = 10000;
/**
* The server socket channel that operates as a selectable channel
* for stream-orientated sockets.
*/
private ServerSocketChannel serverChannel;
/**
* A multiplexor of {@link SelectableChannel} Objects.
*/
private Selector selector;
/**
* Maps an arrays of bytes to their SocketChannels.
*/
private Map<SocketChannel, byte[]> dataTracking = new HashMap<SocketChannel, byte[]>();
/**
* The main entry to the application.
*
* @parma args
* The command line arguments.
*/
public static void main(String[] args) {
new Server().run();
}
/**
* The constructor that will instantiate this class.
*/
public Server() {
initialize();
}
/**
* Prepares the server to accept incoming connections.
*/
public void initialize() {
logger.info("Initializing the server...");
if (selector != null) return;
if (serverChannel != null) return;
try {
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(ADDRESS, PORT));
logger.info("Binding server to address " + ADDRESS + " on port " + PORT + ".");
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void run() {
//The server is now accepting connections.
try {
while(!Thread.currentThread().isInterrupted()) {
selector.select(TIME_OUT);
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
accept(key);
}
if (key.isWritable()) {
// a channel is ready for writing
logger.info("Writing message...");
write(key);
}
if (key.isReadable()) {
// a channel is ready for reading
logger.info("Reading message...");
read(key);
}
}
}
} catch(IOException ex) {
logger.log(Level.SEVERE, "A problem occurred while running the server.", ex);
} finally {
closeConnection();
}
}
/**
* Handles properly closing a servers connection.
*/
public void closeConnection() {
if (selector != null) {
try {
selector.close();
serverChannel.socket().close();
serverChannel.close();
} catch(IOException ex) {
logger.log(Level.SEVERE, "A problem occurred while closing the servers connection.", ex);
}
}
}
/**
* Accepts an incoming connection.
*
* @param key
* The key for this session.
*
* @throws IOException
*
*/
private void accept(SelectionKey key) throws IOException{
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
logger.info("Connection received from: " + socketChannel.socket().getInetAddress());
}
/**
* Writes a message to the client.
*
* @param key
* The key for this session.
*
* @throws IOException
*/
public void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
byte[] data = dataTracking.get(channel);
channel.write(ByteBuffer.wrap(data));
key.interestOps(SelectionKey.OP_READ);
}
/**
* Reads a message from the client.
*
* @param key
* The key for this session.
*
* @throws IOException
*/
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
readBuffer.clear();
int read;
try {
read = channel.read(readBuffer);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Problem occured while reading the message. Closing connection...", ex);
key.cancel();
channel.close();
return;
}
if (read == -1) {
logger.log(Level.WARNING, "No message detected, closing connection...");
channel.close();
key.cancel();
return;
}
readBuffer.flip();
byte[] data = new byte[read];
readBuffer.get(data, 0, read);
logger.info("Received from: [" + channel.socket().getInetAddress() + "] - " + new String(data));
}
}
Code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A basic client for connecting to the chat server. The client
* allows the user to input a text message from the input stream
* and sends the message to the server.
*
* @author SeVen
*/
public class Client implements Runnable {
public static final Logger logger = Logger.getLogger(Client.class.getName());
/**
* The multiplexor of {@link SelectableChannel} objects
*/
private Selector selector;
/**
* The message to be sent to the server.
*/
private String message = "";
/**
* The main entry-point to the client application.
*
* @param args
* The command-line arguments.
*/
public static void main(String[] args) {
new Client().run();
}
@Override
public void run() {
SocketChannel channel;
try {
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress("localhost", 3000));
while(!Thread.interrupted()) {
selector.select(1000);
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) {
continue;
}
if (key.isConnectable()) {
// a connection was established with a remote server.
logger.info("Connected to " + channel.socket().getInetAddress() + " on port " + channel.socket().getPort() + ".");
connect(key);
}
if (key.isWritable()) {
// a channel is ready for writing
write(key);
}
if (key.isReadable()) {
// a channel is ready for reading
read(key);
}
}
}
} catch(IOException ex) {
logger.log(Level.SEVERE, "A problem occurred while running the client.", ex);
}
}
/**
* Establishes a connection between the server and client.
*
* @param key
* The key for this session.
*
* @throws IOException
*/
private void connect(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {
channel.finishConnect();
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_WRITE);
}
/**
* Reads a message sent from the server.
*
* @param key
* The key for this session.
*
* @throws IOException
*/
private void read (SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1000);
readBuffer.clear();
int length;
try{
length = channel.read(readBuffer);
} catch (IOException ex){
logger.log(Level.SEVERE, "A problem occurred while reading a message.", ex);
key.cancel();
channel.close();
return;
}
if (length == -1){
logger.log(Level.WARNING, "No message was read from the server.");
channel.close();
key.cancel();
return;
}
readBuffer.flip();
byte[] buff = new byte[1024];
readBuffer.get(buff, 0, length);
logger.info("Message from the server: " + new String(buff));
}
/**
* Writes a message from the client to the server.
*
* @param key
* The key for this session.
*
* @throws IOException
*/
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
//continuously allows a user to keep inputing messages until the user types "exit".
while(!message.equalsIgnoreCase("exit")) {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
//read the users input
message = reader.readLine();
//then write the message to the channel to prepare to be sent to server.
channel.write(ByteBuffer.wrap(message.getBytes()));
} catch (IOException ex) {
logger.log(Level.SEVERE, "A problem occurred while writing a message.", ex);
}
}
key.interestOps(SelectionKey.OP_READ);
}
}