按关键词阅读:
一、多用户服务器多用户服务器是指服务器能同时支持多个用户并发访问服务器所提供的服务资源,如聊天服务、文件传输等 。 上一篇的TCPServer是单用户版本 , 每次只能和一个用户对话 。 我们可以尝试多用户连接 , 开启多个客户端 , 具体操作如下:
文章插图
文章插图
这样就允许同时并行执行多个客户端 , 测试发现 , 单用户版本的TCPServer.java程序能同时支持多个用户并发连接(TCP三次握手) , 但不能同时服务多用户对话 , 只有前一个用户退出后 , 后面的用户才能完成服务器连接 。 多线程技术 , 线程调用的并行执行 。
文章插图
上一篇提到在java中有两种实现多线程的方法 , 一是使用Thread类 , 二是使用Runnable类并实现run()方法 。 下面将使用Runnable类对服务端相关操作功能进行封装 , 结合上一篇 , 就学到了两种多线程实现方法 。
//使用Runnable类 , 作为匿名内部类class Handler implements Runnable {public void run() {//实现run方法}}
服务器面临很多客户的并发连接 , 这种情况的多线程方案一般是:主线程只负责监听客户请求和接受连接请求 , 用一个线程专门负责和一个客户对话 , 即一个客户请求成功后 , 创建一个新线程来专门负责该客户 。 对于这种方案 , 可以用上一篇方式new Thread创建线程 , 但是频繁创建线程需要消耗大量系统资源 。 所以不采用这种方法 。 对于服务器 , 一般使用线程池来管理和复用线程 。 线程池内部维护了若干个线程 , 没有任务的时候 , 这些线程都处于等待状态 。 如果有新任务 , 就分配一个空闲线程执行 。 如果所有线程都处于忙碌状态 , 新任务要么放入队列等待 , 要么增加一个新线程进行处理 。 显然 , 我们采用第2种线程池的方法 。常见创建方法如下:
ExecutorService executorService = Executors.newFixedThreadPool(n);//指定线程数量ExecutorService executorService = Executors.newCachedThreadPool();//动态线程池
接下来就是选择线程池的类型了 。使用第一个固定线程数的线程池 , 显然不够灵活 , 第二种方式的线程池会根据任务数量动态调整线程池的大小 , 作为小并发使用问题不大 , 但其在实际生产环境使用并不合适 , 如果并发量过大 , 常常会引发超出内存错误(OutOfMemoryError) , 根据我们的应用场景 , 可以用这个动态调整线程池 。 二、使用线程池实现服务端多线程1、单线程版本首先 , 与之前的单线程通信对比一下 , 下面代码只能实现单用户与服务端通信 , 如果多用户与服务器通信 , 则出现阻塞 。
//单客户版本 , 每次只能与一个用户建立通信连接public void Service(){while (true){Socket socket=null;try {//此处程序阻塞 , 监听并等待用户发起连接 , 有连接请求就生成一个套接字socket=serverSocket.accept();//本地服务器控制台显示客户连接的用户信息System.out.println("New connection accepted:"+socket.getInetAddress());BufferedReader br=getReader(socket);//字符串输入流PrintWriter pw=getWriter(socket);//字符串输出流pw.println("来自服务器消息:欢迎使用本服务!");String msg=null;//此处程序阻塞 , 每次从输入流中读入一行字符串while ((msg=br.readLine())!=null){//如果用户发送信息为”bye“ , 就结束通信if(msg.equals("bye")){pw.println("来自服务器消息:服务器断开连接 , 结束服务!");System.out.println("客户端离开 。 ");break;}msg=msg.replace("?","!").replace("?","!").replace("吗","").replace("吗?","").replace("在","没");pw.println("来自服务器消息:"+msg);pw.println("来自服务器,重复消息:"+msg);}}catch (IOException e){e.printStackTrace();}finally {try {if (socket!=null)socket.close();//关闭socket连接以及相关的输入输出流}catch (IOException e){e.printStackTrace();}}}}
所以 , 根据上面的分析 , 将该单线程版本服务端与客户端通信对话的功能独立处理 , 由一个线程来处理 。 这样就不会阻塞主进程的执行 。 具体实现如下面 。 2、多线程版本1、创建匿名内部类Handler , 实现Runnable类的run方法 , 将通信对话放到run()里面:
class Handler implements Runnable {private Socket socket;public Handler(Socket socket) {this.socket = socket;}public void run() {//本地服务器控制台显示客户端连接的用户信息System.out.println("New connection accept:" + socket.getInetAddress());try {BufferedReader br = getReader(socket);PrintWriter pw = getWriter(socket);pw.println("From 服务器:欢迎使用服务!");String msg = null;while ((msg = br.readLine()) != null) {if (msg.trim().equalsIgnoreCase("bye")) {pw.println("From 服务器:服务器已断开连接 , 结束服务!");System.out.println("客户端离开 。 ");break;}pw.println("From 服务器:" + msg);pw.println("来自服务器,重复消息:"+msg);}} catch (IOException e) {e.printStackTrace();} finally {try {if (socket != null)socket.close();} catch (IOException e) {e.printStackTrace();}}}}
稿源:(未知)
【傻大方】网址:http://www.shadafang.com/c/111J2a392020.html
标题:Java多线程技术:实现多用户服务端Socket通信