简体   繁体   English

Java TCP服务器到许多连接

[英]java tcp server to many connections

I wrote a simple TCP server to transfare some user Data to it and save it in an simple MySQL table. 我编写了一个简单的TCP服务器,将一些用户数据转换为该服务器,并将其保存在一个简单的MySQL表中。 If i now run more than 2000 clients after each other it stops working. 如果我彼此之间现在运行超过2000个客户端,它将停止工作。 While running i get some IO error java.io.EOFException you may also see the misstake i made for that. 运行时,我遇到一些IO error java.io.EOFException您可能还会看到我为此而犯的IO error java.io.EOFException But the most importand is that i get this 但最重要的是我明白了

 IO error java.net.SocketException: Connection reset
    Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Unknown Source)
    at Server.main(Server.java:49)

Enough Memory schould be there but the threads are still running and i dont see where i made the misstake that they dont get terminated. 应该有足够的内存,但是线程仍在运行,我看不到我犯错的地方,因为它们没有被终止。 So i got up to 3900 threads running than. 因此,我最多可以运行3900个线程。 So here is the part of the Server: 因此,这是服务器的一部分:

try {
    // create new socket
    ServerSocket sock = new ServerSocket(port);
    textArea.setText(textArea.getText() + "Server started\n");
    while (true) {
        // accept the connection
            Socket newsock = sock.accept();
        // handle the action
        Thread t = new ThreadHandler(newsock);
            newsock.setSoTimeout(2000); // adding client timeout
        t.start();
        }
    } catch (Exception e) {

guess really simple. 猜测真的很简单。 Here is how i handle the socket: 这是我处理套接字的方法:

class ThreadHandler extends Thread {
    private Socket socket;
    private MySQLConnection sqlConnection;

    ThreadHandler(Socket s) {
        socket = s;
        sqlConnection = new MySQLConnection();
    }

    public void run() {
        try {
            DataOutputStream out = new DataOutputStream(
                    socket.getOutputStream());
            DataInputStream in = new DataInputStream(new BufferedInputStream(
                    socket.getInputStream()));
            Server.textArea.append((new Date()) + "\nClient connected IP: " + socket.getInetAddress().toString()+"\n");

            int firstLine = in.readInt(); // get first line for switch

            switch (firstLine) {
            case 0:
                // getting the whole objekt for the database in own lines!
                String name2 = in.readUTF();
                int level2 = in.readInt();
                int kp2 = in.readInt();
                String skill = in.readUTF();

                LeadboardElement element2 = new LeadboardElement();
                element2.setName(name2);
                element2.setLevel(level2);
                element2.setKillPoints(kp2);
                element2.setSkill(skill);
                sqlConnection.saveChaToLeadboard(element2);
                break;
                //case 1 return the top10
###.... shorten here the rest of the cases
                out.close();
            in.close();
            //close this socket
            socket.close();
                    Server.textArea.append("Client disconnected IP: " + socket.getInetAddress().toString()+ "\n" + (new Date())
                            + "\n----------------------------------------------------\n");
            // autoscrolldown
            Server.textArea.setCaretPosition(Server.textArea.getDocument()
                    .getLength());
         } catch (Exception e) {
            System.out.println("IO error " + e);
            try {
                socket.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }finally{
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

the saveChaToLeadboard simply gets the name level kp and skill and uses a preparedStatement so save it to my MySQL Table. saveChaToLeadboard只是获取名KP水平和技能,并使用preparedStatement那么它保存到我的MySQL表。 I hope you can help me i just dont see the misstake of it. 希望您能帮我,我只是看不到它的错误。 I think i need to Join it somewhere but if i put a join at the end of it (after socket.close()) it still does the same. 我想我需要在某个地方加入它,但是如果我在它的末尾(在socket.close()之后)加入一个加入,它仍然会做同样的事情。

Here the save to database methode: 这里保存到数据库方法:

public void saveChaToLeadboard(LeadboardElement element) {
        try {
            // load driver
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection(this.databaseURL
                    + DATABASE_NAME, this.user, this.password);
            // insert values into the prep statement
            preparedStatement = connection
                    .prepareStatement(PREP_INSERT_STATEMENT);
            preparedStatement.setString(1, element.getName());
            preparedStatement.setInt(2, element.getLevel());
            preparedStatement.setInt(3, element.getKillPoints());
            if(!element.getSkill().equalsIgnoreCase("")){
                preparedStatement.setString(4, element.getSkill());
            }else{
                preparedStatement.setString(4, null);
            }
            // execute
            preparedStatement.executeUpdate();
            connection.close();

        } catch (Exception e) {
            Server.textArea.append(e.getMessage() + "\n");
            Server.textArea.setCaretPosition(Server.textArea.getDocument()
                    .getLength());
            try {
                connection.close();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }

Thanks alot! 非常感谢! Regards 问候

Your run() method is mangled, but I suspect that part of the problem is that you are not always closing network sockets and streams. 您的run()方法已损坏,但我怀疑问题的部分原因在于您并不总是关闭网络套接字和流。 In particular, I suspect that you are not closing them if there is an exception while reading, or processing the data you read. 特别是,如果在读取或处理读取的数据时出现异常,我怀疑您没有关闭它们。 You should always close sockets and streams in a finally block (or the Java 7 equivalent). 您应该始终在finally块(或等效的Java 7)中关闭套接字和流。

Another potential problem is that some of the connections may be stalling due to the other end not sending data. 另一个潜在的问题是某些连接可能由于另一端未发送数据而停止。 To deal with that, you would need to set a read timeout on the socket ... so that connections to slow / stuck clients can be closed. 为了解决这个问题,您需要在套接字上设置一个读取超时...,以便可以关闭与慢速/阻塞客户端的连接。

Finally, it is probably unrealistic to even try to process 2000+ connections in parallel with a thread per connection. 最后,甚至尝试与每个连接的线程并行处理2000+个连接可能是不现实的。 That's a LOT of resources 1 . 那是很多资源1 I recommend you use a thread pool with a fixed upper limit in the low hundreds, and stop accepting new connections if all threads are in use. 我建议您使用固定上限为低数的线程池,如果所有线程都在使用中,则停止接受新连接。


1 - Each thread stack occupies at least 64K of memory on a HotSpot JVM, and possibly as much of 1Mb. 1-每个线程堆栈在HotSpot JVM上至少占用64K内存,并且可能多达1Mb。 Then there are the Heap resources that the thread directly or indirectly refers to, and OS resources needed to maintain the state of the threads and the sockets. 然后是线程直接或间接引用的Heap资源,以及维护线程和套接字状态所需的OS资源。 For 2000 threads, that's probably multiple Gb of memory. 对于2000个线程,这可能是多个Gb的内存。

IMHO 2000 threads is on the high side for a single process and 2000 database connections definately is. 对于单个进程,IMHO 2000线程处于较高的位置,而对于2000个数据库连接,则绝对是如此。

Regardless of whether or not you're hitting limits with 2000 incoming connections, your approach simply will not scale. 无论您是否达到2000个传入连接的限制,您的方法都无法扩展。

To acheive scalability you need to look at using resource pools - this means: 为了实现可伸缩性,您需要考虑使用资源池-这意味着:

  • a pool of reader threads reading from the sockets queuing the data for processing. 从套接字读取数据的读取器线程池,以排队处理数据。
  • a pool of worker threads processing the data queued by the reader threads. 一个工作线程池,用于处理读取器线程排队的数据。
  • a pool of database connections used by the worker threads - this connection pool could be adjusted so that each worker thread has it's own connection but the important thing is that you don't continually open and close database connections. 一个工作线程使用的数据库连接池-可以调整此连接池,以便每个工作线程都有自己的连接,但是重要的是您不能持续打开和关闭数据库连接。

Look at the concurreny API for the thread pools and the NIO API for the IO. 查看线程池的并发API和IO的NIO API

This arrangement will allow you to tune your server to acheive the desired throughput. 这种安排将允许您调整服务器以实现所需的吞吐量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM