您的问题是ClientThread中的问题:
private static Socket socket = null;
这意味着所有线程都为ClientThread的所有实例共享相同的套接字实例。这意味着您的套接字将与会话状态不同步。您的对话是:
客户陈述:
- 客户端连接
- 客户发送请求
- 客户等待回应
- 客户收到回应
- 客户端关闭套接字(应由每个客户端完成)。
服务器状态:
- 服务器等待客户端
- 服务器读取请求
- 服务器进程命令
- 服务器发送响应
- 服务器关闭套接字
但是,您有30个会话都试图同时处于不同状态,服务器和客户端无法跟踪它们。第一个线程创建套接字,将请求发送到状态2,等待另一个线程创建另一个套接字,将其写入状态2,当第一个线程唤醒时,它开始处理线程2创建的新套接字尚未完成处理的命令。现在,第三个开始,并再次覆盖该引用,依此类推。第一条命令永远不会被读取,因为当线程2重写它时,它将丢失对原始套接字的引用,并且它开始读取线程2的命令并将其吐出。线程1进入close语句后,它将关闭套接字,而其他线程试图对其进行读写,
本质上,每个ClientThread应该创建自己的套接字实例,然后每个对话都可以独立于其他正在进行的对话而发生。考虑一下您是否将客户端编写为单线程。然后启动两个单独的进程(运行Java应用两次)。每个进程将创建自己的套接字,每个会话将独立进行。您现在拥有的是一个带有30个线程的套接字,通过一个扩音器向服务器喊命令。当所有人都喊着同一个扩音器时,工作无法有条不紊地进行。
因此,总的来说,从ClientThread的套接字成员中删除static修饰符,它应该可以很好地开始工作。
顺便说一句,永远不要将这段代码发布给世人。它存在严重的安全问题,因为客户端可以在服务器进程正在运行的安全级别执行任何命令。因此,任何人都可以轻松拥有您的机器或相对容易地捕获您的帐户。从客户端执行这样的命令意味着它们可以发送:
sudo cat /etc/passwd
并例如捕获您的密码哈希。我认为您只是在学习,但是我觉得应该提醒您注意自己所做的一切,以确保安全。
另一件事是,如果对话按预期进行,则服务器仅关闭套接字。您确实应该将close()调用移至您拥有的try块上的finally块中。否则,如果客户端过早关闭其套接字(发生这种情况),则服务器将泄漏套接字,最终它将耗尽 *** 作系统中的套接字。
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个线程,并且每个线程将被多次使用以处理每个请求。随着新连接的进入,它们在处理线程之前等待线程释放。如果连接过多,客户端将超时,而不是破坏需要重新启动的计算机。它可以让您更快地处理更多请求,同时避免一次连接太多连接而导致死亡。
最后,由于许多有效的原因,可能会发生套接字关闭异常。通常,如果客户端只是在对话过程中关闭,服务器就会收到该异常。最好在发生这种情况时正确关闭并清理自己。你永远无法避免这是我的观点。您只需要响应它。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)