Java多线程客户端/服务器-java.net.SocketException:套接字已关闭 [英] Java Multithreaded client/server - java.net.SocketException: Socket closed

查看:101
本文介绍了Java多线程客户端/服务器-java.net.SocketException:套接字已关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须使用Java的套接字API编写多线程客户端和服务器.客户端和服务器都是多线程的,因此服务器可以处理多个连接,客户端可以测试服务器处理连接的能力.

I have to write a multithreaded client and server using Java's socket api. Both the client and server are multithreaded, so the server can handle multiple connections and the client can test the server's ability to handle connections.

我的代码在这里: https://github.com/sandyw/Simple-Java -客户端服务器

我遇到了一些可能与之相关的问题.一,偶尔有一个客户端线程会抛出

I am having a couple, probably related issues. One, occasionally one of the client threads will throw

java.net.SocketException: Socket closed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
    at java.io.InputStreamReader.read(InputStreamReader.java:167)
    at java.io.BufferedReader.fill(BufferedReader.java:136)
    at java.io.BufferedReader.readLine(BufferedReader.java:299)
    at java.io.BufferedReader.readLine(BufferedReader.java:362)
    at ClientThread.run(ClientThread.java:45)
    at java.lang.Thread.run(Thread.java:680)

从其位置来看,这意味着服务器在客户端完成从套接字读取操作之前正在关闭其套接字.我该如何预防?

Which from its position means the server is closing its socket before the client is done reading from it. How can I prevent this?

此外,即使计算抛出Socket closed异常的线程,似乎也不是所有的服务器线程都在发送其输出.例如,30个服务器线程中的23个将发送其输出,但是客户端线程中只有22个将接收任何内容.通讯显然在某处迷路了,如何防止这种情况发生?

Also, even counting the threads which throw the Socket closed exception, it seems that not all the server threads are sending their output. For example, 23 of the 30 server threads will send their output, but only 22 of the client threads will receive anything. The communications are obviously getting lost somewhere, how do I prevent this?

推荐答案

您的问题是ClientThread中的这个问题:

Your problem is this in ClientThread:

private static Socket socket = null;

这意味着所有线程都为ClientThread的所有实例共享相同的套接字实例.这意味着您的套接字将与会话状态不同步.您的对话是:

That means all threads are sharing the same socket instance for all instances of ClientThread. That means your sockets are going to get out of sync with the state of the conversation. Your conversation is:

客户端状态:

  1. 客户端连接
  2. 客户端发送请求
  3. 客户端等待响应
  4. 客户收到回复
  5. 客户端关闭套接字(应由每个客户端完成).

服务器状态:

  1. 服务器等待客户端
  2. 服务器读取请求
  3. 服务器处理命令
  4. 服务器发送响应
  5. 服务器关闭套接字

但是,您有30个会话都试图同时处于不同状态,服务器和客户端无法跟踪它们.第一个线程创建套接字,将请求发送到状态2,等待另一个线程创建另一个套接字,将其写入状态2,当第一个线程唤醒时,它开始处理线程2创建的新套接字尚未完成处理的命令.现在,第三个启动并再次覆盖该引用,依此类推.第一条命令永远不会被读取,因为当线程2重写它时,它会丢失对原始套接字的引用,并且它将开始读取线程2的命令并将其吐出.一旦线程1进入close语句,它将关闭套接字,而其他线程试图对其进行读写,并抛出异常.

However, you have 30 conversations all trying to be in different states at the same time, and the server and client can't keep track of them. First thread creates the socket sends a request moves to state 2, while it's waiting another threads creates another socket, writes it to moves to state 2, when the First thread wakes up it then starts talking over the new socket that was created by thread 2 which hasn't finished processing it's command. Now a third starts and overwrites that reference again, so on and so on. The first command won't ever be read because it lost the reference to the original socket when thread 2 overwrote it, and it starts reading thread 2's command and spitting it out. Once thread 1 makes it to the close statements it closes the socket down while other threads are trying to read/write to it, and bang the exception is tossed.

基本上,每个ClientThread都应该创建自己的套接字实例,然后每个对话都可以独立于其他正在进行的对话而发生.考虑一下您是否将客户端编写为单线程.然后启动两个单独的过程(运行Java应用两次).每个进程将创建自己的套接字,每个对话将独立进行.您现在拥有的是一个带有30个线程的套接字,通过一个扩音器向服务器喊命令.每个人都喊着同一个扩音器时,工作无法有条不紊地进行.

