`
游伯度
  • 浏览: 22017 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

学习Mina2(知识准备 - socket - 2)

阅读更多
1. 一点儿废话
这篇博文拖泥带水的搞了很久,一直没有发出来。我还是要坚持自己是mina学习之路的。
今天我们来解决一下如何来搞定一个server端,多个client的socket通信方式。

2.服务端的支持

2.1 ServerSocket.accept()
Java doc:
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
监听一个Socket连接并接受他。这个方法会一直阻塞直到有一个Socket连接创建。(也就说,accept方法会等待客户端连接,如果没有连接,他将一直等待。

2.2 Server端支持多个客户端
当服务单通过accept方法取得一个socket连接后,他应该取做自己该做的工作,例如从socket中取得客户端给的命令等等,所以他需要一个单独的线程去处理。而服务端应该接到一个客户端的socket连接后,继续等待下一个客户端的到来。所以实现代码应该像下面这样:
		while (true) {
			try {
				Socket client = server.accept();
				// 异步客户端连接处理
			}
			catch (IOException e) {
				LOG.error(e.getMessage(), e);
				throw new RuntimeException(e.getMessage(), e.getCause());
			}
		}

2.3 整体服务端代码
为了测试,我想服务端可以用一个线路在后台运行,所以实现了Runnable接口,如下:
package com.ex.io.socket.thread;

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ex.io.socket.page.Console;

public class MultiClientCmdSocketServer implements Runnable {
	public static final Log LOG = LogFactory.getLog(MultiClientCmdSocketServer.class);

	public static final int SERVER_PROT = 8100;

	private ServerSocket server;
	private Console console;
	private Executor executer;
	private boolean interruptable = false;

	public MultiClientCmdSocketServer() {
		init();
	}

	private void init() {
		try {
			server = new ServerSocket(SERVER_PROT);
		}
		catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e.getCause());
		}

		BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(10);
		executer = new ThreadPoolExecutor(10, 100, 1, TimeUnit.HOURS, workQueue);
	}

	@Override
	public void run() {
		while (true && !interruptable) {
			try {
				Socket client = server.accept();
				executer.execute(new SocketRunnable(client, console));
			}
			catch (IOException e) {
				LOG.error(e.getMessage(), e);
				throw new RuntimeException(e.getMessage(), e.getCause());
			}
		}
	}

	public void setConsole(Console console) {
		this.console = console;
	}

	public void setInterruptable(boolean interruptable) {
		this.interruptable = interruptable;
	}

	private class SocketRunnable implements Runnable {
		private Socket client;
		private Console console;

		public SocketRunnable(Socket client, Console console) {
			this.client = client;
			this.console = console;
		}

		@Override
		public void run() {
			LOG.info(">>>Client[" + client + "]接受命令开始...");
			console.info("Receive command from client [" + client + "] start ...");
			BufferedReader reader = null;
			PrintWriter writer = null;
			try {
				reader =new BufferedReader(new InputStreamReader(client.getInputStream()));
				writer = new PrintWriter(client.getOutputStream());
				while (true) {
					String cmd = reader.readLine();
					writer.print("roger : " + cmd + "\n");
					console.info("roger from client [" + client + "]: " + cmd);
					writer.flush();
					
					if (StringUtils.endsWithIgnoreCase(":quit", cmd)) {
						break;
					}
				}
				client.close();
			}
			catch (Exception e) {
				LOG.error(e.getMessage(), e);
				throw new RuntimeException(e.getMessage(), e.getCause());
			}
			finally {
				IOUtils.closeQuietly(reader);
				IOUtils.closeQuietly(writer);
			}
			console.info("Receive cmd from client [" + client + "] end");
			LOG.info("<<<Client[" + client + "]接受命令结束.");
		}

	}
}


3. 客户端支持
说完服务端,客户端就显得很简单,只需要有真是的客户端连上,获自己写个线程模拟一下。为了不从系统IO中获取输入,我用了一个BlockingQueue。
import java.io.*;
import java.net.*;
import java.util.concurrent.*;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ex.io.socket.page.Console;

public class SocketClientRunnable implements Runnable {
	public static final Log LOG = LogFactory.getLog(SocketClientRunnable.class);

	private String name;
	private Console console;
	private BlockingQueue<String> inputQueue = new LinkedBlockingQueue<String>(20);

	public SocketClientRunnable(String name, Console console) {
		this.name = name;
		this.console = console;
	}

	public void putCmd(String cmd) {
		try {
			inputQueue.put(cmd);
		}
		catch (InterruptedException e) {
			throw new RuntimeException(e.getMessage(), e.getCause());
		}
	}

	@Override
	public void run() {
		console.info(">>>Client [" + name + "] start >>>");
		BufferedReader reader = null;
		BufferedWriter writer = null;
		try {
			// Get socket from server
			Socket server =
					new Socket(InetAddress.getLocalHost(), MultiClientCmdSocketServer.SERVER_PROT);

			reader = new BufferedReader(new InputStreamReader(server.getInputStream()));
			writer = new BufferedWriter(new OutputStreamWriter(server.getOutputStream()));

			while (true) {
				String cmd = inputQueue.take();
				console.info(name + " puts command [" + cmd + "] to server");
				writer.write(cmd.concat("\n"));
				writer.flush();

				if (StringUtils.endsWithIgnoreCase(":quit", cmd)) {
					break;
				}

				String serverRoger = reader.readLine();
				String rogerMsg = "Server feed back for client[" + name + "] : " + serverRoger;
				console.info(rogerMsg);
				LOG.info(rogerMsg);
			}

			server.close();
		}
		catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e.getCause());
		}
		finally {
			IOUtils.closeQuietly(reader);
			IOUtils.closeQuietly(writer);
		}
		console.info("<<<Client [" + name + "] end <<<");
	}

}


4. 如果你比较有余力,还可以用Java Swing演示一下
上面代码中的console就是为向Swing中打日志,如果不需要,直接删除


5. 小结一下
  • 服务端支持多客户端,增加多线程处理,让主线程等待客户端的到来
  • 客户端连接,可以通过线程模拟

就这么简单
  • 大小: 47 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics