使用JAXB从套接字解组xml后,客户端被阻止

时间:2013-05-18 01:33:52

标签: java xml sockets jaxb marshalling

我正在尝试将一些xml从客户端发送到服务器,并让服务器在收到数据后向客户端发回消息。如果它只是发送xml然后它运行顺利,但当客户端期待响应时,它会永久阻止。我从另一篇文章中读到,解组关闭套接字,所以我使用了filterStream,但它似乎没有解决问题。有什么想法吗?

这是我的代码:

客户端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.net.Socket;
import javax.xml.bind.annotation.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Client {

    public static void main(String args[]) throws JAXBException, IOException {
        JAXBContext context = JAXBContext.newInstance(Product.class);
        Socket sock = new Socket("localhost", 4000);
        Marshaller m = context.createMarshaller();
        BufferedReader br = new BufferedReader(new InputStreamReader(new MyInputStream(sock.getInputStream())));
        PrintStream ps = new PrintStream(sock.getOutputStream());
        Product object = new Product();
        object.setCode("WI1");
        object.setName("Widget Number One");
        object.setPrice(BigDecimal.valueOf(300.00));
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.setProperty(Marshaller.JAXB_FRAGMENT, true);

        System.out.println("Data Sent");
        m.marshal(object, ps);

        System.out.println("Waiting for response from server");
        String msg = br.readLine(); // runs smoothly if client doesn't wait for response
        if (msg != null) {
            System.out.println(msg);
        }
        br.close();
        sock.close();
    }
}

@XmlRootElement
class Product {

    private String code;
    private String name;
    private BigDecimal price;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

服务器:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.math.BigDecimal;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;

public class Server {

    public static void main(String a[]) throws IOException, JAXBException {
        Socket sock;
        ServerSocket servsock = new ServerSocket(4000, 1);
        while (true) {
            sock = servsock.accept();
            new WorkerThread(sock).start();
        }
    }
}

class WorkerThread extends Thread {

    Socket sock;
    WorkerThread(Socket s) {
        sock = s;
    }

    public void run() {
        try {
            PrintStream out = new PrintStream(sock.getOutputStream()); // to send ack back to client;
            BufferedReader in = new BufferedReader(new InputStreamReader(new MyInputStream(sock.getInputStream())));;
            JAXBContext context = JAXBContext.newInstance(Product.class);  
            Unmarshaller um = context.createUnmarshaller();
            XMLInputFactory xif = XMLInputFactory.newFactory();
                StreamSource ss = new StreamSource(in);
                XMLStreamReader xsr = xif.createXMLStreamReader(ss);
                xsr.nextTag();

                JAXBElement<Product> xmlToJava = um.unmarshal(xsr, Product.class);

                Product product = xmlToJava.getValue();
                System.out.println("closing");
                xsr.close();

                System.out.println(product.getName());
                System.out.println(product.getCode());
            System.out.println(product.getPrice());
            out.println("data recieved");
            out.flush();
        } catch (IOException ex) {
            Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (JAXBException ex) {
            Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (XMLStreamException ex) {
            Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

class MyInputStream extends FilterInputStream {

    public MyInputStream(InputStream in) {
        super(in);
    }

    @Override
    public void close() {
    }
}

@XmlRootElement
class Product {

    private String code;
    private String name;
    private BigDecimal price;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

3 个答案:

答案 0 :(得分:1)

据我所知,服务器既没有编写响应,也没有在run()方法返回之前关闭套接字。所以套接字只会坐在那里......客户端将在读取时被阻止。

简单的解决方案是服务器的run()方法关闭sock ...最好在finally块中,以便它始终发生。


(以上是有效的,即使它不是您当前问题的原因!)

<强>更新

有几件事需要研究。

  1. 您无需使用PrintStream。您可以(并且应该)直接编组到套接字输出流。这可能会使服务器端混乱并导致其阻塞客户端。即使没有,PrintStream也没有用处。

  2. 服务器可能会抛出一些未经检查的异常,从而导致线程以静默方式退出。这可能会让客户端被阻止......等待服务器读取或关闭套接字。我之前的建议(至少)会阻止客户端阻止。但是,您还需要安装未捕获的异常处理程序来诊断任何未经检查的异常和错误。

答案 1 :(得分:0)

对于客户端,在编组后,您必须添加sock.shutdownOuput();似乎编组不知道何时结束,因此您必须手动关闭输出流。

答案 2 :(得分:0)

我有同样的问题。我决定用字符串缓冲它。

@Override
public void run() {
    try {
        while(true) {
            try(
                Socket socket = ss.accept();
                DataInputStream is = new DataInputStream(socket.getInputStream());
                DataOutputStream os = new DataOutputStream(socket.getOutputStream());
            ) {
                socket.setSoTimeout(1000);

                Unmarshaller unmarshaller = JAXBContext.newInstance(Sale.class).createUnmarshaller();
                unmarshaller.setEventHandler(new ValidationEventHandler() {
                    @Override
                    public boolean handleEvent(ValidationEvent event) {
                        if(event.getSeverity() == ValidationEvent.FATAL_ERROR) {
                            InvoiceSrv.logMessage(event.getMessage());
                        }

                        return true;
                    }
                });

                Sale sale = (Sale)unmarshaller.unmarshal(new StringReader(is.readUTF()));
                Response response = getResponse(sale);

                StringWriter writer = new StringWriter();
                Marshaller marshaller = JAXBContext.newInstance(Response.class).createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                marshaller.marshal(response, writer);

                os.writeUTF(writer.toString());
            }
        }
    }
    catch(SocketException e) { e.printStackTrace(); } // for close !!!
    catch(IOException | JAXBException e) {
        InvoiceSrv.logMessage(e);
    }
}

和客户

public static Response send(Sale sale) throws DeviceException {
    try(
        Socket socket = new Socket(addr, 9999);
        DataInputStream is = new DataInputStream(socket.getInputStream());
        DataOutputStream os = new DataOutputStream(socket.getOutputStream());
    ) {
        StringWriter writer = new StringWriter();
        Marshaller marshaller = JAXBContext.newInstance(Sale.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(sale, writer);

        os.writeUTF(writer.toString());

        Unmarshaller unmarshaller = JAXBContext.newInstance(Response.class).createUnmarshaller();
        Response response = (Response)unmarshaller.unmarshal(new StringReader(is.readUTF()));

        return response;
    }
    catch(IOException | JAXBException e) {
        e.printStackTrace();
    }

    throw new DeviceException();
}