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.
Related
I am doing a Server/Client network. The messages are send as Json Objects.
I have a method for writing the Json-Object but my method for reading the Json-Object doesnt work. I dont have any exceptions but there is no output like excpected. Something must be wrong with the bufferedReader. I dont know how to get that Json-Object from the socket who sent it.
Method for writing:
public void writeMessage(JSONObject json) {
try {
PrintWriter printWriter = new PrintWriter(
new OutputStreamWriter(
socket.getOutputStream()));
printWriter.print(json);
printWriter.flush();
} catch (IOException writeMessageException) {
System.out.println("!");
}
}
method for reading the message/ receiving the message:
private static void readMessageFromServer(Socket socket) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())))
{
StringBuilder sb = new StringBuilder();
String readLine;
while ((readLine = br.readLine()) != null) {
sb.append(readLine);
}
JSONObject js = new JSONObject(sb.toString());
String action1 = (String) js.get("action1");
System.out.println(action1);
} catch(IOException e) {
System.out.println("!");
}
}
Thank you :)
Consider using javax websocket.
javax.websocket client simple example
You can simply implement MessageHandler and specify the type parameter you expecting to receive using POJOs.
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<MyJsonObject>() {
#Override
public void onMessage(MyJsonObject message) {
//do something
}
});
}
That way you are creating a listener (#OnMessage) and handling each message as soon as it received. You can also implement MessageHandler with String type parameter and handle parsing the json yourself inside OnMessage method;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
MyJsonObject jObj = new ObjectMapper().readValue(message, MyJsonObject.class);
}
});
}
If you want to stick with java net socket consider switching to Jackson Object Mapper from JSONObject, that way you can convert input stream into any object:
private static void readMessageFromServer(Socket socket){
InputStream in = socket.getInputStream();
MyObject obj = new ObjectMapper().readValue(in, MyObject.class);
}
more javax webSocket examples
When reading data in bytes, we need to define our own protocol for communication between server and client. The simplest protocol which
we can define is called TLV (Type Length Value). It means that every
*message written to the socket is in the form *of the Type Length Value.
So we define every message sent as:
A 1 byte character that represents the data type, like s for String A
4 byte integer that indicates the length to the data And then the
actual data, whose length was just indicated
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
char dataType = in.readChar();
int length = in.readInt();
if(dataType == 's') {
byte[] messageByte = new byte[length];
boolean end = false;
StringBuilder dataString = new StringBuilder(length);
int totalBytesRead = 0;
while(!end) {
int currentBytesRead = in.read(messageByte);
totalBytesRead = currentBytesRead + totalBytesRead;
if(totalBytesRead <= length) {
dataString
.append(new String(messageByte, 0, currentBytesRead, StandardCharsets.UTF_8));
} else {
dataString
.append(new String(messageByte, 0, length - totalBytesRead + currentBytesRead,
StandardCharsets.UTF_8));
}
if(dataString.length()>=length) {
end = true;
}
}
}
More detail info can be found here
https://www.baeldung.com/java-inputstream-server-socket
I created a Java webserver that should send clients webpage files when the server's IP address is searched on their browsers. The problem is that I can only send an HTML file but I need to send CSS and also images that is embedded in the HTML file. Although I already linked the HTML file to an external CSS code and images, the CSS and images weren't displayed. I was at least able to get the CSS working because I inserted all the CSS codes directly in the HTML code. I am looking to find a way that enables the images to be displayed in the client's browser and if possible, also a way to send the external CSS code so I don't need to write CSS codes directly into HTML files in the future. It would be great if you could show the codes to fix the problem.
This is my main Server class:
package myserver.pkg1.pkg0;
import java.net.*;
public class Server implements Runnable {
protected boolean isStopped = false;
protected static ServerSocket server = null;
public static void main(String[] args) {
try {
server = new ServerSocket(9000);
System.out.println("Server is ON and listening for incoming requests...");
Thread t1 = new Thread(new Server());
t1.start();
} catch(Exception e) {
System.out.println("Could not open port 9000.\n" + e);
}
}
#Override
public void run() {
while(!isStopped) {
try {
Socket client = server.accept();
System.out.println(client.getRemoteSocketAddress() + " has connected.");
Thread t2 = new Thread(new Server());
t2.start();
new Thread(new Worker(client)).start();
} catch(Exception e) {
System.out.println(e);
}
}
}
}
As you can see after the server accepts request, the client socket variable is forwarded to a new class Worker. The Worker class handles each output to each clients.
This is Worker class:
package myserver.pkg1.pkg0;
import java.io.*;
import java.net.*;
public class Worker implements Runnable {
protected Socket client;
public Worker(Socket client) {
this.client = client;
}
#Override
public void run() {
try {
PrintWriter out = new PrintWriter(client.getOutputStream());
out.println("HTTP/1.1 200 OK");
out.println("Content-type: text/html");
out.println("\r\n");
out.flush();
out.close();
client.close();
} catch(Exception e) {
System.out.println(e);
}
}
}
Since you are re-inventing the wheel, i'm assuming this question is for academic purposes. If not, you can always host your css, images and other static content on a CDN and refer to them in your HTML.
Now, what you are doing here is responding to a TCP request and not a HTTP request. Your first step should be to write a Request Handler, that captures the inputstream from the TCP socket. Then parse the stream into a valid HTTP Request. Then capture the URL path from the http request and serve content based on the path. This way you can refer to relative paths on your server for images/css. The handler for those paths should be able to map to a filesystem and pick the relevant file and compose it into a http response.
I think you can do something like this in your Worker class,
#Override
public void run() {
try {
// Get html content in string or build your static string here.
StringBuilder contentBuilder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader("mypage.html"));
String str;
while ((str = in.readLine()) != null) {
contentBuilder.append(str);
}
in.close();
} catch (IOException e) {
}
String content = contentBuilder.toString();
// Here logic to search and get that file here.
File file = new File("demo.html");
//After getting file, to write data in that file,
PrintWriter writer = new PrintWriter(file);
writer.write(content);
writer.close();
} catch(Exception e) {
System.out.println(e);
}
}
Hope this helps.
out.println("<img src=\"https://static.oschina.net/uploads/space/2017/0730/143737_rMYR_2903254.jpg\">");
just this code can display ur image
As of Java 6, there is an http server in the JDK that you can use.
Otherwise, you need to read the request, at least the first line, and supply a different output depending on the URL in the request.
UPDATE:
You can read the HTTP line like this:
private String readHttpLine() throws IOException {
InputStream stream = client.getInputStream();
Reader reader = new InputStreamReader(stream);
BufferedReader in = new BufferedReader(reader);
String line = in.readLine();
if (line == null) {
throw new IOException("No HTTP line");
}
return line;
}
You can use a regexp to parse the HTTP line:
private static Pattern HTTP_LINE = Pattern.compile(
"([A-Z]+) +(.*) +HTTP/([0-9]+\\.[0-9]+)",
Pattern.CASE_INSENSITIVE);
You would then parse it like this:
String line = readHttpLine();
Matcher matcher = HTTP_LINE.matcher(line);
if (!matcher.matches()) {
throw new IOException("Invalid HTTP line");
}
String method = matcher.group(1).toUpperCase();
String path = matcher.group(2);
String httpVersion = matcher.group(3);
Now you can test the method and the path to provide the HTTP response:
if (method.equals("GET")) {
switch (path) {
case "/":
case "/index.html":
PrintWriter out = new PrintWriter(client.getOutputStream());
out.print("HTTP/1.1 200 OK\r\n");
out.print("Content-type: text/html\r\n");
out.print("\r\n");
//In this line I used out.println("full html code"); but I'd like a simpler way where it can search for the html file in the directory and send it.
out.flush();
out.close();
break;
case "/style.css":
// provide CSS stylesheet
break;
case "/image1.png":
// provide image 1
break;
case "/image2.jpeg":
// provide image 1
break;
}
}
Of course, unless the content is dynamic, you can keep in in files and return the appropriate file content.
UPDATE 2:
Example to supply files in resources:
...
if (method.equals("GET")) {
switch (path) {
case "/":
case "/index.html":
Writer out = new OutputStreamWriter(
client.getOutputStream(), "UTF-8");
out.write("HTTP/1.1 200 OK\r\n");
out.write("Content-type: text/html\r\n");
out.write("\r\n");
//In this line I used out.println("full html code"); but I'd like a simpler way where it can search for the html file in the directory and send it.
out.close();
break;
default:
supplyFile(path);
break;
}
}
The supplyFile method looks like that:
private void supplyFile(String path) throws IOException {
String contentType = mimetypes.getContentType(path);
OutputStream stream = client.getOutputStream();
Writer out = new OutputStreamWriter(stream, "UTF-8");
out.write("HTTP/1.1 200 OK\r\n");
out.write("Content-type: " + contentType + "\r\n");
out.write("\r\n");
out.flush();
try (InputStream istream = getClass().getResourceAsStream(path)) {
byte[] buf = new byte[4096];
for (int n = istream.read(buf); n > 0; n = istream.read(buf)) {
stream.write(buf, 0, n);
}
}
out.close();
}
Here I am creating a thread to check for a server response every 2 seconds, the issue is that the client.monitorResponse() is a readLine() method and will not continue until a response is received.
client = new ClientObject("localhost");
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
try {
String response = null;
if(!(response = client.monitorResponse()).isEmpty()) {
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 2000, 2000);
I am sending the response via the Server like so (where client is a established Socket):
public SocketObject(Socket client, int numberOfClients) throws Exception {
socket = client; // the .accept() socket is passed through
// this is because I assign them ID's for later use (I hold an ArrayList of sockets)
this.clientId = numberOfClients;
// both these are static to the class
outputStream = new PrintWriter(client.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(client.getInputStream()));
}
public void sendResponse(String response) {
outputStream.println(response);
}
I am then picking the response up via the client Socket that has connected to the server:
public ClientObject(String hostname) throws IOException {
// socket is static to this class
socket = new Socket(hostname, 4444);
System.out.println("Connected to " + hostname + " on port 4444...");
// both static to this class
outputStream = new PrintWriter(socket.getOutputStream(), true);
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Successfully started a stream on " + hostname);
this.hostname = hostname;
}
public String monitorResponse() throws Exception {
System.out.println("Listening for a response...");
return inputStream.readLine();
}
The debug console only displays the Listening for a response... output once which is telling me that it doesn't get past the inputStream.readLine() method in-side the Thread. Is there anyway I can add a timeout on the BufferedReader? I have tried multiple solutions like adding a .setSoTimeout() to the socket before creating the BufferedReader but all that did was close the connection/socket after the specified time.
Any help would be appreciated.
You should use a non-blocking (NIO) request and read chunks looking for newlines in-between. Typically in Java you just have to look for the NIO version of the Stream class you are using and use it to check every N seconds for new content. In your case with minimal modifications you can use the less fancy and efficient method of blocking calls with BufferedReader.ready() to prevent blocking:
String partialLine="";
public static String monitorResponse() throws Exception {
System.out.println("Listening for a response...");
int nextByte;
String nextChar;
while (inputStream.ready()) {
nextByte = inputStream.read();
nextChar = Character.toString ((char) nextByte);
partialLine += nextChar;
if ("\n".equals(nextChar)) {
String line = partialLine;
partialLine = "";
return line.replace("\r\n", "");
}
}
return "";
}
Check out http://tutorials.jenkov.com/java-nio/nio-vs-io.html for more info.
Is there anyway I can add a timeout on the BufferedReader?
No, but you can set a timeout on the Socket, with Socket.setSoTimeout().
I have tried multiple solutions like adding a .setSoTimeout() to the socket before creating the BufferedReader but all that did was close the connection/socket after the specified time.
No it doesn't close the socket. It throws SocketTimeoutException, which you should catch and handle as pertinent. If the socket is being closed, you're closing it. Solution: don't.
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);
}
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));