Removing redundant code from http server, filehandler - java

I have set up my webserver, for the time being only localhost, at port 8080 for testing.
public class WebServer {
static int port = 8080; //not the final port
static String ip = "127.0.0.1"; // not the final IP
public static void main(String[] args) throws IOException {
InetSocketAddress i = new InetSocketAddress(ip, port); //localhost - 127.0.0.1
HttpServer server = HttpServer.create(i, 0);
server.createContext("/tester", new testHandler("index.html"));
server.createContext("/startpage", new PagesHandler());
server.createContext("/online", new OnlineHandler());
server.createContext("/logfile", new LogFileHandler());
server.setExecutor(null);
server.start();
}
I have a seperate handler for each of the page references, which basicly does the same thing.
Example for the PagesHandler()
static class PagesHandler implements HttpHandler {
String content = "public/";
#Override
public void handle(HttpExchange he) throws IOException {
File file = new File(content + "index.html");
byte[] bytesToSend = new byte[(int) file.length()];
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.read(bytesToSend, 0, bytesToSend.length);
} catch (IOException ie) {
ie.printStackTrace();
}
he.sendResponseHeaders(200, bytesToSend.length);
try (OutputStream os = he.getResponseBody()) {
os.write(bytesToSend, 0, bytesToSend.length);
}
}
}
This makes me have a lot of redundant code, which i really dont see the need for.
My question is. I am guessing there is a way to parse the handler with a filename, instead of the hardcodeded filename in File file,
But how would i go about doing that?
would a constructor of the handler with a String parameter for the filename do?
like so
public pagesHandler(String fileName){
}
and in the main something like this:
server.createContext("/tester", new testHandler("index.html"));
And if not how would i go about reducing the rudandant code ?

Related

How can I include css file using com.sun.net.httpserver?

I'm trying to write a simple http server, using com.sun.net.httpserver class. I send html file (index.html) to browser on startup, but I don't know how to include an external css file. It works when css code is placed inside html file. I know, that browser should send a request, asking server for css file, but I'm not sure how to receive this request and send back this file to browser. I attach a fragment of my code below, if it could be helpful.
private void startServer()
{
try
{
server = HttpServer.create(new InetSocketAddress(8000), 0);
}
catch (IOException e)
{
System.err.println("Exception in class : " + e.getMessage());
}
server.createContext("/", new indexHandler());
server.setExecutor(null);
server.start();
}
private static class indexHandler implements HttpHandler
{
public void handle(HttpExchange httpExchange) throws IOException
{
Headers header = httpExchange.getResponseHeaders();
header.add("Content-Type", "text/html");
sendIndexFile(httpExchange);
}
}
static private void sendIndexFile(HttpExchange httpExchange) throws IOException
{
File indexFile = new File(getIndexFilePath());
byte [] indexFileByteArray = new byte[(int)indexFile.length()];
BufferedInputStream requestStream = new BufferedInputStream(new FileInputStream(indexFile));
requestStream.read(indexFileByteArray, 0, indexFileByteArray.length);
httpExchange.sendResponseHeaders(200, indexFile.length());
OutputStream responseStream = httpExchange.getResponseBody();
responseStream.write(indexFileByteArray, 0, indexFileByteArray.length);
responseStream.close();
}
There is no built-in method for handling static content. You have two options.
Either use a light-weight webserver for static content like nginx, but than distribution of your application will be more difficult.
Or create your own file serving classes. For that, you have to create a new context in your web server:
int port = 8080;
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
// ... more server contexts
server.createContext("/static", new StaticFileServer());
And than create the class that will serve your static files.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
#SuppressWarnings("restriction")
public class StaticFileServer implements HttpHandler {
#Override
public void handle(HttpExchange exchange) throws IOException {
String fileId = exchange.getRequestURI().getPath();
File file = getFile(fileId);
if (file == null) {
String response = "Error 404 File not found.";
exchange.sendResponseHeaders(404, response.length());
OutputStream output = exchange.getResponseBody();
output.write(response.getBytes());
output.flush();
output.close();
} else {
exchange.sendResponseHeaders(200, 0);
OutputStream output = exchange.getResponseBody();
FileInputStream fs = new FileInputStream(file);
final byte[] buffer = new byte[0x10000];
int count = 0;
while ((count = fs.read(buffer)) >= 0) {
output.write(buffer, 0, count);
}
output.flush();
output.close();
fs.close();
}
}
private File getFile(String fileId) {
// TODO retrieve the file associated with the id
return null;
}
}
For the method getFile(String fileId); you can implement any way of retrieving the file associated with the fileId. A good option is to create a file structure mirroring the URL hierarchy. If you don't have many files, than you can use a HashMap to store valid id-file pairs.

