⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 generalserver.java

📁 java jdk 实例宝典 源码 夏先波 编著 随书光盘源码
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
		public void run() {
			// 如果标识要停止侦听器,则一直运行
			while (!stop) {
				try {
					// 等待连接请求
					Socket client = listen_socket.accept();
					// 将收到的请求加入到服务器上
					addConnection(client, service);
				} catch (InterruptedIOException e) {
				} catch (IOException e) {
					log(e);
				}
			}
		}
	}

	/**
	 * 内部类,描述连接,处理客户端和服务之间连接。
	 * 因为每个连接都用线程,可以独立运行。
	 * 这是实现服务器支持多线程的关键点。
	 */
	public class Connection extends Thread {
		// 连接的客户端
		Socket client;
		// 客户端请求的服务
		Service service;

		/**
		 * 构造方法。被侦听器线程调用,由于侦听器线程属于服务器线程组,
		 * 所以连接的线程也属于服务器线程组。
		 * @param client	
		 * @param service	
		 */
		public Connection(Socket client, Service service) {
			super("Server.Connection:"
					+ client.getInetAddress().getHostAddress() + ":"
					+ client.getPort());
			this.client = client;
			this.service = service;
		}
		/**
		 * 连接的线程体
		 * 获取来自客户端的输入流和输出流,然后调用服务的serve方法。
		 * 服务处理完请求后,关闭连接。
		 */
		public void run() {
			try {
				InputStream in = client.getInputStream();
				OutputStream out = client.getOutputStream();
				// 调用具体的服务
				service.serve(in, out);
			} catch (IOException e) {
				log(e);
			} finally {
				// 关闭连接
				endConnection(this);
			}
		}
	}

	/**
	 * 服务的接口定义。服务器上所有服务都必须实现该接口。
	 * 由于服务器使用了反射机制通过服务类的无参数构造方法创建服务的实例,
	 * 所以所有的服务实现类都必须提供一个无参数的构造方法。
	 */
	public interface Service {
		/**
		 * 服务方法
		 * @param in  客户端的输入流
		 * @param out	客户端的输出流
		 * @throws IOException
		 */
		public void serve(InputStream in, OutputStream out) throws IOException;
	}

	/**
	 * 一个简单的服务,向客户端通知服务器上的当前时间
	 **/
	public static class Time implements Service {
		public void serve(InputStream i, OutputStream o) throws IOException {
			PrintWriter out = new PrintWriter(o);
			out.print(new Date() + LINE_SEPERATOR);
			out.close();
			i.close();
		}
	}

	/**
	 * 倒序字符串的服务。将客户端输入的字符串,倒序后返回。
	 * 当客户端输入一行"."时,关闭连接。
	 */
	public static class Reverse implements Service {
		public void serve(InputStream i, OutputStream o) throws IOException {
			BufferedReader in = new BufferedReader(new InputStreamReader(i));
			PrintWriter out = new PrintWriter(new BufferedWriter(
					new OutputStreamWriter(o)));
			out.print("Welcome to the line reversal server." + LINE_SEPERATOR);
			out.print("Enter lines.  End with a '.' on a line by itself." + LINE_SEPERATOR);
			for (;;) {
				out.print("> ");
				out.flush();
				// 从客户端的输入流中取出一行
				String line = in.readLine();
				if ((line == null) || line.equals(".")){
					break;
				}
				// 将字符串倒序返回
				for (int j = line.length() - 1; j >= 0; j--){
					out.print(line.charAt(j));
				}
				out.print(LINE_SEPERATOR);
			}
			out.close();
			in.close();
		}
	}


	/**
	 * 计数器服务,这个服务通过实例变量保存访问该服务的连接数。
	 * 每一次连接都将计数器加1。
	 **/
	public static class UniqueID implements Service {
		public int id = 0;

		public synchronized int nextId() {
			return id++;
		}

		public void serve(InputStream i, OutputStream o) throws IOException {
			PrintWriter out = new PrintWriter(o);
			out.print("You are client #: " + nextId() + LINE_SEPERATOR);
			out.close();
			i.close();
		}
	}

	/**
	 * 控制服务器的服务。通过密码认证。
	 * 客户端提供命令,该服务执行命令,控制服务器的状态。
	 * 命令有:
	 * (1)password: 输入密码,只有密码正确才能够执行其他命令
	 * (2)add: 增加服务命令,后面跟着服务名和端口号
	 * (3)remove: 删除服务命令,后面跟端口号
	 * (4)max: 修改服务器的最大并发连接数
	 * (5)status: 显示服务器的状态
	 * (6)help: 显示帮助信息
	 * (7)quit: 退出控制服务
	 * 一个服务器同时只能有一个客户端连接到它的控制服务上。
	 */
	public static class Control implements Service {
		// 待控制的服务器
		GeneralServer server;
		// 正确的密码
		String password;
		// 标识是否已经有客户端连接到该服务上
		boolean connected = false; 
		// 帮助信息
		String helpMsg = "COMMANDS:" + LINE_SEPERATOR + "\tpassword <password>" + LINE_SEPERATOR
				+ "\tadd <service> <port>" + LINE_SEPERATOR + "\tremove <port>" + LINE_SEPERATOR
				+ "\tmax <max-connections>" + LINE_SEPERATOR + "\tstatus" + LINE_SEPERATOR 
				+ "\thelp" + LINE_SEPERATOR	+ "\tquit" + LINE_SEPERATOR;

		// 默认构造方法,私有方法,
		// 表示该服务不能被服务器动态加载,只能构在启动服务器时静态加载
		private Control(){
			// do nothig
		}
	
		/**
		 * 构造控制服务。控制特定的服务器。
		 * 根据密码认证客户端是否有权限控制服务器。
		 * @param server
		 * @param password
		 */
		public Control(GeneralServer server, String password) {
			this.server = server;
			this.password = password;
		}

		/**
		 * 提供的服务。读取客户端的输入,使用java.util.StringTokenizer解析命令。
		 * 根据命令,调用服务器的控制操作。
		 **/
		public void serve(InputStream i, OutputStream o) throws IOException {
			BufferedReader in = new BufferedReader(new InputStreamReader(i));
			PrintWriter out = new PrintWriter(o);
			String line; 
			// 标识是否经过的认证
			boolean authorized = false;

			// 这里使用了同步锁,
			// 标识如果有客户端连接到了该服务,其他客户端将不能进入该服务。
			synchronized (this) {
				if (connected) {
					out.print("ONLY ONE CONTROL CONNECTION ALLOWED." + LINE_SEPERATOR);
					out.close();
					return;
				} else {
					connected = true;
				}
			}

			// 解析命令,执行命令
			for (;;) { 
				// 给客户端一个提示符
				out.print("> "); 
				out.flush(); 
				// 读取客户端输入
				line = in.readLine();
				if (line == null){
					// 如果没有输入,结束
					break;
				}
				try {
					// 使用StringTokenizer分析命令
					StringTokenizer t = new StringTokenizer(line);
					if (!t.hasMoreTokens()){
						// 如果是一个空串,则继续下一次循环
						continue;
					}
					// 获取第一个命令
					String command = t.nextToken().toLowerCase();
					//根据命令作不同的处理
					if (command.equals("password")) {
						// 获取输入的密码
						String p = t.nextToken();
						// 匹配密码
						if (p.equals(this.password)) { 
							// 匹配成功
							out.print("OK" + LINE_SEPERATOR);
							authorized = true;
						} else {
							// 匹配失败
							out.print("INVALID PASSWORD" + LINE_SEPERATOR); 
						}
						
					} else if (command.equals("add")) { 
						// 增加服务
						// 首先判断是否已经通过认证
						if (!authorized) {
							out.print("PASSWORD REQUIRED" + LINE_SEPERATOR);
						} else {
							// 获取服务名,并动态加载
							String serviceName = t.nextToken();
							Class serviceClass = Class.forName(serviceName);
							Service service;
							try {
								service = (Service) serviceClass.newInstance();
							} catch (NoSuchMethodError e) {
								throw new IllegalArgumentException(
										"Service must have a "
												+ "no-argument constructor");
							}
							// 获取端口号
							int port = Integer.parseInt(t.nextToken());
							// 添加到服务器
							server.addService(service, port);
							out.print("SERVICE ADDED" + LINE_SEPERATOR);
						}
						
					} else if (command.equals("remove")) {
						// 删除服务
						if (!authorized) {
							out.print("PASSWORD REQUIRED" + LINE_SEPERATOR);
						} else {
							// 获取端口号
							int port = Integer.parseInt(t.nextToken());
							// 从服务器上删除
							server.removeService(port);
							out.print("SERVICE REMOVED" + LINE_SEPERATOR);
						}
						
					} else if (command.equals("max")) {
						// 设置服务器最大并发连接数
						if (!authorized) {
							out.print("PASSWORD REQUIRED" + LINE_SEPERATOR);
						} else {
							int max = Integer.parseInt(t.nextToken());
							server.setMaxConnections(max);
							out.print("MAX CONNECTIONS CHANGED" + LINE_SEPERATOR);
						}
						
					} else if (command.equals("status")) {
						// 显示服务器状态
						if (!authorized) {
							out.print("PASSWORD REQUIRED" + LINE_SEPERATOR);
						} else {
							server.displayStatus(out);
						}
						
					} else if (command.equals("help")) { 
						// 显示帮助信息
						out.print(helpMsg);
						
					} else if (command.equals("quit")) {
						// 退出命令
						break; 
						
					} else {
						out.print("UNRECOGNIZED COMMAND" + LINE_SEPERATOR); 
					}
				} catch (Exception e) {
					out.print("ERROR WHILE PARSING OR EXECUTING COMMAND:" + LINE_SEPERATOR
							+ e	+ LINE_SEPERATOR);
				}
			}
			// 执行完客户端命令后,将标志位置为false,其他客户端便可访问该服务了。
			connected = false;
			out.close();
			in.close();
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -