我目前正在学校参与一个项目,我们正在建立一个用于Android手机的通信系统。为此,我们将使用一个服务器向所有客户端打开套接字,使它们进行通信。
我之前已经完成了几个聊天应用程序,没有任何套接字或线程处理问题,但这一次,由于某些原因,它让我感到困惑。
问题是,一旦我发起ServerSocket
对象serverSocket = new ServerSocket(5000)
,而不是serverSocket.accept()
,应用就会开始收听。
为什么?
一旦我使用以下方法:
public void startListen(String port) {
try {
serverSocket = new ServerSocket(Integer.parseInt(port));
portField.setEditable(false);
} catch (IOException e) {
printMessage("Failed to initiate serverSocket: " + e.toString());
}}
端口在命令提示符下显示为Listening(使用netstat
)。如果我不调用它,则端口不会被列为监听。
TCP 0.0.0.0:5000计算机:0聆听
那么,使用ServerSocket
对象时,我在这里找不到任何东西吗?在我致电ServerSocket
之前,我使用accept()
的旧程序才开始收听。
答案 0 :(得分:7)
如果您正在谈论Java ServerSocket,那么它没有listen
方法,大概是因为它与客户端套接字不同。在这种情况下,一旦它有一个端口号(在构造函数中或作为bind
的一部分),它就可以继续自动监听。
“常规”套接字(la BSD)有一个监听的原因是因为客户端和服务器使用了相同的类型,所以你需要自己决定如何使用它。 ServerSocket
的情况并非如此,因为它是服务器套接字: - )
说实话,我不确定为什么在调用accept
之前你是否关心是否有效。这是“监听”调用(这个类中隐含的),应该标记您的服务器为业务开放。此时,通信层应该开始允许传入呼叫排队等待您呼叫accept
。这通常是他们工作的方式,排队请求以防你的程序接受它们的速度有点慢。
就它为什么这样做而言,它实际上应该根据源代码来实现。在OpenJDK6 source/share/classes/java/net/ServerSocket.java
中,构造函数最终都调用了一个构造函数:
public ServerSocket(int port, int backlog, InetAddress bindAddr)
throws IOException {
setImpl();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException(
"Port value out of range: " + port);
if (backlog < 1)
backlog = 50;
try {
bind(new InetSocketAddress(bindAddr, port), backlog);
} catch(SecurityException e) {
close();
throw e;
} catch(IOException e) {
close();
throw e;
}
}
对bind
(同一文件)的调用如下:
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!oldImpl && isBound())
throw new SocketException("Already bound");
if (endpoint == null)
endpoint = new InetSocketAddress(0);
if (!(endpoint instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
InetSocketAddress epoint = (InetSocketAddress) endpoint;
if (epoint.isUnresolved())
throw new SocketException("Unresolved address");
if (backlog < 1)
backlog = 50;
try {
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkListen(epoint.getPort());
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
bound = true;
} catch(SecurityException e) {
bound = false;
throw e;
} catch(IOException e) {
bound = false;
throw e;
}
}
相关位是:
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
意味着在创建套接字时,bind
和 listen
都在较低级别完成。
所以问题不在于“为什么它突然出现在netstat
?”但“为什么它不出现在netstat
之前?”
我可能会将此归结为您的错误阅读或netstat
的不太好的实现。前者更有可能,除非您专门测试未调用accept
的套接字,这是不可能的。
答案 1 :(得分:2)
我认为你对accept
的目的略有错误。将ServerSocket分配到队列,将accept
分配给阻止出列操作。套接字一旦绑定到端口就会将传入连接排入队列,accept
方法会按照自己的节奏将它们出列。所以,是的,他们可以更好地命名accept
,更不用说混淆了。
答案 2 :(得分:0)
一个关键原因可能是myServerSocket.setSoTimeout()
。 accept()
调用会阻塞-除非您在调用之前定义了超时,否则它只会在该持续时间内阻塞,然后无害地(即ServerSocket仍然有效)抛出SocketTimeoutException
。
这样,线程就在您的控制之下……但是当您不在暂时阻止accept()
的调用中,在那毫秒内发生了什么?客户是否会找到监听端口? -这就是为什么端口监听时不需要accept()
调用是一件好事的原因。
问题在于,应用程序一开始就开始监听 初始化
ServerSocket
对象serverSocket = new ServerSocket(5000)
,而不是初始化serverSocket.accept()
。
我很感谢这个问题(-> upvote),因为这正是我想知道的这种行为,而无需进行实验。 Google的术语是“ java serversocket如果尝试在不接受的情况下进行连接会发生什么情况”,那么这个问题就出现在第一个匹配项的链接列表中。