Send String from server to client - save it as textfile

I know how to send a file from a server to a client but how do I send a textstring to the client where the client save the string as a file? Should I use PrintWriter for this issue?
This is the code for sending a file from server to client:
Sending files through sockets
What I want to do is to (instead of sending a file), sending a String from the server and let the client receive it and save it as a file.
public class SocketFileExample {
static void server() throws IOException {
ServerSocket ss = new ServerSocket(3434);
Socket socket = ss.accept();
InputStream in = new FileInputStream("send.jpg");
OutputStream out = socket.getOutputStream();
copy(in, out);
out.close();
in.close();
}
static void client() throws IOException {
Socket socket = new Socket("localhost", 3434);
InputStream in = socket.getInputStream();
OutputStream out = new FileOutputStream("recv.jpg");
copy(in, out);
out.close();
in.close();
}
static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[8192];
int len = 0;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
public static void main(String[] args) throws IOException {
new Thread() {
public void run() {
try {
server();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
client();
}
}
in that case use ObjectOutputStream at server side to write String and use ObjectInputStream at client side to read String coming from server..
so your server() method will look like this..
static void server() throws IOException {
ServerSocket ss = new ServerSocket(3434);
Socket socket = ss.accept();
OutputStream out = socket.getOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(out);
oout.writeObject("your string here");
oout.close();
}
and client() method will look like this..
static void client() throws IOException, ClassNotFoundException {
Socket socket = new Socket("localhost", 3434);
InputStream in = socket.getInputStream();
ObjectInputStream oin = new ObjectInputStream(in);
String stringFromServer = (String) oin.readObject();
FileWriter writer = new FileWriter("received.txt");
writer.write(stringFromServer);
in.close();
writer.close();
}
also throw ClassNotFoundException in main method.
public static void main(String[] args) throws IOException, ClassNotFoundException {.......}
done...

Unable to connect to Netty Server twice

I'm working on a simple Application.
I'm trying to understand my the exception, but i just can't.
How to reproduce:
Starting Netty Server
Connecting to Netty Server with Client -> Valid Response, Server + Client working fine.
Client closed (0 Channels are active, proven by a debug thread)
NEW Client tries to read -> Error: java.net.SocketException: Software caused connection abort: recv failed
Restarting Server, goto 2.
Server source:
public class Server {
private final int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
final EventLoopGroup boss = new NioEventLoopGroup();
final EventLoopGroup worker = new NioEventLoopGroup();
try {
final ServerBootstrap b = new ServerBootstrap();
b.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new SocketChannelInitializer());
final ChannelFuture f = b.bind(port).sync();
final ChannelFuture c = f.channel().closeFuture();
System.out.println("- DONE -");
c.sync();
} finally {
worker.shutdownGracefully();
boss.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).run();
}
}
My Initialisizer:
public static final String PACKET = "packet";
public static final String STRING_DECODER = "stringDecoder";
public static final String NETWORK_HANDLER = "networkHandler";
#Override
public void initChannel(SocketChannel ch) throws Exception {
System.out.println("Creating new Channel!");
final ChannelPipeline p = ch.pipeline();
p.addLast(NETWORK_HANDLER, new NetworkHandler());
p.addLast(STRING_DECODER, new StringDecoder(CharsetUtil.UTF_8));
p.addLast(new Testdecoder());
p.addLast(new ChatAdapter());
}
Server DOES process the request, but it cannot read a correct Message.
My Client:
public static void main(String[] args) throws IOException {
final Socket s = new Socket();
s.connect(new InetSocketAddress("localhost", 8080));
final InputStream is = s.getInputStream();
final OutputStream os = s.getOutputStream();
os.write(0x0);
os.write("username".getBytes(CharsetUtil.UTF_8));
os.flush();
System.out.println("!");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[4096];
while (true) {
int n = is.read(b);
if (n < 0) {
break;
}
baos.write(b, 0, n);
}
byte data[] = baos.toByteArray();
System.out.println(new String(data, CharsetUtil.UTF_8));
System.out.println("- DONE -");
s.close();
}
Using Netty 4.0.23.Final
Example Application:
https://github.com/BjoernAkAManf/Chat
Startup Server.main(String[] args) and run Client.main(String[] args) twice. You'll get a proper output first. 2nd run will fail.
I tested anything i could possibly think of. Really appreciate help here. Thank you
Despite my first Idea this is not a 'real' server issue, nor is it an issue with netty. The Client uses oio (java.IO) Sockets to connect to a Nio Netty Server.
Changing NioEventLoopGroup to OioEventLoopGroup and NioServerSocketChannel to OioServerSocketChannel resolves this weird issue.
Furthermore one may change the Client to work with Nio. In the following Example i will use java.nio:
final SocketChannel c = SocketChannel.open();
c.connect(new InetSocketAddress("localhost", 8007));
final ByteBuffer buf = ByteBuffer.allocate(5);
// fill Buffer
buf.put((byte) 0x0);
buf.put("Test".getBytes(CharsetUtil.UTF_8));
// Flip buffer
buf.flip();
// Write buffer to socket
while (buf.hasRemaining()) {
c.write(buf);
}
System.out.println("Reading ...");
// Read
buf.flip();
while (true) {
int n = c.read(buf);
if (n < 0) {
break;
}
final String d = new String(buf.array());
if(!d.isEmpty()) System.out.println("'" + d + "'");
buf.clear();
}
// Finish Programm
System.out.println("- DONE -");
c.close();
That example works perfectly fine. Obviously the problem was right infront of my eyes.
Now it's fixed.
This can be solved by providing Thread.sleep(time) before you close the server and start again.

Null pointer exception occurs while trying to read repeatedly from an InputStream

The code works fine when I close the client just after sending one instruction. But when I want a client and server connection to persist, so that the client can send multiple instructions to the server one after another, I get a Null pointer exception at the server and the message java.net.SocketException: Socket is closed at the client. This happens after the client sends a file to the server and the server successfully receives it. Need help. The error occurs at the Connection class code line switch(clientMsg). It seems to me that for some reason the BufferedReader in goes null, but I might be mistaken about that. The code is as follows. Thanks.
Server
public class server {
private static ServerSocket serverSocket;
private static Socket socket = null;
public static void print(Object s) {
System.out.println(s);
}
#SuppressWarnings("resource")
public static void main (String args[]) throws IOException {
System.out.print("Specify listening port: ");
Scanner _a = new Scanner(System.in);
int a = _a.nextInt();
try{
serverSocket = new ServerSocket(a);
}
catch(IOException e) {
System.out.println(e);
}
while (true) {
try {
socket = serverSocket.accept();
print("Connected to " + socket);
Thread client = new Thread(new Connection(socket));
client.start();
}
catch (IOException e) {
print(e);
}
}
}
}
Connection
public class Connection implements Runnable {
public static void print(Object s) {
System.out.println(s);
}
private Socket socket;
private BufferedReader in = null;
public Connection(Socket client) {
this.socket = client;
}
#Override
public void run(){
try {
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
String clientMsg;
while (true) {
clientMsg = in.readLine();
switch (clientMsg) {
case "1":
receiveFile(); //method code not included
break;
default:
print("Command not recognized");
break;
}
//in.close();
}
}//try run()
catch (IOException e) {
print(e);
}
}
Client
public class client {
private static Socket connectToServer;
private static String fileName;
private static BufferedReader keybrdIn;
private static PrintStream msgToServer;
public static void println(Object e) {
System.out.println(e);
}
public static void print(Object e) {
System.out.print(e);
}
public static void main(String args[]) throws IOException{
try{
print("Enter IP: ");
String ip = new Scanner(System.in).nextLine();
print("Enter port: ");
int port = new Scanner(System.in).nextInt();
connectToServer = new Socket(ip, port);
keybrdIn = new BufferedReader(new InputStreamReader(System.in));
}catch(IOException e) {
println(e);
}
msgToServer = new PrintStream(connectToServer.getOutputStream());
while (true) {
try {
switch(Integer.parseInt(action())) { //action() method code not included
case 1:
msgToServer.println("1");
sendFile();
break;
default:
println("Invalid input");
break;
}
}catch (IOException e) {
println(e);
}
}
}
sendFile()
public static void sendFile() throws IOException {
print("Enter file name: ");
fileName = keybrdIn.readLine();
File file = new File(fileName);
byte[] bytearray = new byte[8192];
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
OutputStream os = connectToServer.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(file.getName());
int count;
while ((count = dis.read(bytearray)) > 0){
dos.write(bytearray, 0, count);
}
dis.close();
dos.flush();
dos.close();
}
receiveFile()
public void receiveFile() {
try {
int count;
DataInputStream clientFileStream = new DataInputStream(socket.getInputStream());
String fileName = clientFileStream.readUTF();
OutputStream fileOutput = new FileOutputStream("_" + fileName);
byte[] mybytearray = new byte[8192];
BufferedOutputStream bos = new BufferedOutputStream(fileOutput);
System.out.println("Downloading " + fileName + " ...");
//outToClient().writeBytes("Uploading. Please wait...\n");
while ((count = clientFileStream.read(mybytearray)) > 0){
bos.write(mybytearray, 0, count);
}
fileOutput.close();
bos.close();
clientFileStream.close();
}
catch (IOException e) {
print(e);
}
}
In sendFile(), you close the data output stream which closes your underlying connection's output stream.
According to the documentation of Socket.getOutputStream():
"Closing the returned OutputStream will close the associated socket".
Since you already closed stream, it will also close socket as well as Eyal mentioned. However, at the moment you close the stream, server side will aware of that and return -1 for read() results.
So, even if you didn't specify file length at beginning, this will generally works well.
However, since you already closed stream, you can't reuse it no matter what. To fix this issue, probably you need to change your Client class so that Client should create socket connection, send files, close socket. That's one lifecycle of opened client socket.
Or maybe in while loop of Client class, 1) take ip, port, and filename to send 2) Create new Thread and provide those information so let thread open connection, send file, close connection 3) and in the meantime, client while() can keep take next ip, port, and filename to send from the user. By doing this, you don't need to make client program wait until file transfer to be completed.
Regarding the NPE in the server, readLine() returns null at end of stream. You are ignoring it. You should be testing for it immediately after the call, and if null close the socket and exit the read loop.

