网络编程--05 HTTP服务器

Posted Anrys

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程--05 HTTP服务器相关的知识,希望对你有一定的参考价值。

6.HTTP服务器

6.1需求

编写服务器端代码,实现可以解析浏览器的请求,给浏览器响应数据

6.2环境搭建

实现步骤:
1.编写HttpServer类,实现可以接收浏览器发出的请求
2.其中获取连接的代码可以单独抽取到一个类中
代码:

// 服务端代码
public class HttpServer {
    public static void main(String[] args) throws IOException {
        //1.打开服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.让这个通道绑定一个端口
        serverSocketChannel.bind(new InetSocketAddress(10000));
        //3.设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        //4.打开一个选择器
        Selector selector = Selector.open();

        //5.绑定选择器和服务端通道
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

        while(true){
            //6.选择器会监视通道的状态.
            int count = selector.select();
            if(count != 0){
                //7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.
                //获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    //selectionKey 依次表示每一个服务端通道的令牌
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()){
                        //获取连接
                        AcceptHandler acceptHandler = new AcceptHandler();
                        acceptHandler.connSocketChannel(selectionKey);
                    }else if(selectionKey.isReadable()){
                       
                    }
                    //任务处理完毕以后,将SelectionKey从集合中移除
                    iterator.remove();
                }
            }
        }
    }
}
// 将获取连接的代码抽取到这个类中
public class AcceptHandler {

