I'm trying to write a client-server application in Java with an XML-based protocol.
But I have a great problem!
See this part of client code:
InputStream incoming = skt.getInputStream(); //I get Stream from Socket.
OutputStream out = skt.getOutputStream();
[...]
XMLSerializer serializer = new XMLSerializer();
//This create an XML document.
tosend = WUTPClientWriter.createMessage100(projectid, cpuclock, cpunumber);
serializer.setOutputByteStream(out);
serializer.serialize(tosend);
At this point server fall in deadlock. It wait for EOF but I can't send it because if I use
out.close();
or
skt.shutdownOutput();
I close the Socket and I must keep this connection alive.
I can't send '\0' becouse I get Parse Error in the server.
How can I do it? Can I "close" the output stream without closing the socket?
RESOLVED
I've created new class XMLStreamOutput and XMLStreamInput with advanced Stream gesture.
I've resolved with this four class:
1)
public class XMLOutputStream extends ByteArrayOutputStream {
private DataOutputStream outchannel;
public XMLOutputStream(OutputStream outchannel) {
super();
this.outchannel = new DataOutputStream(outchannel);
}
public void send() throws IOException {
byte[] data = toByteArray();
outchannel.writeInt(data.length);
outchannel.write(data);
reset();
}
}
2)
public class XMLSender {
public static void send(Document tosend, OutputStream channel) throws TransformerConfigurationException, IOException {
XMLOutputStream out = new XMLOutputStream(channel);
StreamResult sr = new StreamResult(out);
DOMSource ds = new DOMSource(tosend);
Transformer tf = TransformerFactory.newInstance().newTransformer();
try {
tf.transform(ds, sr);
} catch (TransformerException ex) {
Logger.getLogger(XMLSender.class.getName()).log(Level.SEVERE, null, ex);
}
out.send();
}
}
3)
public class XMLInputStream extends ByteArrayInputStream {
private DataInputStream inchannel;
public XMLInputStream(InputStream inchannel) {
super(new byte[2]);
this.inchannel = new DataInputStream(inchannel);
}
public void recive() throws IOException {
int i = inchannel.readInt();
byte[] data = new byte[i];
inchannel.read(data, 0, i);
this.buf = data;
this.count = i;
this.mark = 0;
this.pos = 0;
}
}
4)
public class XMLReceiver {
public static Document receive(InputStream channel) throws ParserConfigurationException, TransformerConfigurationException, IOException, SAXException {
DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder();
Document request = null;
XMLInputStream xmlin = new XMLInputStream(channel);
xmlin.recive();
request = docBuilder.parse(xmlin);
return request;
}
}
You don't want to close the socket's OutputStream, because the socket only has one OutputStream.
It looks like you just need to flush your OutputStream after writing to it.
out.flush();
EDIT: Thanks for the extra info. If you're reading the socket like this, receiver needs to know when you're done writing. An InputStream only knows you're done writing if you close the socket.
But since you have already stated you can't close the socket, you need another way of telling the receiving side that you're done. You either need to use a special type of stream which knows about the data being sent, or you need to set up a contract for writing/reading the appropriate amount of data.
It would probably be easiest to send the data as an Object (using ObjectOutputStream/ObjectInputStream--maybe you don't even need to convert to XML).
If you don't want the overhead associated with Object streams, the simple solution is to compute the length of the data being sent, and send that number just prior to sending the actual data. In this case, you can use a DataOutputStream/DataInputStream. Send the number of bytes to read, followed by the data. On the receiving side, read the number, then read the given number of bytes into a temporary variable and feed that to DocumentBuilder.parse(InputStream).
On the sending end, you would do this:
DataOutputStream out = new DataOutputStream(s.getOutputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLSerializer serializer = new XMLSerializer();
serializer.setOutputByteStream(baos);
tosend = WUTPClientWriter.createMessage100(projectid, cpuclock, cpunumber);
serializer.serialize(tosend);
out.writeInt(baos.size());
out.write(baos.toByteArray());
out.flush();
Then on the receiving end, you do something like the following:
DataInputStream in = new DataInputStream(s.getInputStream());
int len = in.readInt();
byte[] xml = new byte[len];
in.read(xml, 0, len);
Document doc = builder.parse(new ByteArrayInputStream(xml));
Related
I have a problem that I have not been able to solve and it does not occur to me that it could be.
I have a class to which I am passing an InputStream from the main method, the problem is that when transforming the InputString to String with the class IOUtils.toString of AWS, or with the IOUtils of commons-io, they return
an empty String
No matter what the problem may be, since inside the main class, it works correctly and returns the String it should, but when I use it inside the other class (without having done anything), it returns the empty String to me.
these are my classes:
public class Main {
public static void main(String [] args) throws IOException {
InputStream inputStream = new ByteArrayInputStream("{\"name\":\"Camilo\",\"functionName\":\"hello\"}".getBytes());
OutputStream outputStream = new ByteArrayOutputStream();
LambdaExecutor lambdaExecutor = new LambdaExecutor();
String test = IOUtils.toString(inputStream); //this test variable have "{\"name\":\"Camilo\",\"functionName\":\"hello\"}"
lambdaExecutor.handleRequest(inputStream,outputStream);
}
}
and this:
public class LambdaExecutor{
private FrontController frontController;
public LambdaExecutor(){
this.frontController = new FrontController();
}
public void handleRequest(InputStream inputStream, OutputStream outputStream) throws IOException {
//Service service = frontController.findService(inputStream);
String test = IOUtils.toString(inputStream); //this test variable have "" <-empty String
System.exit(0);
//service.execute(inputStream, outputStream, context);
}
}
I used the debug tool, and the InputStream object is the same in both classes
By the time that you've passed the stream into handleRequest(), you've already consumed the stream:
public static void main(String [] args) throws IOException {
InputStream inputStream = new ByteArrayInputStream("{\"name\":\"Camilo\",\"functionName\":\"hello\"}".getBytes());
OutputStream outputStream = new ByteArrayOutputStream();
LambdaExecutor lambdaExecutor = new LambdaExecutor();
String test = IOUtils.toString(inputStream); //this consumes the stream, and nothing more can be read from it
lambdaExecutor.handleRequest(inputStream,outputStream);
}
When you took that out, the method worked as, as you said in the comments.
If you want the data to be re-useable, you'll have to use the reset() method if you want the same data again, or close and re-open the stream to re-use the object with different data.
// have your data
byte[] data = "{\"name\":\"Camilo\",\"functionName\":\"hello\"}".getBytes();
// open the stream
InputStream inputStream = new ByteArrayInputStream(data);
...
// do something with the inputStream, and reset if you need the same data again
if(inputStream.markSupported()) {
inputStream.reset();
} else {
inputStream.close();
inputStream = new ByteArrayInputStream(data);
}
...
// close the stream after use
inputStream.close();
Always close the stream after you use it, or use a try block to take advantage of AutoCloseable; you can do the same with the output stream:
try (InputStream inputStream = new ByteArrayInputStream(data);
OutputStream outputStream = new ByteArrayOutputStream()) {
lambdaExecutor.handleRequest(inputStream, outputStream);
} // auto-closed the streams
The reason you can't is because you can only read from a stream once.
To be able to read twice, you must call the reset() method for it to return to the beginning. After reading, call reset() and you can read it again!
Some sources don't support resetting it so you would actually have to create the stream again. To check if the source supports it, use the markSupported() method of the stream!
I wrote some code for a server and a client to transfer a file from the server to the client and it worked like a charm; however I have few questions. I want to build this code under a GUI, and I want to list all the files on the folder, but how can I make the client choose the file he wants after he see the list of files offered (how can I send a string to the server in order to choose the file)?
Server Code
import java.io.*;
import java.net.*;
class TCPServer {
public static void listfile(){
File folder = new File("c:/");
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
System.out.println("File " + listOfFiles[i].getName());
} else if (listOfFiles[i].isDirectory()) {
System.out.println("Directory " + listOfFiles[i].getName());
}
}
}
public static void main(String args[]) {
listfile();
while (true) {
ServerSocket welcomeSocket = null;
Socket connectionSocket = null;
BufferedOutputStream outToClient = null;
try {
welcomeSocket = new ServerSocket(3248);
connectionSocket = welcomeSocket.accept();
outToClient = new BufferedOutputStream(connectionSocket.getOutputStream());
} catch (IOException ex) {
// Do exception handling
}
if (outToClient != null) {
String FileName = "carexception.java";
File myFile = new File("C:\\"+FileName);
byte[] mybytearray = new byte[(int) myFile.length()];
FileInputStream fis = null;
try {
fis = new FileInputStream(myFile);
} catch (FileNotFoundException ex) {
// Do exception handling
}
BufferedInputStream bis = new BufferedInputStream(fis);
try {
bis.read(mybytearray, 0, mybytearray.length);
outToClient.write(mybytearray, 0, mybytearray.length);
outToClient.flush();
outToClient.close();
connectionSocket.close();
// File sent, exit the main method
return;
} catch (IOException ex) {
// Do exception handling
}
}
}
}
}
Client Code
import java.io.*;
import java.net.*;
import java.util.*;
class TCPClient {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
byte[] aByte = new byte[1];
int bytesRead;
Socket clientSocket = null;
InputStream is = null;
try {
clientSocket = new Socket("127.0.0.1", 3248);
is = clientSocket.getInputStream();
} catch (IOException ex) {
// Do exception handling
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (is != null) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
fos = new FileOutputStream("E:\\sss.java");
bos = new BufferedOutputStream(fos);
bytesRead = is.read(aByte, 0, aByte.length);
do {
baos.write(aByte);
bytesRead = is.read(aByte);
} while (bytesRead != -1);
bos.write(baos.toByteArray());
bos.flush();
bos.close();
clientSocket.close();
} catch (IOException ex) {
// Do exception handling
}
}
}
}
To accomplish what you're after you have to change quite a few things.
You can assume a specific protocol order, in the sense that the client needs to send a request to the server in order for the server to do anything, so the server is always in a listening state when the connection has been established.
You should,
Introduce a loop of sending requests and receiving responses
Figure out how to send a string object
Break up the file sending part so you don't allocate larger byte arrays than the OS can hand you (consider a file being 4GB for instance, allocating a byte array for the whole file can be troublesome)
So, with this in mind we can get going. Regarding step 1, this can be accomplished using a while loop. If we assume that the server always listens for a request, the server "request loop" can look something like this.
ClientRequest request;
while (request.getType() != RequestType.Complete) {
// receive new request
// depending on type, send response
}
We simply added two classes here, one ClientRequest that encapsulates a message from the client, and an enum RequestType that defines the type of request the client is interested in, for instance a file list or file contents.
public enum RequestType {
None, Complete, RequestFileList, RequestFileContent
}
public class ClientRequest {
private RequestType type;
public ClientRequest() {
type = RequestType.None;
}
public RequestType getType() {
return type;
}
}
Now we need to attach this to the socket somehow, so we add a method for receiving a request, and assign that request to the current request instance.
ClientRequest request = new ClientRequest();
while (request.getType() != RequestType.Complete) {
// receive new request
receiveRequest(clientSocket.getInputStream(), request);
if (request.getType() != RequestType.Complete) {
// pick a response
}
}
private void receiveRequest(DataInputStream socketStream, ClientRequest request) {
// get a type of request
byte type = socketStream.readByte();
request.setType(RequestType.from(type));
// get parameters for request, depending on type
if (request.getType() == RequestType.RequestFileContent) {
// receive file id (file name for instance, or some other id that you prefer)
String argument = readString(socketStream);
request.setArgument(argument);
}
}
This adds a from method in RequestType, to convert a byte to a request, a setType method in ClientRequest, and a readString method. We also add a new field and corresponding get and set methods in ClientRequest.
public enum RequestType {
// types as before
;
public static RequestType from(byte b) {
switch (b) {
case 1: return RequestType.Complete;
case 2: return RequestType.RequestFileList;
case 3: return RequestType.RequestFileContent;
default: return RequestType.None;
}
}
}
public class ClientRequest {
private String argument;
public void setType(RequestType value) {
type = value;
}
public String getArgument() {
return argument;
}
public void setArgument(String value) {
this.argument = value;
}
}
private String readString(DataInputStream socketStream) {
int length = socketStream.readInt();
byte[] stringBytes = new byte[length];
socketStream.read(stringBytes);
return new String(stringBytes, "UTF-8");
}
Now we get to the next step, responding to the request. Simply add a switch case and handle the type of request.
{
// depending on type, send response
handleRequest(clientSocket.getOutputStream(), request);
}
private void handleRequest(DataOutputStream socketStream, ClientRequest request) {
switch (request.getType()) {
case RequestType.RequestFileList: {
String[] fileList = getFileList(getCurrentDirectory());
// send response type
socketStream.write(ResponseType.ResponseFileList.getByte());
// send number of files
socketStream.writeInt(fileList.length);
// send each string
for (String fileName : fileList) {
sendString(socketStream, fileName);
}
}
break;
case RequestType.RequestFileContent: {
// send response type ResponseType.ResponseFileContent
// send length of file so other party can determine number of bytes to receive
// send file contents in chunks of a fixed byte array length
// send last part of file contents, if length of file is not evenly divided by array chunk size
}
break;
}
}
the sendString method is simply a "reversed order" of the readString method.
private void sendString(DataOutputStream socketStream, String value) {
int length = value.length();
socketStream.writeInt(length);
byte[] stringBytes = value.getBytes("UTF-8");
socketStream.write(stringBytes);
}
The ResponseType is an enum of values similar to the ones in RequestType, so the client can handle the type of response that the server sends.
With these changes, you will be able to request a file list and present the response of files that the server sends. When the user picks a file to receive, the client can send a new request to the server and the server can send the appropriate file contents to the client.
The client application will have to define a similar ClientRequest class (perhaps with the name ServerResponse) with corresponding methods that the server specified for reading from and writing to the socket streams. This can be abstracted further by encapsulating the socket in a class, with a listener pattern for when a request or response is received that the GUI can subscribe to, ..although this goes beyond my example.
If you feel I need to clarify anything, please leave a comment and I'll do my best to answer.
How do you ask files? By name! I think that server accepts commands and responds with responses. You may use the format for your commands to server: CMD_NAME, arg1, arg2, ... Arguments can be binary, dependent on command. By CMD_NAME, your server will distinguish what you want from it (either accept the file or provide one).
You have a problem that you accept only one type of request. You need a command mechanism to ask different requests from server. Server needs to parse those requests rather than give a hardcoded answer immediately. This will make it flexible.
http://www.javamex.com/tutorials/networking/simple_server_s_side.shtml I am sure that there a tons of other examples like this. Java sockets are reliable, there cannot be problem. Just start learning basics, how to communicate different messages between client and server. But, your problem is not Sockets-related at all. Imagine that you communicate over files: read requests from one and write responses into another. Which messages do you write then? This is called a protocol. You need to design a simple one.
Have you tried creating an array, so each file has it own index... When the client chooses the file he wants, then you return the file on certain array index.
~ btw you can serialize your array and send it to the client.
You can use ObjectOutputStream to send Strings or any other kind of Object via writeObject.
I have a file containing java objects, wrote with this code:
from(somewhere).process(new Processor() {
#Override
public void process(final Exchange exchange) {
...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(myObject);
exchange.getOut().setBody(bos.toByteArray());
}
}).to("file://pathFile");
And now, I want read them fastly. I don't know how can I do that, something like the following code I gess.
from("file://pathFile").convertBodyTo(String.class)
.split(body().tokenize("???")) // How can I tokenize my file ?
.streaming().threads(2)
.process(new Processor() {
#Override
public void process(final Exchange exchange) {
String filePath = (String) exchange.getIn().getHeader(Exchange.FILE_PATH);
File file = new File(filePath);
MyObject myObject = null;
try {
FileInputStream fis = new FileInputStream(file);
InputStream buffer = new BufferedInputStream(fis);
ObjectInput input = new ObjectInputStream(buffer);
Object obj = null;
while ((obj = input.readObject()) != null) {
// Do something
myObject = obj;
}
} catch (Exception e) {
...
} finally {
...
}
exchange.getIn().setBody(myObject);
}
}).to(somewhere);
EDIT: I edit my way to read object. There is still a problem with that code, we can't append to an ObjectOutputStream. That will corrupt the stream. There is a solution [here] for this problem. We can only write the stream header one time.
But If I do that, I wont be able to split and read my file with multiple threads. So can I split or access my file on ObjectOutputStream header ?
you just converted it to a String using convertBodyTo(String.class), therefore you have a String in the body rather than an InputStream....
I would like to send file from client to server and be able do it again in the future.
So my client connect to server and upload file, ok - it works but it hangs at the end..
so here is my code in client, the server side is quite similar.
private void SenderFile(File file) {
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = socket.getOutputStream();
IoUtil.copy(fis, os);
} catch (Exception ex) {
ex.printStackTrace();
}
}
IoUtils found on Stack :)
public static class IoUtil {
private final static int bufferSize = 8192;
public static void copy(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[bufferSize];
int read;
while ((read = in.read(buffer, 0, bufferSize)) != -1) {
out.write(buffer, 0, read);
}
out.flush();
}
}
Explanation: my client has a socket connected to server, and I send any file to him.
My server download it but hangs at the end because he is listening for more infromation.
If I choose another file, my server will download new data to the existing one.
How could I upload any file to server, make my server work on and be able download another one file properly?
ps. If I add to ioutil.copy at the end of function out.close my server will work on but the connection will be lost. I do not know what to do :{
After update:
Client side:
private void SenderFile(File file) {
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = socket.getOutputStream();
DataOutputStream wrapper = new DataOutputStream(os);
wrapper.writeLong(file.length());
IoUtil.copy(fis, wrapper);
} catch (Exception ex) {
ex.printStackTrace();
}
}
Server side (thread listening for any message from client):
public void run() {
String msg;
File newfile;
try {
//Nothing special code here
while ((msg = reader.readLine()) != null) {
String[] message = msg.split("\\|");
if (message[0].equals("file")) {//file|filename|size
String filename = message[1];
//int filesize = Integer.parseInt(message[2]);
newfile = new File("server" + filename);
InputStream is = socket.getInputStream();
OutputStream os = new FileOutputStream(newfile);
DataInputStream wrapper = new DataInputStream(is);
long fileSize = wrapper.readLong();
byte[] fileData = new byte[(int) fileSize];
is.read(fileData, 0, (int) fileSize);
os.write(fileData, 0, (int) fileSize);
System.out.println("Downloaded file");
} else
//Nothing special here too
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
Ok, now I can download file - still once, another one is downloaded but unable to read. For example, second time I want send by client a file.png. I got it on server, but this file is not possible to view.
Thanks in advance :)
You need to make your server able to differentiate files. The easiest way is to tell in advance how many bytes the receiving end should expect for a single file; this way, it knows when to stop reading and wait for another one.
This is what the SenderFile method could look like:
private void SenderFile(File file)
{
try
{
FileInputStream fis = new FileInputStream(file);
OutputStream os = socket.getOutputStream();
DataOutputStream wrapper = new DataOutputStream(os);
wrapper.writeLong(file.length());
IoUtil.copy(fis, wrapper);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
And this is what the ReceiveFile method could look like:
// the signature of the method is complete speculation, adapt it to your needs
private void ReceiveFile(File file)
{
FileOutputStream fos = new File(file);
InputStream is = socket.getInputStream();
DataInputStream wrapper = new DataInputStream(is);
// will not work for very big files, adapt to your needs too
long fileSize = wrapper.readLong();
byte[] fileData = new byte[fileSize];
is.read(fileData, 0, fileSize);
fos.write(fileData, 0, fileSize);
}
Then don't close the socket.
I'm trying to get a simple multithreaded proxy to work in Java. However I don't manage to get the webpage show up in my browser, after the first GET request and the response from the webpage, the program is just stuck (as you can see from my code, I'm printing everything i get on the stdout for debugging, and there I see the sourcecode of the webpage, however after printing out "After Client Write", nothing happens (no exception, just nothing...)).
import java.net.*;
import java.util.*;
import java.io.*;
public class Proxy
{
public static void main(String[] args)
{
try
{
ServerSocket listensocket = new ServerSocket(Integer.valueOf(args[0]));
while(true)
{
System.out.println("wait");
Socket acceptsocket = listensocket.accept(); // blocking call until it receives a connection
myThread thr = new myThread(acceptsocket);
thr.start();
}
}
catch(IOException e)
{
System.err.println(">>>>" + e.getMessage() );
e.printStackTrace();
}
}
static class myThread extends Thread
{
Socket acceptsocket;
int host, port;
String url;
myThread(Socket acceptsocket)
{
this.acceptsocket=acceptsocket;
}
public void run() {
try
{
System.out.println("hello");
Socket client = acceptsocket;
//client.setSoTimeout(100);
InputStream clientIn = client.getInputStream();
//BufferedInputStream clientIn=new BufferedInputStream(clientis);
OutputStream clientOut = client.getOutputStream();
System.out.println("hello");
String clientRequest = readStuffFromClient(clientIn); // parses the host and what else you need
System.out.print("Client request: -----\n"+clientRequest);
Socket server;
server = new Socket("xxxxxxxxxxxxx" , 80);
InputStream serverIn = server.getInputStream();
//BufferedInputStream serverIn=new BufferedInputStream(serveris);
OutputStream serverOut = server.getOutputStream();
serverOut.write(clientRequest.getBytes());
serverOut.flush();
String serverResponse = readStuffFromClient(serverIn);
System.out.print("Server Response: -----\n"+serverResponse);
clientOut.write(serverResponse.getBytes());
clientOut.flush();
System.out.println("After Client Write");
clientIn.close();
clientOut.close();
serverIn.close();
serverOut.close();
server.close();
client.close();
}
catch(Exception e)
{
System.out.println(e);
}
}
private String readStuffFromClient(InputStream clientdata)
{
ByteArrayOutputStream response = new ByteArrayOutputStream();
StringBuffer request=new StringBuffer(8192);
int i, httpstart,n=-1 ;
byte[] buffer = new byte[8192];
System.out.println("beforetry");
try
{
while((n = clientdata.read(buffer))!=-1)
{
System.out.println("before");
response.write(buffer,0,n);
//response.flush();
}
request=new StringBuffer(response.toString());
/*System.out.println("new:"+n+" "+ request.toString());
System.out.println("End client data");*/
}
catch (Exception e)
{
System.out.println("here");
System.out.println(e);
e.printStackTrace();
i = -1;
}
System.out.println("End manipulation method");
return request.toString();
}
}
}
(this is a stripped down not working example of my program, from the comments one can see I already tried to use BufferedInputStream). In general, this program is very unresponsive even for the first GET request from the browser. When I only read the clientdata once (not in a loop), I get a little bit further, e.g. get more GET/Response pairs, but at some point the program still gets stuck.
Somehow I think either I've a real trivial error I just don't manage to see, or the program should work, but simply doesn't for no real reason.
Any help is appreciated, thanks in advance!
You need two threads: one to read from the client and write to the server, and one to do the opposite, for each accepted socket. There is a further subtlety: when you read EOS from one direction, shutdown the opposite socket for output, and then if the input socket for that thread is already shutdown for output, close both sockets. In both cases exit the thread that read the EOS.
Try getting first the OutputStream and then the InputStream!
InputStream clientIn = client.getInputStream();
OutputStream clientOut = client.getOutputStream();
change it to:
OutputStream clientOut = client.getOutputStream();
InputStream clientIn = client.getInputStream();
This will make it work:
It will check if there is more data available to read
Still, it's important that you use BufferedIS because I think ByteArrayIS doesn't implement available method.
BufferedInputStream bis = new BufferedInputStream(clientdata);
System.out.println("beforetry");
try {
while(bis.available() > 0){
n = bis.read(buffer);
response.write(buffer, 0, n);
}