一、目标

诸如tomcat等web服务器中间件简化了我们web的开发成本,但有时候我们或许并不需要这么一个完备的服务器,只是希望做一个简单地处理或者做特殊用途的服务器。

本文将提供一个HTTP的服务器示例,采用Java的ServerSocket进行编码。随着计算机硬件的提升,以及Java地不断优化,使用Java网络编程实现web服务器在实际性能上已经开始可以跟C进行竞争。

二、代码示例

以下代码分为两块:

1)HttpServer:主要包含一个ServerSocket,用于接收客户端请求。并通过线程池将请求从主线程剥离,分散到各个线程中去处理;

2)RequestHandler:实现了Runnable接口,将获取一个线程来处理各个请求;流的读取和响应遵循HTTP协议。

注意:编写Http服务器和一般的socket程序并没有太大不同,但是你需要遵循HTTP协议,这样对于采用HTTP协议的客户端或者其它服务器就可以直接进行HTTP请求来通讯。

HttpServer

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

/**
 * Http服务器端示例,端口设置为8080,编码设置为UTF-8
 * @author lay
 * @date 2019-01-01
 */
public class HttpServer {
    private static final int port = 8080;

    /**
     * 启动HTTP服务器
     */
    public void start() throws IOException {
        // 初始化线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        // 初始化服务器socket
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("ServerSocket启动完成");
        while (true) {
            // 阻塞等待socket连接
            System.out.println("等待socket");
            Socket socket = serverSocket.accept();
            // 提交至线程池处理
            executor.submit(new RequestHandler(socket));
            System.out.println("提交线程池处理请求");
        }
    }

    public static void main(String[] args) {
        try {
            new HttpServer().start();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

RequestHandler

import java.io.*;
import java.net.Socket;

/**
 * 请求处理类
 * @author lay
 * @date 2019-01-01
 */
public class RequestHandler implements Runnable {
    /**
     * HTTP响应头
     */
    private static final String response = "http/1.1 200 ok";
    private static final String splitStr = "\r\n";

    private Socket         socket;
    private BufferedReader reader;
    private BufferedWriter writer;

    public RequestHandler(Socket socket) throws IOException {
        this.socket = socket;
        this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    }

    /**
     * 响应结果
     * @param content 响应内容
     * @throws IOException IO异常抛出
     */
    public void sendResponse(String content) throws IOException {
        writer.write(String.format("%s%s", response, splitStr));
        writer.write(splitStr);
        writer.write(content);
    }

    /**
     * 获得请求
     * @return 请求文本
     * @throws IOException IO异常抛出
     */
    public String getRequest() throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        String        line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(splitStr);
            // 空字符串
            if (line.isEmpty()) {
                break;
            }
        }
        System.out.println("request:\r\n" + stringBuilder);
        return stringBuilder.toString();
    }

    @Override
    public void run() {
        try {
            String request = getRequest();
            // 这里直接把请求数据响应回去
            sendResponse(request);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

请求处理器这里直接将请求的HTTP内容返回回去了,如果你使用浏览器请求8080端口,你会看到如下内容:

不过浏览器会默认请求一个icon,所以针对一个URL地址会有两个请求

你可以在ico请求的时候返回一个二进制的ico文件流,它将显示在浏览器的tab上。

扩展点:

1)我们可以像tomcat一样去支持Java Servlet API

2)支持如GET、POST、PUT、DELETE等restful请求

3)将程序配置进行XML配置

4)增加管理界面

5)请求跟踪处理

6)缓存、非阻塞IO、通道来增加性能