Synchronize server with client java sockets

Currently I am working on a server/client application which sends data using java with Runnable and threads. The problem is that the client is sending the data and when the server starts to read it the client has already finished and closed the connection which on the server side only a partially of the data is arrived, can they be setup to be synchronized?
this is the client:
private void ConnectionToServer(final String ipAddress, final int Port) {
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
socket = new Socket(ipAddress, Port);
bos = new BufferedOutputStream(socket.getOutputStream());
dos = new DataOutputStream(socket.getOutputStream());
File f = new File("C:/Users/lukeLaptop/Downloads/RemoveWAT22.zip");
String data = f.getName()+f.length();
byte[] b = data.getBytes();
sendBytes(b, 0, b.length);
dos.flush();
bos.flush();
bis.close();
dos.close();
//clientProcessingPool.submit(new ServerTask(socket));
} catch (IOException ex) {
Logger.getLogger(ClientClass.class.getName()).log(Level.SEVERE, null, ex); } finally {
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
public void sendBytes(byte[] myByteArray, int start, int len) throws IOException {
if (len < 0) {
throw new IllegalArgumentException("Negative length not allowed");
}
if (start < 0 || start >= myByteArray.length) {
throw new IndexOutOfBoundsException("Out of bounds: " + start);
}
// Other checks if needed.
// May be better to save the streams in the support class;
// just like the socket variable.
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.writeInt(len);
if (len > 0) {
dos.write(myByteArray, start, len);
}
}
server code:
private void acceptConnection() {
try {
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
ServerSocket server = new ServerSocket(8080);
while (true) {
socket = server.accept();
System.out.println("Got a client !");
bis = new BufferedInputStream(socket.getInputStream());
dis = new DataInputStream(socket.getInputStream());
String data = readBytes().toString();
System.out.println(data);
bos.close();
dis.close();
//clientProcessingPool.submit(new ClientTask(socket));
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
} catch (Exception io) {
io.printStackTrace();
}
}
public byte[] readBytes() throws IOException {
// Again, probably better to store these objects references in the support class
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
int len = dis.readInt();
byte[] data = new byte[len];
if (len > 0) {
dis.readFully(data);
}
return data;
}
You mixed up many things:
Variables start most of the time with a lowercase letter, e.g. int port, int ipAddress
Classes start with a uppercase letter, e.g. Client, Server
only open one Data*stream on a socket. new DataInputStream(socket.getInputStream()) or new BufferedInputStream(socket.getInputStream()), but not both
If you need both, chain them: new DataInputStream(new BufferedInputStream(socket.getInputStream()));
KISS (Keep it short & simple)
If you use a DataInputStream, then use the given functionality of sending objects and primitives, e.g. sendUTF(), sendInt(), sendShort(), and so on...
Name your vars right: servertask is a client thread? no
Move long anonymous classes to a new class
Don't use port 8080, this port is used for many other application and will cause problems
example code regarding your example an my advices:
Server
public class Server implements Runnable {
private void acceptConnection() {
Thread serverThread = new Thread(this);
serverThread.start();
}
#Override
public void run() {
try {
ServerSocket server = new ServerSocket(8081);
while (true) {
Socket socket = server.accept();
System.out.println("Got a client !");
// either open the datainputstream directly
DataInputStream dis = new DataInputStream(socket.getInputStream());
// or chain them, but do not open two different streams:
// DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
// Your DataStream allows you to read/write objects, use it!
String data = dis.readUTF();
System.out.println(data);
dis.close();
// in case you have a bufferedInputStream inside of Datainputstream:
// you do not have to close the bufferedstream
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server().acceptConnection();
}
}
description:
main: create a new Server Object, which is a Runnable
acceptConnections: create a Thread
run:
open a Serversocket
wait for a connection
open exactly one stream
read the Data
close the stream and wait for next connection
Client
public class Client {
private static void sendToServer(String ipAddress, int port) throws UnknownHostException, IOException {
Socket socket = new Socket(ipAddress, port);
// same here, only open one stream
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
File f = new File("C:/Users/lukeLaptop/Downloads/RemoveWAT22.zip");
String data = f.getName()+f.length();
dos.writeUTF(data);
dos.flush();
dos.close();
}
public static void main(String[] args) throws UnknownHostException, IOException {
Client.sendToServer("localhost", 8081);
}
}
description (This one is straight forward):
open socket
open DataStream
send Data
flush and close

Categories