I'm trying to transfer an mp3 file from Server to Client using Java NIO APIs.
In particular, I am trying to use transferTo & transferFrom methods.
I've already checked that the server recognizes the file appropriately and transfers to a FileChannel.
However, in the point of view of the client, it considers that the size of the FileChannel connected to the server is 0 which can be interpreted that the client did not receive any files from the channel.
Here are the results on the consoles of both server and client.
[Server]
Server is started...
java.nio.channels.SocketChannel[connected local=/127.0.0.1:7777 remote=/127.0.0.1:60430]Here comes a new client!
!!write activated!!
Channel size : 3622994
filename : C:\Users\InhoKim\Music\5 O'clock - Black Nut.mp3
java.nio.channels.SocketChannel[connected local=/127.0.0.1:7777 remote=/127.0.0.1:60430]The number of files transferred : 1
[Client]
!!read activated!!
Channel size : 0
How do I have to solve this problem?
Here are the full codes of both server and client
[Server]
import java.io.File;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.net.ServerSocket;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.channels.FileChannel;
import java.io.FileInputStream;
import java.util.Set;
import java.nio.ByteBuffer;
public class MusicServer {
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private ServerSocket serverSocket = null;
File dir = new File("C:\\Users\\InhoKim\\Music\\");
boolean test = true;
public static void main(String[] args) {
MusicServer ms = new MusicServer();
ms.initServer();
ms.startServer();
}
public void initServer() {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocket = serverSocketChannel.socket();
InetSocketAddress isa = new InetSocketAddress("localhost", 7777);
serverSocket.bind(isa);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch(Exception e) {e.printStackTrace();}
}
public void startServer() {
System.out.println("Server is started...");
try {
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
SelectableChannel channel = key.channel();
if (channel instanceof ServerSocketChannel) {
if (key.isAcceptable())
accept(key);
} else {
if (key.isWritable()) {
if(test)
write(key);
}
}
}
}
} catch(Exception e) {e.printStackTrace();}
}
private void accept(SelectionKey key) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
try {
SocketChannel sc = server.accept();
if (sc == null) return;
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println(sc.toString() + "Here comes a new client!");
} catch(Exception e) {e.printStackTrace();}
}
private void write(SelectionKey key) {
if(test)
System.out.println("!!write activated!!");
test = false;
SocketChannel sc = (SocketChannel) key.channel();
try {
File[] files = dir.listFiles();
int count = 0;
for (File file : files) {
count++;
FileInputStream fis = new FileInputStream(file);
FileChannel inChannel = fis.getChannel();
System.out.println("Channel size : " + (int)inChannel.size());
System.out.println("filename : " + file);
inChannel.transferTo(0, (int)inChannel.size(), sc);
fis.close();
break;
}
System.out.println(sc.toString() + "The number of files transferred : " + count);
} catch(Exception e) {e.printStackTrace();}
}
}
[Client]
import java.io.File;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.SelectionKey;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.util.Set;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
public class MusicClient {
private Selector selector = null;
private SocketChannel sc = null;
int count = 0;
public static void main(String[] args) {
MusicClient mc = new MusicClient();
mc.startServer();
}
public void initServer() {
try {
selector = Selector.open();
sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7777));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
} catch(Exception e) {e.printStackTrace();}
}
public void startServer() {
initServer();
startReader();
}
public void startReader() {
try {
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isReadable()) {
read(key);
System.exit(0);
}
}
}
} catch(Exception e) {e.printStackTrace();}
}
private void read(SelectionKey key) {
System.out.println("!!read activated!!");
SocketChannel sc = (SocketChannel) key.channel();
try {
File dir = new File("D:\\Target2\\");
FileOutputStream fos = new FileOutputStream(dir + "\\" + count + ".mp3"); // file name has been set as a number
count++;
FileChannel outChannel = fos.getChannel();
System.out.println("Channel size : " + (int)outChannel.size());
outChannel.transferFrom(sc, 0, (int)outChannel.size());
fos.close();
} catch(Exception e) {e.printStackTrace();}
}
}
in the point of view of the client, it considers that the size of the FileChannel connected to the server is 0
There is no such thing as a 'FileChannel connected to the server'. File channels are connected to files. What you have is a FileChannel connected to a new FileOutputStream which has just created a new file, whose size is therefore zero.
which can be interpreted that the client did not receive any files from the channel.
No, it can be interpreted as you telling the FileChannel to transfer zero bytes, which it did correctly.
You can't get the size of a SocketChannel, because it doesn't mean anything (consider a peer that just keeps sending and never closes the connection). So in this case you have to use Long.MAX_VALUE as the size. The transfer will complete when there are no more bytes to be transferred, or indeed before, especially as you are using non-blocking mode.
EDIT I don't see any reason to use non-blocking mode in the client. I would remove that, and the Selector. And transferFrom() must be called in a loop that terminates when it returns zero. Using transferTo() in the server is considerably more complex if you must use non-blocking mode there, as you have to register OP_WRITE when it returns zero and re-select and restart using an adjusted offset when you get it, and deregister OP_WRITE if it doesn't return zero.
Related
I have written a simple NIO Server and Inner-Client (Inside the same program)in a single program such that Server receives data from outside and the Inner-Client sends the data received by the server to out side Server. I am running both the processes continuously in two parallel threads using While() loops. Now the problem is, I will be receiving data at a very high speed to the Inside server and everything I receive, I will send them to the outer server using the Inside client. Some times, the retrieval of the data from the Buffer resulting in half the size of the total string. That means I am receiving "HELLO", but the total string is "HELLO SERVER". This is just an example. I will receive very long strings. Similarly, after sending the data to Outer-server through Inner client I will be listening for data and I am receiving the same half-strings.Is there any way I can eliminate these Half-strings and get the full-length string. I have to get the full string without any fail.
I am using while loops for the process. This is making the CPU utilization go high like 50%. Is there any way I can reduce the CPU utilization without using Thread.sleep method? Because I need to continuously listen to data from the Outer parties. They may send 2-4 strings for one single request. I tried using Executor service thread for running the processes continuously but it requires some sleep to be included. If I include some sleep I am not able to get the String and if I don't include the sleep my CPU-Utilization is going very high (50-60%). Can anyone help me with these two issues?
Here is my code:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Main {
static SocketChannel channel;
public static void main(String[] args) throws IOException {
System.out.println("Listening for connections on : 8888"); //8888
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8888));
channel = serverChannel.accept();
System.out.println("Connected...");
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.configureBlocking(false);
ReceiveFromOMS receivefromOMS;
SendToExchange sendExchange;
receivefromOMS = new ReceiveFromOMS();
sendExchange = new SendToExchange();
Thread t1 = new Thread(receivefromOMS);
Thread t2 = new Thread(sendExchange);
t1.start();
t2.start();
}
}
class ReceiveFromOMS extends Thread{
public static SocketChannel channel;
static ByteBuffer buffer = ByteBuffer.allocate(1024);
static ServerSocketChannel serverChannel ;
public static int ReceiveFromOMSPort;
BlockingQueue<String> fromOMSqueue = new LinkedBlockingQueue<>(30);
#Override
public void run(){
while(true){
try {
receiveFromOMS();
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
try {
Thread.sleep(5000);
} catch (InterruptedException ex1) { }
}
}
}
public void receiveFromOMS() throws InterruptedException{
try {
int numRead = -1;
numRead = channel.read(buffer);
while(numRead==0){
numRead = channel.read(buffer);
}
if (numRead == -1) {
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
System.out.println("Connection closed by client: " + remoteAddr);
channel.close();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
fromOMSqueue.add(new String(data));
String msg = fromOMSqueue.poll();
System.out.println("OutGoing To Exchange>> " + msg);
SendToExchange.sendToEchange(msg);
buffer.flip();
buffer.clear();
} catch (IOException ex) {
System.err.println(ex.getMessage());
Thread.sleep(5000);
}
}
}
class SendToExchange extends Thread{
static SocketChannel channel;
static ByteBuffer bb = ByteBuffer.allocateDirect(1024);
static Charset charset = Charset.forName("UTF-8");
public byte[] data;
public static String message;
#Override
public void run(){
try {
while(true){
receive();
Thread.sleep(100);
}
} catch (IOException | InterruptedException ex) {
System.err.println(ex.getMessage());
try {
Thread.sleep(5000);
} catch (InterruptedException ex1) {}
}
}
public static void sendToEchange(String msg){
try {
bb = stringToByteBuffer(msg, charset);
channel.write(bb);
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
public void receive() throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
int numRead = -1;
numRead = channel.read(buffer);
while (numRead == 0) {
numRead = channel.read(buffer);
}
if (numRead == -1) {
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
System.out.println("Connection closed by Exchange: " + remoteAddr);
channel.close();
return;
}
buffer.flip();
data = new byte[numRead];
buffer.get(data);
message = new String(data);
System.out.println("Incoming from Exchange>> " + message);
buffer.clear();
}
public static ByteBuffer stringToByteBuffer(String msg, Charset charset){
return ByteBuffer.wrap(msg.getBytes(charset));
}
}
Lets assume that your server is appending to each string an End of message marker string, e.g. "<EOM>", then the following modification of your code (treat it as a sketch since I did not verified it completely) can be used to wait for the full string:
String end = "<EOM>";
StringBuilder curStr = new StringBuilder();
int numRead = 0;
while(-1 != (numRead = channel.read(buffer))){
curStr.append(new String(buffer.array(), 0, numRead, Charset.forName("UTF-8")));
int endIdx = curStr.indexOf(end);
if (endIdx != -1) {
fromOMSqueue.add(curStr.substring(0, endIdx + end.length()));
break;
}
}
if (numRead == -1) {
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
System.out.println("Connection closed by client: " + remoteAddr);
channel.close();
return;
}
String msg = fromOMSqueue.poll();
I know this question has been asked before, and I've tried the different solutions, but I got stuck in the implementation part.. :(
Currently multiple clients can connect to the server, I used the multithreaded KnockKnock server/client example from javadocs, and edited it slightly so that you can just send messages to the server, and it will echo them back to you, but I want to be able to make it so that if client 1 sends a message, then the server will broadcast them back to all the clients connected to the server.
I've tried looking around and saw people in the same position as I am in now, and they were told to make a list to keep track of all the connections, and then iterate through the list and send the message, but I really don't know in which class to put it or how to handle it.
If someone could show me or just give me hints to where I should start, it would be greatly appreciated, as I'm really just stuck at the moment :(
Here's where I'm at so far:
Server:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
public static void main(String[] args) throws IOException {
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(4444)) {
while (listening) {
ServerThread thread = new ServerThread(serverSocket.accept());
thread.start();
}
} catch (IOException e) {
System.err.println("Could not listen on port " );
System.exit(-1);
}
}
}
ServerThread
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.List;
public class ServerThread extends Thread{
private Socket socket = null;
public ServerThread(Socket socket) {
super("MultiServerThread");
this.socket = socket;
}
public void run() {
try (
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
) {
while (true) {
String input = in.readLine();
System.out.println(input);
out.println("ecco " + input);
if (input.equals("Bye"))
break;
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client (not sure if necessary, but here is it anyways)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) throws IOException {
try (
Socket kkSocket = new Socket("172.30.242.51", 4444);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
) {
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
if(in != null) {
String input = stdIn.readLine();
out.println("Client: " + input);
System.out.println(in.readLine());
out.flush();
}
}
} catch (UnknownHostException e) {
System.err.println("Don't know about host " );
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " );
System.exit(1);
}
}
}
Have a nice weekend =)
Operation 'write' is blocking in your example. So iterating by all connections can lead to delays and blocking your push thread. Also always set SO_TIMEOUT for socket if you do not want to have memory leaks.
I suggest using netty server
It has very nice functionality for pushing data to all connected clients - look for ChannelGroup
Why don't you use NIO to solve this problem?
A simple example:
public class EchoServer {
public static void main(String[] args) throws Exception {
//Create TCP server channel
ServerSocketChannel serv = ServerSocketChannel.open();
ServerSocket sock = serv.socket();
//Create a socket on your IP and port (i.e: localhost:12345)
SocketAddress addr = new InetSocketAddress(12345);
//Bind server socket and socket address
sock.bind(addr);
//Configure socket so all its methods won't be blocking
serv.configureBlocking(false);
//Create a selector to attend all the incoming requests
Selector selector = Selector.open();
//Register into the selector the accept request type
serv.register(selector,SelectionKey.OP_ACCEPT);
//Create a common buffer
ByteBuffer commonBuffer = ByteBuffer.allocate(10000);
commonBuffer.clear();
Iterator<SelectionKey> it = null;
ByteBuffer channelBuffer = null;
for (;;){ //Infinite loop
System.out.println("Waiting for events......");
selector.select(); // This call do is blocking
System.out.println("New event received");
it = selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
System.out.println(String.format("Processing %s", key));
it.remove(); // Remove it to avoid duplications
try{
if (key.isAcceptable()) {
System.out.println("Received new connection request");
processConnectionRequest(serv, selector);
}else if (key.isReadable()) {
System.out.println("Received new reading request");
processReadingRequest(selector, commonBuffer, key);
}else if (key.isWritable()) {
System.out.println("Received new writing request");
processWritingRequest(key);
}
}catch(Exception e){
key.cancel();
try {
key.channel().close();
} catch (Exception ce) {}
}//end catch
}//end while
}//end for
}//end main
private static void processWritingRequest(SelectionKey key) throws IOException {
SocketChannel cli = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
System.out.println(String.format("Wrinting into the channel %s", cli));
buf.flip();//prepare the buffer
buf.rewind();
cli.write(buf);
if (buf.hasRemaining()) {
//If there is more content remaining, compact the buffer
buf.compact();
} else {
buf.clear();
key.interestOps(SelectionKey.OP_READ);
}
}
private static void processReadingRequest(Selector selector, ByteBuffer commonBuffer, SelectionKey key)
throws IOException {
SocketChannel cli = (SocketChannel) key.channel();
if (cli.read(commonBuffer) == -1) {
System.out.println(String.format("Closing channel %s", cli));
cli.close(); // internally calls key.cancel()
}
else {//Send the data to all the channels
commonBuffer.flip();//prepare the buffer
Iterator<SelectionKey> it2 = selector.keys().iterator();
System.out.println("Writing data to all the channels");
SelectionKey keys = null;
while(it2.hasNext()) {
keys = it2.next();
System.out.println(String.format("Writing in %s", keys));
ByteBuffer buf = (ByteBuffer) keys.attachment();
if(buf!=null)
{
buf.put(commonBuffer);
keys.interestOps(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
commonBuffer.rewind();
}
}
commonBuffer.clear();
}
}
private static void processConnectionRequest(ServerSocketChannel serv, Selector selector)
throws IOException, ClosedChannelException {
ByteBuffer channelBuffer;
SocketChannel cli = serv.accept();
cli.configureBlocking(false);
channelBuffer = ByteBuffer.allocate(10000);
System.out.println(String.format("Registering new reading channel: %s", cli));
cli.register(selector, SelectionKey.OP_READ, channelBuffer);
}
}
I am trying to write an application using Java that will allow me to transfer files between a server and a client that requests the file. I plan to do it using sockets. My algorithm is somewhat like this:
On Server:
Create the connection between client and server.
Once connected find the file u need to send to client.
Then send the size of file to client.
Then send file broken down in parts.
On Client
After connection is created, ask for the file.
Receive the file size, then accept data till u reach file size.
Stop.
Please correct me if i am wrong somewhere in the algorithm
This isn't really an "algorithm" question; you're designing a (simple) protocol. What you've described sounds reasonable, but it's too vague to implement. You need to be more specific. For example, some things you need to decide:
How does the receiving program know what filename it should save to? Should that be sent through the socket, or should it just ask the user?
How is the file size transmitted?
Is it a character string? If so, how is its length indicated? (With a null terminator? A newline?)
Is it a binary value? If so, how big? (32 bits or 64?) What endianness?
What does "broken down in parts" mean? If you're writing to a TCP socket, you don't need to worry about packet boundaries; TCP takes care of that.
Does the recipient send anything back, like a success or failure indication?
What happens when the whole file has been transmitted?
Should both ends assume that the connection must be closed?
Or can you send multiple files through a single connection? If so, how does the sender indicate that another file will follow?
Also, you're using the terms "client" and "server" backward. Typically the "client" is the machine that initiates a connection to a server, and the "server" is the machine that waits for connections from clients.
You can also add Acknowledgement from server once a particular part of the file is recieved,
similar to what we have in HTTP protocol , that would ensure proper delivery of the file has been received on the server.
Here is the method that I use, it uses the socket's input and output streams to send and receive the files, and when it's done, it will automatically restart the server and reconnect to it from the client.
Server Code:
package app.server;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Functions
{
private static ServerSocket server;
private static Socket socket;
public static void startServer(int port)
{
try
{
server = new ServerSocket(port);
socket = server.accept();
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static void restartServer()
{
new Thread()
{
#Override
public void run()
{
try
{
socket = server.accept();
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
}
public static void sendFile(String inputFilePath)
{
FileInputStream fis;
BufferedInputStream bis;
OutputStream os;
BufferedOutputStream bos;
try
{
File input = new File(inputFilePath);
fis = new FileInputStream(input);
bis = new BufferedInputStream(fis);
os = socket.getOutputStream();
bos = new BufferedOutputStream(os);
byte[] buffer = new byte[1024];
int data;
while(true)
{
data = bis.read(buffer);
if(data != -1)
{
bos.write(buffer, 0, 1024);
}
else
{
bis.close();
bos.close();
break;
}
}
}
catch (FileNotFoundException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
restartServer();
}
}
Client Code:
package app.client;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Functions
{
private static Socket socket;
private static String hostName;
private static int portNumber;
public static void connectToServer(String host, int port)
{
new Thread()
{
#Override
public void run()
{
try
{
hostName = host;
portNumber = port;
socket = new Socket(host, port);
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
}
private static void reconnectToServer()
{
try
{
socket = new Socket(hostName, portNumber);
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void receiveFile(String outputFilePath)
{
InputStream is;
BufferedInputStream bis;
FileOutputStream fos;
BufferedOutputStream bos;
try
{
File output = new File(outputFilePath);
is = socket.getInputStream();
bis = new BufferedInputStream(is);
fos = new FileOutputStream(output);
bos = new BufferedOutputStream(fos);
byte[] buffer = new byte[1024];
int data;
while(true)
{
data = bis.read(buffer);
if(data != -1)
{
bos.write(buffer, 0, 1024);
}
else
{
bis.close();
bos.close();
break;
}
}
}
catch (IOException ex)
{
Logger.getLogger(Functions.class.getName()).log(Level.SEVERE, null, ex);
}
reconnectToServer();
}
}
This method works very well, I use it for my server and client file transfer program, all you need to do is enter the Server Host's IP address and choose a port number (I use 8888).
I have created a basic Client Server that will send image files in a specified directory over a network. The code worked last week but I came back to it today and it seems that I am only getting one file on the server side, even though the client prints out that it has sent all the image files in the directory.
It may be something in the client code but I think it is something on the server side.
Any help is greatly appreciated and if you have a more efficient solution, I am happy to change my code as necessary. My code is below:
ImageServer
package com.encima.network.server;
import java.io.*;
import java.net.*;
public class ImageServer{
ServerSocket ss;
Socket s;
ObjectOutputStream oos;
int port = 4440;
public ImageServer() throws IOException {
try {
ss = new ServerSocket(port);
System.out.println("Server started on Port: " + port);
} catch(IOException e) {
System.out.println("Serevr: Port-" + port + " not available, exiting.");
System.exit(0);
}
System.out.println("Server: Waiting for Client Connection...");
while(true) {
try {
s = ss.accept();
new ImageHandler(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
ImageServer is = new ImageServer();
}
}
ImageHandler
package com.encima.network.server;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import javax.imageio.ImageIO;
public class ImageHandler implements Runnable {
Socket s;
int count = 0;
public ImageHandler(Socket socket) {
s = socket;
Thread t = new Thread(this);
t.start();
}
#Override
public void run() {
try {
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
FileOutputStream fos = new FileOutputStream("image" + System.nanoTime() + ".jpg");
count++;
//BufferedImage in = ImageIO.read(ois);
//ImageIO.write(in, "jpg", fos);
int ch = 0;
while(true) {
ch = ois.read();
if(ch == -1) {
break;
}
fos.write(ch);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Finally, the ImageClient
package com.encima.network.client;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import javax.imageio.ImageIO;
import com.encima.network.ImageFilter;
public class ImageClient {
Socket s;
String ip = "localhost";
int port = 4440;
ObjectOutputStream oos;
public ImageClient(File[] files) throws IOException, ClassNotFoundException, InterruptedException {
try {
s = new Socket(ip, port);
System.out.println("Client connected to Server via " + ip + " on port 80");
} catch (Exception e) {
System.out.println("Client: Cannot find Host: " + ip + ". Exiting.");
System.exit(0);
}
oos = new ObjectOutputStream(s.getOutputStream());
for(File f: files) {
sendFile(f);
}
oos.close();
//System.out.println("Written Image " + i + " of " + files.length);
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
File dir = new File("/Users/christophergwilliams/Dropbox/PhD/Projects/PhD/Year 1/GSN/images");
File[] files = dir.listFiles(new ImageFilter());
ImageClient ic = new ImageClient(files);
}
public void sendFile(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
//BufferedImage b = ImageIO.read(file);
//ImageIO.write(b, "jpg", oos);
int ch = 0;
while(true) {
ch = fis.read();
if(ch == -1) {
break;
}
oos.write(ch);
}
oos.flush();
System.out.println("Image Sent");
}
}
I am aware that it is a lot of code to read through but I do appreciate any help I can get on this!
I may be wrong but, for the sake of efficiency and network traffic, would it be beneficial to send the images as a zip from the client to the server?
Why are you using ObjectInputStream at all? You're not reading or writing any serialized objects - just raw binary data. Use whatever InputStream is provided, and read from that.
Anyway, that's not the big problem. The big problem is that you're just writing several files to one stream, with no indication of where one file is meant to finish and the next one is meant to start. How were you expecting to split the multiple files up? Options:
Use a delimiter between files (very ugly - you'd have to potentially escape any data which looked like the delimiter as you went along)
Prefix each file with its length
Send each file on a different connection
(You're also reading and writing a single byte at a time. Use the overloads of read/write which accept byte arrays.)
I have created a simple server client application using java NIO.
I used a single selector there for accepting connection, reading data and writing.
But I want an application where 1 selector will be busy in accepting the connection while the 2nd selector will read the data and the 3rd selector will write the data.
Means I donot want to put all the load into single selector.
How to achieve this?
Is there any online help
Thanks
Deepak.
// Create the selector
Selector selector = Selector.open();
// Create two non-blocking server sockets on 80 and 81
ServerSocketChannel ssChannel1 = ServerSocketChannel.open();
ssChannel1.configureBlocking(false);
ssChannel1.socket().bind(new InetSocketAddress(80));
// Register both channels with selector
ssChannel1.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// Wait for an event
selector.select();
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator();
// Process each key
while (it.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)it.next();
// Remove it from the list to indicate that it is being processed
it.remove();
// Check if it's a connection request
if (selKey.isAcceptable()) {
// Get channel with connection request
ServerSocketChannel ssChannel = (ServerSocketChannel)selKey.channel();
// Accepting a Connection on a ServerSocketChannel
SocketChannel sChannel = serverSocketChannel.accept();
// If serverSocketChannel is non-blocking, sChannel may be null
if (sChannel == null) {
// There were no pending connection requests; try again later.
// To be notified of connection requests,
} else {
// Use the socket channel to communicate with the client
}
}
}
}
Usually, on a non-blocking tcp server, first accept, then read, then write,
you need to register the selector in this order to make sense.
Example code
Here is a full example of non-blocking io:
TcpChannelTest.java: (a TestNG testing class)
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.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
import org.testng.annotations.Test;
/**
* tcp channel test
*
* #author eric
* #date Sep 2, 2012 9:17:40 PM
*/
public class TcpChannelTest {
public String serverHost = "localhost";
public int serverPort = 12345;
private ServerSocketChannel server;
private int clientSerial = 0;
private int clientCount = 5;
// test tcp non-blocking channel,
#Test
public void testTcpNonBlockingChanne() throws IOException, InterruptedException {
// start server
startServerNonBlocking();
Thread.sleep(500); // wait server to be ready, before start client,
// start clients
for (int i = 0; i < clientCount; i++) {
startClientOnce();
}
// shutdown server,
Thread.sleep(500); // wait client to be handled,
shutdownServer();
}
// start non-blocking server,
private void startServerNonBlocking() throws IOException {
new Thread(new Runnable() {
#Override
public void run() {
try {
server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(serverHost, serverPort)); // bind,
server.configureBlocking(false); // non-blocking mode,
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> readyKeys = selector.selectedKeys();
// process each ready key...
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), "accept connection");
client.configureBlocking(false);
// prepare for read,
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// read
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer inBuf = ByteBuffer.allocate(1024);
while (client.read(inBuf) > 0) {
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), new String(inBuf.array(), StandardCharsets.UTF_8));
}
// prepare for write,
client.register(selector, SelectionKey.OP_WRITE);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
String response = "hi - from non-blocking server";
byte[] bs = response.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(bs);
client.write(buffer);
// switch to read, and disable write,
client.register(selector, SelectionKey.OP_READ);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}, "t-server-threads").start();
}
// close server,
private void shutdownServer() {
try {
if (server != null) {
server.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* <p>
* tcp client - via channel,
* </p>
* <p>
* It send once request.
* </p>
*
* #throws IOException
*/
private void startClientOnce() throws IOException {
// start client in a new thread
new Thread(new Runnable() {
#Override
public void run() {
try {
SocketChannel client = SocketChannel.open(new InetSocketAddress(serverHost, serverPort));
// write
String request = "hello - from client [" + Thread.currentThread().getName() + "}";
byte[] bs = request.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(bs);
while (buffer.hasRemaining()) {
client.write(buffer);
}
// read
ByteBuffer inBuf = ByteBuffer.allocate(1024);
while (client.read(inBuf) > 0) {
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), new String(inBuf.array(), StandardCharsets.UTF_8));
}
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}, "t-channelClient-" + clientSerial++).start();
}
}
It is possible to register a channel with multiple Selectors using register(Selector sel, int ops). You then register different interest ops on each of the selectors:
// After the accepting a connection:
SelectionKey readKey = sChannel.register(readSelector, SelectionKey.OP_READ);
// When you have something to write:
SelectionKey writeKey = sChannel.register(writeSelector, SelectionKey.OP_WRITE);