gpt4 book ai didi

Java TCP 客户端重复连接导致 EMFILE 错误

转载 作者:可可西里 更新时间:2023-11-01 02:50:23 29 4
gpt4 key购买 nike

我的 Java 应用程序与服务器建立 TCP 连接,并通过发送和接收消息每秒与其通信。服务器和客户端都在同一台 Mac 上运行。大约 15-20 分钟后,我的服务器崩溃并出现错误“Errno::EMFILE 打开的文件过多”。这是我的客户端代码:

package testtcp;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class TestTCP extends JPanel
{
JFrame frame = new JFrame("Button Demo");

ScheduledExecutorService executorService;

private Socket socket = null;
private DataInputStream input = null;
private DataOutputStream output = null;
private BufferedReader br = null;

private boolean isMapUpdating = false;


public TestTCP()
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300,300));
frame.add(this);

JButton b1 = new JButton("BLACK");
b1.setPreferredSize(new Dimension(150,50));
b1.setFocusPainted(false); // get rid of border around text
add(b1);
b1.addActionListener((java.awt.event.ActionEvent evt) ->
{
startAcarsConnection();
});

frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

}


public void startAcarsConnection()
{
start();

}

public void start()
{
System.out.println("THREAD START");

// Default timer rate
int timerRate = 1;

executorService = Executors.newSingleThreadScheduledExecutor();

executorService.scheduleAtFixedRate(new Runnable()
{
@Override
public void run()
{
// Create new TCP connection if the map is not currently updating
if(isMapUpdating == false)
{
isMapUpdating = true;

communicateWithServer();
}
}
}, 0, timerRate, TimeUnit.SECONDS);
}


public void stop()
{
executorService.shutdown();
}

public void communicateWithServer()
{
// Create a message to the server
String messageToServer = makeMessageToServer();

// Connect to the client and receive the response
String messageFromServer = connectToClient(messageToServer);

SwingUtilities.invokeLater(() ->
{
messageReceived(messageFromServer);
});

}


public String connectToClient(String messageToServer)
{
String data = "";
// Message from the server that should terminate TCP connection
String terminator = "END_IGOCONNECT_DATA";

try
{
// Create socket and streams
socket = new Socket("192.168.1.2", 7767);
input = new DataInputStream( socket.getInputStream());
output = new DataOutputStream( socket.getOutputStream());

//Send message to the server
output.writeBytes(messageToServer);


System.out.println("MESSAGE TO SERVER FROM CONNECT TO CLIENT: "+messageToServer);


//Read Response
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder sb = new StringBuilder();
String s = "";
int value;

// Process the message from the server and add to the StringBuilder
while((value = br.read()) != -1)
{
// converts int to character
char c = (char)value;

sb.append(c);

if(sb.toString().contains(terminator))
{
break;
}
}

// Create the final string
data = sb.toString();
}

catch (UnknownHostException e)
{
System.out.println("Sock:"+e.getMessage());

// Close Connection
cancelConnection();

// Pop-up message that the airport was not found
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: UnknownHostException",
JOptionPane.ERROR_MESSAGE);

data = "ERROR";
}

catch (EOFException e)
{
System.out.println("EOF:"+e.getMessage());

// Close Connection
cancelConnection();

// Pop-up message that the airport was not found
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: EOFException",
JOptionPane.ERROR_MESSAGE);

data = "ERROR";
}

catch (IOException e)
{
System.out.println("IO:"+e.getMessage());

// Close Connection
cancelConnection();

// Pop-up message that the server was not found
if(!e.getMessage().equals("Socket closed"))
{
String message = "Application was not able to establish connection with X-Plane.\n"
+ "Check whether IP Address and Port number were correctly entered in Settings.\n"
+ "Check whether connection is not being blocked by your firewall.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: IOException",
JOptionPane.ERROR_MESSAGE);
}
// "Connection reset"

data = "ERROR";
}

finally
{
// TO DO!!! DISABLED FOR NOW!! closeSocketPax();
}

return data;
}


public void cancelConnection()
{

executorService.shutdown();

closeSocketPax();

SwingUtilities.invokeLater(() ->
{
System.out.println("Cancel Connection");
});
}


private void closeSocketPax()
{
try
{
if(socket!=null) { socket.close();}
if(input != null) { input.close();}
if(output != null) { output.close();}
if(br != null) { br.close();}
}
catch (IOException ex)
{
String message = "Error closing socket.";
JOptionPane.showMessageDialog(new JFrame(), message, "TCP Connection Error: IOException",
JOptionPane.ERROR_MESSAGE);
}
socket = null;
input = null;
output = null;
br = null;
}

private String makeMessageToServer()
{
return "MESSAGE TO SERVER";
}

private void messageReceived(String message)
{
System.out.println("MESSAGE RECEIVED: "+message);

isMapUpdating = false;
}


public static void main(String[] args)
{
new TestTCP();
}
}

我已经尝试解决这个问题将近一个月了!有没有人看到代码中的问题并且知道如何缓解该问题?非常感谢!

最佳答案

您创建的每个连接都使用一个文件描述符。在任何操作系统中,您的进程可以拥有的描述符数量都有限制。例如,在 Linux 环境中,我的限制是 1024。不同的 O/S 有不同的限制,但在 Linux 和 Mac O/S 等 Unix 派生环境中,您可以运行 ulimit -n 来查看限制是什么。

在您的代码中:

socket = new Socket("192.168.1.2", 7767);

connectToClient 方法中。每次你这样做并且你不关闭套接字时,你就会用完一个文件描述符。最终您达到了 O/S 定义的限制,并且在 Mac O/S 中出现了 Errno::EMFILE 错误。

您有两种选择来解决这个问题。第一个几乎是您注释掉的内容 - 完成后关闭连接。但是,正如您在评论中指出的那样,这种情况经常发生,您不想承担不断打开和关闭的开销。

这给我们带来了第二个选择——重用连接。如果您正在设计的协议(protocol)处理数据,套接字可以一遍又一遍地发送数据。通过协议(protocol)来回发送数据并重用 Socket。

虽然有一个警告 - 如果您的物理连接以某种方式被切断 - 例如,您从以太网切换到 Wi-Fi - 您的服务仍然需要处理可能的错误。您的代码包含大部分内容,但您可能需要考虑在发生这种情况时关闭并尝试重新连接。

关于Java TCP 客户端重复连接导致 EMFILE 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48246383/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com