我正在用Java编写网络软件,但是通过“真正的”网络使用我的应用程序时遇到了一个真正的问题。
让软件成为主机,并监听客户端连接。 这是我的服务器循环:
public void run() {
while (mServerSocket != null) {
try {
Socket wClient = mServerSocket.accept();
System.out.println("Client connecté");
wClient.setSoTimeout(50);
wClient.setTcpNoDelay(false);
Client c = new Client(wClient);
synchronized(this) {
mWaitingClients.add(c);
c.start();
}
} catch(Exception ex) {
System.out.println("Server error : " + ex.getMessage());
}
}
}
当客户端尝试连接到服务器时,我使用此功能:
public Client connect(InetAddress addr, int port) throws Exception {
Socket socket = new Socket(addr, port);
socket.setSoTimeout(50);
socket.setTcpNoDelay(false);
Client c = new Client(socket);
c.start();
return c;
}
这是客户端循环:
public void run() {
try {
ObjectOutputStream out = new ObjectOutputStream(mSocket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(mSocket.getInputStream());
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
}
out.flush();
mOutMessages.clear();
Thread.sleep(50);
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
Thread.sleep(50);
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
}
程序的其他一些部分执行Message并将它们放在客户端的输出列表中(mOutMessages)。 程序的其他一些部分从客户端的mInMessages读取消息。
但这有点不对劲。它在本地工作正常(服务器和客户端在同一台计算机上),但使用两台计算机(使用LAN或通过Internet)发生故障或有害(有些消息已发送但从未收到)。 服务器检测到来自客户端的连接,向客户端发送“握手”消息,但客户端从未接收到它们。
我更像是一个C程序员而不是Java程序员,我使用libc套接字从来没有遇到过这种问题,那么,为什么我的做法是错误的呢?
谢谢!
编辑: 我的服务器是使用此功能创建的:
public void open(int port) throws Exception {
mServerSocket = new ServerSocket(port);
start(); // Call the run mentionned above.
}
编辑: 这是我的解决方案,也许它不完美但它有效!
public void run() {
try {
BufferedOutputStream buf_out = new BufferedOutputStream(
mSocket.getOutputStream()
);
BufferedInputStream buf_in = new BufferedInputStream(
mSocket.getInputStream()
);
ObjectOutputStream out = new ObjectOutputStream(buf_out);
out.flush();
ObjectInputStream in = new ObjectInputStream(buf_in);
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
out.flush();
}
mOutMessages.clear();
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
答案 0 :(得分:0)
如果我理解正确,客户端和服务器都使用run
方法。如果客户端和服务器同时写入足够大的消息(不适合所涉及的缓冲区),则会出现死锁,因为两个伙伴都没有进入读取状态(这会耗尽整个缓冲区)。由于网络延迟,这可能仅发生在非本地场景中,即可能有足够的时间在mOutMessages
缓冲区中堆积足够的消息。
请注意,Socket.setSoTimeout
(您使用过的)的文档仅表示它会影响read()。 (例如,在我的JDK中,ObjectOutputStream似乎使用缓冲区大小为1024字节的BlockDataOutputStream。)
我建议使用单独的线程进行读/写,或者(如果知道最大消息大小)使用足够大的缓冲区(将SocketOutputStream
包裹在BufferedOutputStream
中)。如果您选择更大的缓冲区,您可能还希望一次编写一条消息(并尝试在每个消息之后读取消息)。