Essentially each ClientThread should create their own socket instance, and then each conversation can occur independently than the other conversations going on. Think if you wrote your client to be single threaded. Then started up two separate processes (run your Java app twice). Each process would create their own socket and each conversation would work independently. What you have now is one socket with 30 threads shouting commands over one megaphone to the server. Work can't proceed in an orderly fashion when everyone is shouting over the same megaphone.

因此,总的来说,更改要从ClientThread的套接字成员中删除静态修饰符,它应该可以很好地开始工作.

So in summary change remove static modifier from socket member in ClientThread, and it should start working pretty well.

顺便说一句,永远不要将这段代码发布到世界上.它存在严重的安全问题,因为客户端可以在服务器进程正在运行的安全级别执行任何命令.因此,任何人都可以相对轻松地拥有您的计算机或捕获您的帐户,这非常容易.从客户端执行这样的命令意味着它们可以发送:

Just as an aside never release this code into the world. It has serious security problems because clients can execute any command at the security level your server process is running. So very easily anyone could own your machine or capture your account with relatively ease. Executing commands like that from the client means they could send:

sudo cat /etc/passwd

例如,捕获您的密码哈希.我认为您只是在学习,但我想应该提醒您注意自己所做的一切,以确保安全.

And capture your password hashes for example. I think you are just learning, but I felt like you should be alerted to what you're doing to be on the safe side.

另一件事是,如果对话按预期进行,则服务器仅关闭套接字.您确实应该将close()调用移到您拥有的try块上的finally块中.否则,如果客户端过早关闭了套接字(发生这种情况),则服务器将泄漏套接字,最终它将耗尽操作系统中的套接字.

Another thing is the server is only closing down sockets if the conversation went as expected. You really should move the close() calls into the finally block on the try block you have. Otherwise if a client closes its socket prematurely (it happens) then your server will leak sockets and eventually it will run out of sockets in the OS.

public void run() {
   try {
   } catch( SomeException ex ) {
      logger.error( "Something bad happened", ex );
   } finally {
      out.close();   <<<< not a bad idea to try {} finally {} these statements too.
      in.close();    <<<< not a bad idea to try {} finally {} these statements too.
      socket.close();
   }
}

您可能想探索的另一件事是在服务器上使用线程池,而不是为每个新连接都新建一个线程.在您的简单示例中,很容易对此进行操作,并且可以正常工作.但是,如果您要构建真正的服务器,则线程池有两个主要作用. 1.创建线程具有相关的开销,因此,通过让线程四处等待服务传入的请求,您可以获得一些性能响应时间.您可以节省一些时间来回应客户. 2.更重要的是,物理计算机无法无休止地创建线程.如果有很多客户说1000+,那么您的机器将很难回答所有使用1000个线程的客户.线程池意味着您创建最大线程数(例如50个线程),并且每个线程将被多次使用以处理每个请求.随着新连接的进入,它们在处理线程之前等待线程释放.如果连接过多,客户端将超时,而不是破坏需要重新启动的计算机.它可以让您更快地处理更多请求,同时避免一次连接过多而使您丧命.

Another thing you might want to explore is using Thread pools on the server instead of newing up a thread for every new connection you get. In your simple example it's easy to play around with this, and it works. However, if you were building a real server a thread pool has two major contributions. 1. creating threads has an overhead associated with it so you can get some performance response time by have a thread sitting around waiting to service incoming requests. You can save yourself sometime to respond to clients. 2. More importantly a physical computer can't endlessly create threads. If you had lots of clients say 1000+ your machine would be hard pressed to answer all of those using 1000 threads. A thread pool means you create max number of threads, say 50 threads, and each thread will be used multiple times to process each request. As new connections come in they wait for a thread to free up before being processed. If you get too many connections coming in the clients will timeout as opposed to destroying your machine requiring a reboot. It allows you to get through more requests faster while keeping you from dying should too many connections come in at once.

最后,由于许多有效的原因,可能会发生套接字关闭异常.通常,如果客户端只是在对话过程中关闭,服务器就会收到该异常.最好在发生这种情况时正确关闭并清理自己.你永远无法避免这是我的观点.您只需要响应它即可.

Finally, a close socket exception can happen for many valid reasons. Often if a client just shuts down in the middle of a conversation the server will get that exception. It's best to properly shutdown and clean up yourself when that happens. You can't ever prevent it is my point. You just have to respond to it.

这篇关于Java多线程客户端/服务器-java.net.SocketException:套接字已关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