我正在尝试创建一个具有多个客户端连接的服务器。客户端首先发送用户输入的命令。然后服务器读取所有参数并将其作为对象发送给服务器。
但我的问题是,在执行一个命令后,我的程序会陷入死锁。我对它做了一个变通方法,这样我的客户机就可以发送命令,在服务器说可以发送查询之后,就可以发送对象了。如何避免这种变通方法?
这是我的服务器。
try {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
objectOut = new ObjectOutputStream(clientSocket.getOutputStream());
objectIn = new ObjectInputStream(clientSocket.getInputStream());
// communicate with client
if ("Hello Server".equals(in.readLine())) {
out.println("Connection with Server established");
}
else {
out.println("You picked the wrong house, fool! (wrong greeting)");
}
String inputLine;
while ((inputLine = in.readLine()) != null) {
if ("exit".equals(inputLine)) {
break;
}
else if ("ls".equals(inputLine)) {
execute_ls();
}
else if ("data".equals(inputLine)) {
out.println("o");
var query = (Query) objectIn.readObject();
exceute_data(query);
}
}
in.close();
out.close();
clientSocket.close();
} catch (Exception e) {
Logger.err(e.toString());
}
这是我在客户端的功能。
public CompletableFuture<Data> queryData(param) {
Data data_from_server;
CompletableFuture<Data> data_series = new CompletableFuture<>();
try {
outputData.println("data");
inputData.readLine();
objectOutputData.writeObject(param);
data_from_server = (Data) objectInputData.readObject();
data_series.complete(data_from_server);
if (data_from_server.getErrorMessage() != null) {
throw new Exception(data_from_server.getErrorMessage());
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return data_series;
}
提前谢谢各位!
1条答案
按热度按时间wljmcqd81#
你认为“缓冲”了什么
BufferedReader
代表什么?作为一个规则,您不能在同一个“源”inputstream或reader对象周围创建多个 Package 器,并期望事物正常工作-filterstreams(这些流本身不是源,但是您在另一个流周围 Package 它们;bufferedreader就是这样一个例子,因为inputstreamreader)被允许从底层输入源读取比提供任何请求数据所严格要求的更多的数据;某些filterstream,例如bufferedreader,实际上是显式指定的(这是br唯一做的事情,例如,因此命名)。
解决办法是永远不要混流。要“unmix”此流,请定义一个协议。例如:
首先,客户机发送1个字节,表示它将要发送的命令的长度。然后,它发送那么多字节(注意:字节,而不是字符,您不应该在这里生成任何类型的reader对象),然后使用usascii编码将其转换为字符串,然后针对每个命令跳转到单独的代码片段。例如,“exit”命令不需要发送更多的数据,但“data”命令随后将发送一个包含以下数据大小的大端32位值。然后使用java内置的序列化机制(即objectinputstream)读取和解释这些数据。
等等。
这导致了更多的实现:java内置的序列化机制几乎完全不适合这个工作;它是一个你无法描述的协议(除了:“获取这个类文件,找到一个jvm,确保类文件在它的类路径上,创建一个objectinputstream,并在它上面调用readobject”),如果你只使用java,它的效率非常低,并且充满了问题(面对序列化对象,更改代码是非常棘手的),如果你在链中添加了任何不是用java编写的工具,那么就无法阅读。我建议你用别的东西。
一些替代方案:
使用gson或jackson等将对象序列化为json。
对整个协议使用protobuf(不仅仅是数据本身,还有您正在发送的那些命令)。
制作rest接口;而不是一个长寿命的tcp/ip连接,在那里你发送一个混合的命令和二进制数据,取而代之的是客户端调用,例如。
https://yourserver.com/api/listAll
如果在命令调用之间要记住有状态的数据,则在头中使用某种身份验证令牌和/或会话令牌。然后在web上搜索如何用java制作restapi,您将发现大量的选项,例如jersey或spark等jax-rs实现。抛弃对同一基础数据使用多个filterstream的想法,改为使用原始数据
InputStream
,仔细阅读你所需要的内容,并在几乎所有内容的前面加上一个“size”,以便知道要阅读多少内容,而不会意外地阅读过多内容。这是相当复杂的,这就是为什么我建议你不要按照这个计划,而是选择上面的其他3个选项之一。