我想实现一个服务器,它在一个特定的端口上无休止地监听从许多客户端接收数据(从来没有并行,只有串行)。我尝试的第一件事是运行服务器,然后以串行方式启动几个客户端(一个接一个)。
这听起来很容易实现,但我实际上遇到了问题,代码只有在调试模式下运行时才有效,服务器代码中至少有一个断点(但与正常运行时没有断点时相同的错误),对我来说非常奇怪。
服务器端代码:
public class TaskExecutionServer {
public TaskExecutionServer(final int port) {
new Thread() {
@Override
public void run() {
try {
int counter = 0;
ServerSocket serverSocket = new ServerSocket(port);
while(true) {
System.out.println("Waiting for client...");
Socket socket = serverSocket.accept();
System.out.println("Accepted");
InputStream inputStream = socket.getInputStream();
ObjectInputStream objectStream = new ObjectInputStream(inputStream);
while(inputStream.available() > 0 ) {
String to = (String)objectStream.readObject();
System.out.println(to);
System.out.println(++counter);
}
objectStream.close();
inputStream.close();
System.out.println("Closing socket");
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public static void main(String args[]) {
new TaskExecutionServer(2003);
}
}
这里是客户端代码:
public class TaskSenderClient {
public static void main(String args[]){
try{
Socket s = new Socket("localhost",2003);
OutputStream os = s.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject("test");
oos.close();
os.close();
s.close();
}catch(Exception e){
System.out.println("Client exception");
e.printStackTrace();
}
}
}
这是在调试模式下运行时的控制台输出,断点位于服务器代码行System.out.println("Accepted");
:
Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
test
2
Closing socket
Waiting for client...
Accepted
test
3
Closing socket
Waiting for client...
在正常模式下运行/在调试模式下没有断点时的输出:
Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...
我没有得到任何异常…有人能帮忙吗?这是我第一次尝试在java中重用套接字连接。
编辑:检查inputStream。available返回不同的值
我只是在服务器代码的while
之前添加了一个System.out.println(inputStream.available());
。打印
- 总是
7
在调试模式下带有断点 -
7
(在第一次运行时)和0
(在所有其他尝试中)之后在非调试模式/没有断点
EDIT 2:首先等待inputStream。= 0这个方法对我也很有效。但是,我在这里删除了这个代码片段,因为检查available()
似乎不是正确的方法!->查看解决方案!
编辑3:新的服务器代码,使用NonEmptyInputStream
检查每个PushbackInputStream
的非空流:
由于这使用EOFException
,对我来说似乎不是一个最佳的解决方案,所以我也删除了这个代码片段(而是参见下面的解决方案)。
如果还没有数据,InputStream.available()
可以返回0
,这意味着客户端还没有发送一些数据,或者至少还没有到达。如果你添加一个断点,客户端有更多的时间来发送数据。
你可以添加这样的逻辑:你的客户端首先发送它写了多少对象,服务器读取这个数量,然后在它停止读取之前读取这个数量的对象。
另一种可能性是在ObjectInputStream
和InputStream
之间插入一个PushbackInputStream
,然后在PushbackInputStream
上做一个read()
,检查-1
的结果,这意味着流结束,如果不是-1
,在使用ObjectInputStream
方法之前,使用unread()
将读字节推回流。
这里有一个你最初发布的类的例子,用最后一个模式重写:
public class TaskExecutionServer {
public TaskExecutionServer(final int port) {
new Thread() {
@Override
public void run() {
try {
int counter = 0;
ServerSocket serverSocket = new ServerSocket(port);
while(true) {
System.out.println("Waiting for client...");
Socket socket = serverSocket.accept();
System.out.println("Accepted");
InputStream inputStream = socket.getInputStream();
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream);
for(int i; (i = pushbackInputStream.read()) != -1;) {
pushbackInputStream.unread(i);
String to = (String) objectStream.readObject();
System.out.println(to);
System.out.println(++counter);
}
objectStream.close();
pushbackInputStream.close();
inputStream.close();
System.out.println("Closing socket");
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public static void main(String args[]) {
new TaskExecutionServer(2003);
}
}
或者在这里再次使用try-with-resources,这比手动关闭AutoClosable
s更可取。
public class TaskExecutionServer {
public TaskExecutionServer(final int port) {
new Thread() {
@Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(port)) {
int counter = 0;
while(true) {
System.out.println("Waiting for client...");
try (Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream)) {
System.out.println("Accepted");
for(int i; (i = pushbackInputStream.read()) != -1;) {
pushbackInputStream.unread(i);
String to = (String) objectStream.readObject();
System.out.println(to);
System.out.println(++counter);
}
System.out.println("Closing socket");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public static void main(String args[]) {
new TaskExecutionServer(2003);
}
}
available()
不是一个有效的流结束测试。参见Javadoc。您应该从对象流中读取,直到捕获EOFException
。