这里有新鲜出炉的 Java 并发编程示例,程序狗速度看过来!
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
本篇文章主要介绍了 Java 使用 NioSocket 手动实现 HTTP 服务器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
NioSocket 简单复习重要概念NioSocket 里面的三个重要概念:Buffer、Channel、Selector
使用 NioSocket 实现通信大概如以下步骤:
- public class HttpServer {
- public static void main(String[] args) throws Exception {
- ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
- serverSocketChannel.socket().bind(new InetSocketAddress((8080)));
- serverSocketChannel.configureBlocking(false);
- Selector selector = Selector.open();
- // It must be ACCEPT, or it will throw exception
- serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
- while (true) {
- if (selector.select(3000) == 0) {
- continue;
- }
- Iterator < SelectionKey > keyIter = selector.selectedKeys().iterator();
- while (keyIter.hasNext()) {
- SelectionKey key = keyIter.next();
- new Thread(new HttpHandler(key)).run();
- keyIter.remove();
- }
- }
- }
- }
以上代码的逻辑大致遵循着 NioSocket 的大概用法,其中 serverSocketChannel 使用 register 方法注册到 selector 仅是 OP_ACCEPT,使用其他操作就会操作。但是并不是说不能进行其他操作,而是其他操作稍后实现。
在 serverSocketChannel.configureBlocking(false) 后,非阻塞模式启动。Server 接收到请求后就会将记录了请求信息的 key 交给 HttpHandler 做详细处理,处理完就把 key 从迭代器里面 remove 掉。可以看到出来,HttpServer 对请求里面的信息一概不知,这样才能成为一个出色的管理层,它管理着 HttpHandler 来处理请求。
既然选用了 NioSocket 这样的 New IO,HttpHandler 必然是多线程的实现(否则还有什么意义)。
创建 HttpHandler 来处理请求对于来自 HttpServer 的不加工信息,HttpHandler 必须要做全套,因此需要 HttpHandler 自己考虑好有没有中文乱码、Buffer 大小是多少等等。HttpHandler 大概框架如下即可:
- class HttpHandler implements Runnable {
- private int bufferSize = 1024;
- private String localCharset = "UTF-8";
- private SelectionKey key;
- public HttpHandler(SelectionKey key) {
- this.key = key;
- }
- public void handleAccept() throws IOException {}
- public void handleRead() throws IOException {}
- @Override public void run() {
- try {
- if (key.isAcceptable()) {
- handleAccept();
- }
- if (key.isReadable()) {
- handleRead();
- }
- } catch(IOException ex) {
- ex.printStackTrace();
- }
- }
- }
如上框架简单明了,重载 run 实现多线程,handleAccept 和 handleRead 用于详细地处理相关操作,bufferSize 规定 Buffer 大小,localCharset 的设定提前防止中文乱码。
需要注意的是 HttpServer 里面,我们只注册了 OP_ACCEPT 这个操作,那么在 HttpHandler 里面只有 isAcceptable() 判定为真,那么 handleRead() 怎么办呢?我们会在 handleAccept() 注册好的:
- public void handleAccept() throws IOException{
- SocketChannel clientChannel =
- ((ServerSocketChannel)key.channel()).accept();
- clientChannel.configureBlocking(false);
- clientChannel.register(
- key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize)
- );
- }
在 handleAccept 里面,我们先取得 key 里面的请求信息,如对应客户端的 SocketChannel (SocketChannel 需要 ServerSocketChannel 接受了后才有),接着就可以为 SocketChannel 注册 OP_READ 操作了,带上指定大小的 Buffer。注册后,key 可是 isReadable() 了,接下来则是在 handleRead 中对 key 进行解剖处理:(代码有点长,但大多是控制台输出和对字符串的拼接操作,看官可放心食用。)
- public void handleRead() throws IOException {
- SocketChannel sc = (SocketChannel) key.channel();
- ByteBuffer buffer = (ByteBuffer) key.attachment();
- buffer.clear();
- if (sc.read(buffer) == -1) {
- sc.close();
- } else {
- buffer.flip();
- String receiveString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
- String[] requestMessage = receiveString.split("\r\n");
- for (String s: requestMessage) {
- System.out.println(s);
- if (s.isEmpty()) {
- break;
- }
- }
- String[] firstLine = requestMessage[0].split(" ");
- System.out.println();
- System.out.println("Method:\t" + firstLine[0]);
- System.out.println("url:\t" + firstLine[1]);
- System.out.println("HTTP Version:\t" + firstLine[2]);
- System.out.println();
- StringBuilder sendString = new StringBuilder();
- sendString.append("HTTP/1.1 200 OK\r\n");
- sendString.append("Content-Type:text/html;Charset=" + localCharset + "\r\n");
- sendString.append("\r\n");
- sendString.append("<html><head><title>SHOW</title></head></body>");
- sendString.append("Received:<br/>");
- for (String s: requestMessage) {
- sendString.append(s + "<br/>");
- }
- sendString.append("</body></html>");
- buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
- sc.write(buffer);
- sc.close();
- }
- }
handleRead 开头先获取到对应的 SocketChannel 和 ByteBuffer,就这两个最为关键,SocketChannel 负责与客户端的链接和传输数据,而 ByteBuffer 充当数据运输的载体。
而后则是简单的判断连接状态,若是连接,将相关信息输出到控制台,并拼接出 HTTP 头的字符串发送至客户端。
效果如图:
来源: http://www.phperz.com/article/17/1230/357092.html