应用程序卡在 serverSocket.accept() 并提供已在线程的第二次调用中使用的绑定异常地址
app get stuck at serverSocket.accept() and gives bind exception address already in use on the second call for the thread
我为套接字通信构建的应用程序有两个问题,首先我将尝试解释该应用程序的功能,然后我将详细介绍这两个问题。
首先,我单击一个按钮,启动一个线程,该线程通过 UDP 套接字发送多播消息 "group address"。一旦任何设备收到消息,它们将通过 TCP 套接字发送响应,而我的设备将充当发送响应的设备的服务器。所以调试后我发现第一个问题是 clientSocket = serverSocket.accept();
有时会卡住,应用程序将阻止所有内容并继续执行它,这可能会发生,因为 udp 消息可能永远不会到达目的地,这意味着没有客户端我创建的 tcp 服务器。
第一个问题:有没有办法让serverSocket.accept();
非阻塞或者设置超时?我试过 serverSocket.setTimeSoOut()
方法,但没有用。也许这个问题不是来自 UDP 消息?
第二个问题是,如果我按下调用线程的按钮两次,它将抛出一个已在使用的 BindException 地址:由于 serverSocket.bind(new InetSocketAddress(4125));
的重新执行,会发生这种情况。有什么办法可以fix/avoid吗?
以下是我正在使用的线程:
这个在我按下按钮后被调用:
private class ChatClientThread extends Thread {
DatagramSocket socket;
String sentence;
String modifiedSentence;
BufferedReader inFromUser;
DataOutputStream outToServer;
BufferedReader inFromServer;
Socket clientSocket;
ServerSocket serverSocket;
@Override
public void run() {
/*Socket socket = null;
DataOutputStream dataOutputStream = null;
DataInputStream dataInputStream=null;*/
clientSocket=null;
try {
String data="NewTask_"+EmpPhoneNumber;
serverSocket=new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(4125));
socket = new DatagramSocket(52276);
socket.setBroadcast(true);
InetAddress group = InetAddress.getByName(
"224.0.1.2");
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
group, 52276);
socket.send(packet);
while(true){
clientSocket = serverSocket.accept();
ConnectThread ct=new ConnectThread(clientSocket);
ct.start();
}
} catch (UnknownHostException e) {
e.printStackTrace();
final String eString = e.toString();
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
}
});
} catch (IOException e) {
e.printStackTrace();
final String eString = e.toString();
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
}
});
} finally {
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
}
}
如您所见,这是从上面的线程调用的:
private class ConnectThread extends Thread {
Socket socket;
String sentence;
String modifiedSentence;
BufferedReader inFromUser;
DataOutputStream outToServer;
BufferedReader inFromServer;
ConnectThread(Socket socket){
this.socket= socket;
}
@Override
public void run() {
DataInputStream dataInputStream = null;
DataOutputStream dataOutputStream = null;
Socket socket2 = null;
DataOutputStream dataOutputStream2= null;
DataInputStream dataInputStream2=null;
try {
while(true){
inFromUser = new BufferedReader( new InputStreamReader(System.in));
outToServer = new DataOutputStream(socket.getOutputStream());
inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sentence = inFromUser.readLine();
modifiedSentence = inFromServer.readLine();
socket2 = new Socket(socket.getInetAddress().getHostAddress(), 4125);
dataOutputStream2 = new DataOutputStream(
socket2.getOutputStream());
String[] parts = modifiedSentence.split("_");
String partGive = parts[0].substring(4); // 004
String partEmpId = parts[1];
if(partGive.equals("GiveMeATask")&&Integer.parseInt(partEmpId)==empId){
dataOutputStream2.writeUTF(" "+"SolveProblemOrder_2");
dataOutputStream2.flush();
}
System.out.println("FROM SERVER: " + modifiedSentence);
if(modifiedSentence!=null) break;}
outToServer.close();
inFromServer.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
这是两个非常常见的问题。这两个我倒序回答。
您所说的按钮是创建一个ServerSocket并将其绑定到特定端口。在你的情况下,端口是 4125。从你的代码来看,你似乎没有在任何地方关闭那个 serversocket。当您第二次单击该按钮时,ServerSocket 的第二个实例会尝试绑定到同一个端口 - 但该端口仍由第一个 ServerSocket 使用。在这种情况下,您会得到一个绑定异常。一个端口不能被多个 ServerSocket 使用。解决方案是在使用 serverSocket.close();
创建新的 ServerSocket 之前关闭现有的 ServerSocket
如果您阅读the documentation, it clearly states what ServerSocket.accept() does: "[...] The method blocks until a connection is made." This is the "getting stuck" that you described. The thread that executes that code is put into a waiting position and continues only when a connection is made, then returns that new connection. The classic approach is to start a new thread that waits for incoming connections so that your main thread continues to execute and your whole application does not "freeze". Another approach would be a non-blocking framework that encapsulates all that overhead away from you, one of those is Apache MINA。
我强烈建议查看处理基本 client/server 行为的小型示例项目,因为您很可能会在这里处理线程。
第一个问题:您的应用很可能没有收到UDP包。如果 serverSocket.accept() 没有得到任何客户端,它将无限期地等待有人连接。您可以通过使用另一个只接受连接的线程来避免这种情况,以避免冻结您的应用程序。另一种方法是使用 Java 的 NIO classes 为几乎任何东西提供非阻塞 IO。这将要求您使用 ServerSocketChannel and related classes. (Quick googling also gave me this guide,这似乎很容易理解)。
第二个问题:您需要在使用完 ServerSocket 后关闭它。否则该端口将永远不会再次空闲以供另一个 ServerSocket 使用。
或者,您可以让 Socket 打开并记住您已经打开它(例如,在 class 中使用布尔字段)。
我为套接字通信构建的应用程序有两个问题,首先我将尝试解释该应用程序的功能,然后我将详细介绍这两个问题。
首先,我单击一个按钮,启动一个线程,该线程通过 UDP 套接字发送多播消息 "group address"。一旦任何设备收到消息,它们将通过 TCP 套接字发送响应,而我的设备将充当发送响应的设备的服务器。所以调试后我发现第一个问题是 clientSocket = serverSocket.accept();
有时会卡住,应用程序将阻止所有内容并继续执行它,这可能会发生,因为 udp 消息可能永远不会到达目的地,这意味着没有客户端我创建的 tcp 服务器。
第一个问题:有没有办法让serverSocket.accept();
非阻塞或者设置超时?我试过 serverSocket.setTimeSoOut()
方法,但没有用。也许这个问题不是来自 UDP 消息?
第二个问题是,如果我按下调用线程的按钮两次,它将抛出一个已在使用的 BindException 地址:由于 serverSocket.bind(new InetSocketAddress(4125));
的重新执行,会发生这种情况。有什么办法可以fix/avoid吗?
以下是我正在使用的线程: 这个在我按下按钮后被调用:
private class ChatClientThread extends Thread {
DatagramSocket socket;
String sentence;
String modifiedSentence;
BufferedReader inFromUser;
DataOutputStream outToServer;
BufferedReader inFromServer;
Socket clientSocket;
ServerSocket serverSocket;
@Override
public void run() {
/*Socket socket = null;
DataOutputStream dataOutputStream = null;
DataInputStream dataInputStream=null;*/
clientSocket=null;
try {
String data="NewTask_"+EmpPhoneNumber;
serverSocket=new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(4125));
socket = new DatagramSocket(52276);
socket.setBroadcast(true);
InetAddress group = InetAddress.getByName(
"224.0.1.2");
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
group, 52276);
socket.send(packet);
while(true){
clientSocket = serverSocket.accept();
ConnectThread ct=new ConnectThread(clientSocket);
ct.start();
}
} catch (UnknownHostException e) {
e.printStackTrace();
final String eString = e.toString();
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
}
});
} catch (IOException e) {
e.printStackTrace();
final String eString = e.toString();
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(TicketDetails.this, eString, Toast.LENGTH_LONG).show();
}
});
} finally {
TicketDetails.this.runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
}
}
如您所见,这是从上面的线程调用的:
private class ConnectThread extends Thread {
Socket socket;
String sentence;
String modifiedSentence;
BufferedReader inFromUser;
DataOutputStream outToServer;
BufferedReader inFromServer;
ConnectThread(Socket socket){
this.socket= socket;
}
@Override
public void run() {
DataInputStream dataInputStream = null;
DataOutputStream dataOutputStream = null;
Socket socket2 = null;
DataOutputStream dataOutputStream2= null;
DataInputStream dataInputStream2=null;
try {
while(true){
inFromUser = new BufferedReader( new InputStreamReader(System.in));
outToServer = new DataOutputStream(socket.getOutputStream());
inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sentence = inFromUser.readLine();
modifiedSentence = inFromServer.readLine();
socket2 = new Socket(socket.getInetAddress().getHostAddress(), 4125);
dataOutputStream2 = new DataOutputStream(
socket2.getOutputStream());
String[] parts = modifiedSentence.split("_");
String partGive = parts[0].substring(4); // 004
String partEmpId = parts[1];
if(partGive.equals("GiveMeATask")&&Integer.parseInt(partEmpId)==empId){
dataOutputStream2.writeUTF(" "+"SolveProblemOrder_2");
dataOutputStream2.flush();
}
System.out.println("FROM SERVER: " + modifiedSentence);
if(modifiedSentence!=null) break;}
outToServer.close();
inFromServer.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataInputStream != null) {
try {
dataInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (dataOutputStream != null) {
try {
dataOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
这是两个非常常见的问题。这两个我倒序回答。
您所说的按钮是创建一个ServerSocket并将其绑定到特定端口。在你的情况下,端口是 4125。从你的代码来看,你似乎没有在任何地方关闭那个 serversocket。当您第二次单击该按钮时,ServerSocket 的第二个实例会尝试绑定到同一个端口 - 但该端口仍由第一个 ServerSocket 使用。在这种情况下,您会得到一个绑定异常。一个端口不能被多个 ServerSocket 使用。解决方案是在使用
创建新的 ServerSocket 之前关闭现有的 ServerSocketserverSocket.close();
如果您阅读the documentation, it clearly states what ServerSocket.accept() does: "[...] The method blocks until a connection is made." This is the "getting stuck" that you described. The thread that executes that code is put into a waiting position and continues only when a connection is made, then returns that new connection. The classic approach is to start a new thread that waits for incoming connections so that your main thread continues to execute and your whole application does not "freeze". Another approach would be a non-blocking framework that encapsulates all that overhead away from you, one of those is Apache MINA。
我强烈建议查看处理基本 client/server 行为的小型示例项目,因为您很可能会在这里处理线程。
第一个问题:您的应用很可能没有收到UDP包。如果 serverSocket.accept() 没有得到任何客户端,它将无限期地等待有人连接。您可以通过使用另一个只接受连接的线程来避免这种情况,以避免冻结您的应用程序。另一种方法是使用 Java 的 NIO classes 为几乎任何东西提供非阻塞 IO。这将要求您使用 ServerSocketChannel and related classes. (Quick googling also gave me this guide,这似乎很容易理解)。
第二个问题:您需要在使用完 ServerSocket 后关闭它。否则该端口将永远不会再次空闲以供另一个 ServerSocket 使用。 或者,您可以让 Socket 打开并记住您已经打开它(例如,在 class 中使用布尔字段)。