我在Windows XP上用Eclipse编程java。我有一个多进程模拟,它使用ProcessBuilder来运行服务器和两个客户端。服务器启动一个线程来监听两个不同的套接字 - 每个客户端一个。我可以为每个客户注释掉代码,让其他工作完全正常。当我尝试同时运行它们时,一个客户端将始终使用ConnectException错误:连接被拒绝:连接。虽然很难说,但哪个客户似乎是以较慢的速度运行。我可以在启动服务器之后但在客户端之前暂停,netstat会验证两个套接字是否都处于活动状态。可能是什么导致了这个?我在下面有一些简化的代码。
更新:基于评论,我编辑了代码以在单个套接字上多线程服务器,但我仍然遇到同样的问题。下面的代码反映了这些变化。看来套接字是由一个客户端打开和关闭,而另一个客户端有机会打开它。我可以在每个客户端的末尾抛出暂停语句,允许另一个完成,但这是一个修复,而不是解决方案。所以现在真正的问题是,在指示它关闭之前,如何保持ServerSocket的监听?
服务器
try{
server = new ServerSocket(sockNum);
} catch (IOException e) {
System.out.printf("Could not listen on port %d\n",sockNum);
System.exit(-1);
}
while(true){
ClientWorker w;
try{
Socket connection = server.accept();
w = new ClientWorker(connection);
Thread t = new Thread(w);
t.start();
} catch (IOException e) {
System.out.printf("Accept failed: %d\n",sockNum);
System.exit(-1);
}
}
class ClientWorker implements Runnable {
private Socket client;
ClientWorker(Socket client) {
this.client = client;
}
public void run(){
Object line;
ObjectInputStream in = null;
PrintWriter out = null;
try{
in = new ObjectInputStream(client.getInputStream());
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("in or out failed");
System.exit(-1);
}
while(true){
try{
line = in.readObject();
//Send data back to client
out.println("Sent from broker");
if(line instanceof String)
System.out.println(line);
else
System.out.println(line.getClass());
} catch (IOException e) {
System.out.println("Read failed");
System.exit(-1);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
客户端
try{
socket = new Socket("localhost", socketNum);
out = new ObjectOutputStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.writeObject(message);
String line = in.readLine();
System.out.println(line);
} catch (UnknownHostException e) {
System.out.println("Unknown host: localhost.eng");
System.exit(1);
} catch (IOException e) {
System.out.println("No I/O");
System.exit(1);
}
控制器
ProcessBuilder server = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ServerProcess");
server.redirectErrorStream(true);
Process runServer = server.start();
ProcessBuilder clientA = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientAProcess");
clientA.redirectErrorStream(true);
Process runClientA = clientA.start();
ProcessBuilder clientB = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientBProcess");
clientB.redirectErrorStream(true);
Process runClientB = clientB.start();
答案 0 :(得分:1)
'服务器启动一个线程来监听两个不同的套接字 - 每个客户端一个。'
不,请不要这样做!为每个客户端分配一个单独的侦听套接字是一种异常设计。只需启动两个客户端连接的一个监听线程。当accept()返回时,启动该客户端的读线程,传递accept()调用返回的ServerClient Socket。
答案 1 :(得分:1)
编写服务器的常用方法是在循环中侦听单个套接字。
对于每个传入连接,产生一个线程来处理客户端,然后立即在同一服务器套接字上再次执行listen
。
在您的情况下,如果两个客户端尝试连接到同一服务器端口,则第二个客户端将始终获得“连接被拒绝”。第一个客户端获取连接,服务器不会在套接字上重新发出listen
。您尚未显示客户端如何获取其端口号,但我猜他们都在尝试连接到同一端口。
答案 2 :(得分:0)
在我写这篇文章时,你的代码对我来说很好看。您有一个 ServerSocket ,用于侦听新连接。每个连接发生时,都会设置一个新的线程来处理它并且给它自己的 Socket 。您的套接字以及它们的主题应完全独立。每个人的命运应该完全独立于另一个。他们不应该知道彼此的存在。
弄清楚他们如何互动并将它们分开。
要尝试的是设置100个客户端而不是2个。这可能会使问题更加明显;一吨TNT将留下比鞭炮更大的陨石坑。
另一个可能的问题:确保定期重置ObjectOutputStream。这种I / O非常方便,但它可以做非常奇怪的事情。如果没有重置,如果你发送两次相同的对象,第二次它只会发送对第一个版本的引用,如果最新版本发生了变化则没用。此外,即使与重置,如果您发送一个引用另一个的对象,该对象也将被发送到该行。 (这可能非常好。您可以发送一个引用涉及数百万个对象的庞大结构的单个对象,整个事件将被发送和保留其组织。它非常巧妙且非常有用,如果你知道它在做什么。)
答案 3 :(得分:0)
来自OP
因此,在解决我认为无关的问题的过程中,我似乎已经解决了最初的问题。我添加了两个主要的东西。首先,我设置客户端在完成时发送“end”命令,以便在尝试readObject()时套接字不会阻塞。其次,(我认为这是修复它的原因),我为ServerSocket定义了一个超时值,而不是将其保留为undefined(也就是无限)。在我开始工作之后我回去清理我的代码时,我尝试删除客户端进程中的暂停,但事情仍然有效!我的猜测是,没有定义的超时,当一个套接字在另一个套接字打开之前关闭时,ServerSocket也会关闭。定义超时后,ServerSocket将等待,直到关闭之前超时。 (这只是猜测。)
这是当前的工作代码。如果您认为修复的原因不同,请随意发表评论。
服务器
try{
server = new ServerSocket(sockNum);
server.setSoTimeout(timeOut);
} catch (IOException e) {
System.out.printf("Could not listen on port %d\n",sockNum);
System.exit(-1);
}
while(isActive){
ClientWorker w;
try{
Socket connection = server.accept();
w = new ClientWorker(connection);
Thread t = new Thread(w);
t.start();
} catch (IOException e) {
if(e instanceof SocketTimeoutException)
isActive = false;
else{
System.out.printf("Accept failed: %d\n",sockNum);
System.exit(-1);
}
}
}
class ClientWorker implements Runnable {
private Socket client;
private Source source = Source.NONE;
private String sourceName = "?";
private Boolean threadActive = true;
ClientWorker(Socket client) {
this.client = client;
}
public void run(){
Object line;
ObjectInputStream in = null;
PrintWriter out = null;
try{
in = new ObjectInputStream(client.getInputStream());
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("in or out failed");
System.exit(-1);
}
while(threadActive){
try{
line = in.readObject();
if(line instanceof String){
if(line.equals("end")){
threadActive = false;
}else
sourceName = line;
}
} else
handleData;
} catch (IOException e) {
System.out.println("Read failed");
threadActive = false;
} catch (ClassNotFoundException e) {
e.printStackTrace();
threadActive = false;
}
}
try {
this.client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}