[英]Client is blocked after unmarshalling xml from socket using JAXB
我正在嘗試將一些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;
}
}
據我所知,服務器既沒有編寫響應,也沒有在run()
方法返回之前關閉套接字。 因此,套接字將只坐在那里...,並且客戶端將在讀取中保持阻塞狀態。
一種簡單的解決方案是讓服務器的run()
方法關閉sock
...最好在finally
塊中使其始終發生。
(即使不是直接引起問題的原因,以上內容也有效!)
更新
有幾件事需要研究。
您不需要使用PrintStream
。 您可以(並且應該)直接封送至套接字輸出流。 這可能會混淆服務器端並導致其阻塞客戶端。 即使沒有,PrintStream也沒有任何用處。
服務器可能拋出一些未經檢查的異常,導致線程靜默退出。 這可能會使客戶端處於阻塞狀態……等待服務器讀取或關閉套接字。 我以前的建議將(至少)防止客戶端阻塞。 但是,您還需要安裝未捕獲的異常處理程序,以診斷任何未經檢查的異常和錯誤。
對於客戶端,在封送處理之后,您必須添加sock.shutdownOuput();
封送處理似乎不知道何時結束,因此您必須手動關閉輸出流。
我也有同樣的問題。 我決定將其緩沖在字符串中。
@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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.