    public SocketChannel connSocketChannel(SelectionKey selectionKey){
        try {
            //获取到已经就绪的服务端通道
            ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
            SocketChannel socketChannel = ssc.accept();
            //设置为非阻塞状态
            socketChannel.configureBlocking(false);
            //把socketChannel注册到选择器上
            socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
            return socketChannel;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

6.3获取请求信息并解析

在这里插入图片描述

/**
 * 用来封装请求数据的类
 */
public class HttpRequest {
    private String method; //请求方式
    private String requestURI; //请求的uri
    private String version;   //http的协议版本

    private HashMap<String,String> hm = new HashMap<>();//所有的请求头

    //parse --- 获取请求数据 并解析
    public void parse(SelectionKey selectionKey){
        try {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

            StringBuilder sb = new StringBuilder();
            //创建一个缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int len;
            //循环读取
            while((len = socketChannel.read(byteBuffer)) > 0){
                byteBuffer.flip();
                sb.append(new String(byteBuffer.array(),0,len));
                //System.out.println(new String(byteBuffer.array(),0,len));
                byteBuffer.clear();
            }
            //System.out.println(sb);
            parseHttpRequest(sb);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //解析http请求协议中的数据
    private void parseHttpRequest(StringBuilder sb) {
        //1.需要把StringBuilder先变成一个字符串
        String httpRequestStr = sb.toString();
        //2.获取每一行数据
        String[] split = httpRequestStr.split("\\r\\n");
        //3.获取请求行
        String httpRequestLine = split[0];//GET / HTTP/1.1
        //4.按照空格进行切割,得到请求行中的三部分
        String[] httpRequestInfo = httpRequestLine.split(" ");
        this.method = httpRequestInfo[0];
        this.requestURI = httpRequestInfo[1];
        this.version = httpRequestInfo[2];
        //5.操作每一个请求头
        for (int i = 1; i < split.length; i++) {
            String httpRequestHeaderInfo = split[i];//Host: 127.0.0.1:10000
            String[] httpRequestHeaderInfoArr = httpRequestHeaderInfo.split(": ");
            hm.put(httpRequestHeaderInfoArr[0],httpRequestHeaderInfoArr[1]);
        }

    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public HashMap<String, String> getHm() {
        return hm;
    }

    public void setHm(HashMap<String, String> hm) {
        this.hm = hm;
    }

    @Override
    public String toString() {
        return "HttpRequest{" +
                "method='" + method + '\\'' +
                ", requestURI='" + requestURI + '\\'' +
                ", version='" + version + '\\'' +
                ", hm=" + hm +
                '}';
    }
}

6.4给浏览器响应数据

在这里插入图片描述

public class HttpResponse {
    private String version; //协议版本
    private String status;  //响应状态码
    private String desc;    //状态码的描述信息

    //响应头数据
    private HashMap<String, String> hm = new HashMap<>();

    private HttpRequest httpRequest;  //我们后面要根据请求的数据,来进行一些判断

    //给浏览器响应数据的方法
    public void sendStaticResource(SelectionKey selectionKey) {
        //1.给响应行赋值
        this.version = "HTTP/1.1";
        this.status = "200";
        this.desc = "ok";
        //2.将响应行拼接成一个单独的字符串 // HTTP/1.1 200 ok
        String responseLine = this.version + " " + this.status + " " + this.desc + "\\r\\n";

        //3.给响应头赋值
        hm.put("Content-Type", "text/html;charset=UTF-8");

        //4.将所有的响应头拼接成一个单独的字符串
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> entries = hm.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\\r\\n");
        }

        //5.响应空行
        String emptyLine = "\\r\\n";

        //6.响应行,响应头,响应空行拼接成一个大字符串
        String responseLineStr = responseLine + sb.toString() + emptyLine;

        try {
            //7.将上面三个写给浏览器
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer byteBuffer1 = ByteBuffer.wrap(responseLineStr.getBytes());
            socketChannel.write(byteBuffer1);

            //8.单独操作响应体
            //因为在以后响应体不一定是一个字符串
            //有可能是一个文件,所以单独操作
            String s = "哎哟,妈呀,终于写完了.";
            ByteBuffer byteBuffer2 = ByteBuffer.wrap(s.getBytes());
            socketChannel.write(byteBuffer2);

            //9.释放资源
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public HashMap<String, String> getHm() {
        return hm;
    }

    public void setHm(HashMap<String, String> hm) {
        this.hm = hm;
    }

    public HttpRequest getHttpRequest() {
        return httpRequest;
    }

    public void setHttpRequest(HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    @Override
    public String toString() {
        return "HttpResponse{" +
                "version='" + version + '\\'' +
                ", status='" + status + '\\'' +
                ", desc='" + desc + '\\'' +
                ", hm=" + hm +
                ", httpRequest=" + httpRequest +
                '}';
    }
}

6.5代码优化

在这里插入图片描述
在这里插入图片描述

/**
 * 接收连接的任务处理类
 */
public class AcceptHandler {

    public SocketChannel connSocketChannel(SelectionKey selectionKey){
        try {
            //获取到已经就绪的服务端通道
            ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
            SocketChannel socketChannel = ssc.accept();
            //设置为非阻塞状态
            socketChannel.configureBlocking(false);
            //把socketChannel注册到选择器上
            socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
            return socketChannel;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
/**
 * 接收客户端请求的类
 */
public class HttpServer {
    public static void main(String[] args) throws IOException {
        //1.打开服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.让这个通道绑定一个端口
        serverSocketChannel.bind(new InetSocketAddress(10000));
        //3.设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        //4.打开一个选择器
        Selector selector = Selector.open();
        //5.绑定选择器和服务端通道
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

        while(true){
            //6.选择器会监视通道的状态.
            int count = selector.select();
            if(count != 0){
                //7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.
                //获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    //selectionKey 依次表示每一个服务端通道的令牌
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()){
                        //获取连接
                        AcceptHandler acceptHandler = new AcceptHandler();
                        acceptHandler.connSocketChannel(selectionKey);

                    }else if(selectionKey.isReadable()){
                        //读取数据
                        HttpRequest httpRequest = new HttpRequest();
                        httpRequest.parse(selectionKey);
                        System.out.println("http请求的数据为 ---->" + httpRequest);

                        if(httpRequest.getRequestURI() == null || "".equals(httpRequest.getRequestURI())){
                            selectionKey.channel();
                            continue;
                        }
                        System.out.println("...数据解析完毕,准备响应数据....");

                        //响应数据
                        HttpResponse httpResponse = new HttpResponse();
                        httpResponse.setHttpRequest(httpRequest);
                        httpResponse.sendStaticResource(selectionKey);
                    }
                    //任务处理完毕以后,将SelectionKey从集合中移除
                    iterator.remove();
                }
            }
        }
    }
}
/**
 * 用来封装请求数据的类
 */
public class HttpRequest {
    private String method; //请求方式
    private String requestURI; //请求的uri
    private String version;   //http的协议版本

    private HashMap<String,String> hm = new HashMap<>();//所有的请求头

    //parse --- 获取请求数据 并解析
    public void parse(SelectionKey selectionKey){
        try {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

            StringBuilder sb = new StringBuilder();
            //创建一个缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int len;
            //循环读取
            while((len = socketChannel.read(byteBuffer)) > 0){
                byteBuffer.flip();
                sb.append(new String(byteBuffer.array(),0,len));
                //System.out.println(new String(byteBuffer.array(),0,len));
                byteBuffer.clear();
            }
            //System.out.println(sb);
            parseHttpRequest(sb);

        } catch (IOException e) {
 

以上是关于网络编程--05 HTTP服务器的主要内容,如果未能解决你的问题,请参考以下文章

片段中的 super.onCreateView

Android片段z-index覆盖

代码片段 - Golang 实现简单的 Web 服务器

Flutter 报错 DioError [DioErrorType.DEFAULT]: Bad state: Insecure HTTP is not allowed by platform(代码片段

c# https请求忽略证书验证_各种编程语言忽略http的SSL证书认证

Android片段布局完成膨胀