二、socket编写简单BIO的HTTP服务器的更多相关文章

  1. ROS学习(十二)—— 编写简单的消息发布器和订阅器(C++)

    一.创建发布器节点 1 节点功能: 不断的在ROS网络中广播消息 2 创建节点 (1)打开工作空间目录 cd ~/catkin_ws/src/beginner_tutorials 创建一个发布器节点( ...

  2. 关于Socket编写简单聊天工具的总结(原创)

    这段时间再看socket编程,虽然现在是刚刚接触,但是还是忍不住想写一篇总结,来激励自己努力学习,写的不好的地方,还请大家指教啊! 下面针对一个简单的发送消息和文件的程序说说吧.   首先是服务器需要 ...

  3. socket编写简单回显server

    socket在公司代码中应用比较广,比如接口调用的IPCRPC机制,经常看到这样的代码,但是一直也没有动手写过. 在某个比较大的进程中创建一个子进程,由于父子进程复制会浪费内存,可以将创建进程的命令通 ...

  4. Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】

    本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776 版权声明:本文为博主原创文章,未经博主允许不得转载.    https: ...

  5. 小鸟初学Shell编程(二)编写简单的Shell脚本

    Shell脚本 编写Python.PHP脚本通常需要掌握语言的函数,那么Shell脚本则不需要,只需要掌握Linux命令就可以编写Shell脚本,因为Shell脚本就是由多个Linux命令组成,通过将 ...

  6. C#中使用Socket实现简单Web服务器

    上一篇博客中介绍了怎样使用socket访问web服务器.关键有两个: 熟悉Socket编程: 熟悉HTTP协议. 上一篇主要是通过socket来模拟浏览器向(任何)Web服务器发送(HTTP)请求,重 ...

  7. 运用socket实现简单的服务器客户端交互

    Socket解释: 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意 ...

  8. java Socket实现简单在线聊天(二)

    接<java Socket实现简单在线聊天(一)>,在单客户端连接的基础上,这里第二步需要实现多客户端的连接,也就需要使用到线程.每当有一个新的客户端连接上来,服务端便需要新启动一个线程进 ...

  9. SLAM+语音机器人DIY系列:(二)ROS入门——5.编写简单的消息发布器和订阅器

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

随机推荐

  1. datatime模块

    https://www.cnblogs.com/cindy-cindy/p/6720196.html

  2. 【Oracle 12c】CUUG OCP认证071考试原题解析(30)

    30.choose the best answer Examine the commands used to create DEPARTMENT_DETAILS and COURSE_DETAILS: ...

  3. 【OCP认证12c题库】CUUG 071题库考试原题及答案(28)

    28.choose the best answer Evaluate the following SQL statement: SQL> SELECT promo_id, promo_categ ...

  4. “全栈2019”Java第九十七章:在方法中访问局部内部类成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. C++中重载、覆盖和隐藏

    一,多态性 1,(1)声明了基类的指针,该指针指向基类,该指针永远调用自己的成员函数,不管函数是否为虚函数. (2)声明了派生类的指针,该指针指向该派生类,该指针永远调用自己的成员函数,不管函数是否为 ...

  6. Linux系统NAT模式下设置网络网关

    1.配置Vm网络编辑器 2.配置固定IP地址 命令:vi /etc/sysconfig/network-scripts/ifcfg-ens33 #下面内容直接复制进去,如果有重复的可以去除 TYPE= ...

  7. 何在不联网的情况下ping通主机与虚拟机

    选择NAT模式,VM对windows选择ping操作时选择VMnet8的IP地址.

  8. Python基础部分的疑惑解析(2)

    变量: 变量名由 字母.数字.下划线构成,数字不能做为开头 不能用关键字:另外一些内置的方法也别用 推荐使用下划线命名间两个单词user_id 变量在最后底层处理的时候没什么意义,但是在命名的时候有利 ...

  9. 生成allure测试报告之后,服务器端口无法访问查看生成的report,可能是这样引起的。

    1. 检查防火墙 2. 如果机器有安装ADsafe,请关闭adsafe后重试

  10. 通过源码看原理之 selenium

    # selenium的历史1. selenium1.x:这个时候的selenium,使用的是JavaScript注入技术与浏览器打交道,需要Selenium RC启动一个Server,将操作Web元素 ...