IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 计算机网络(应用层:==>HTTP协议和Socket编程) -> 正文阅读

[网络协议]计算机网络(应用层:==>HTTP协议和Socket编程)

1.HTTP协议简介

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。

1.1HTTP协议概述

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(originserver)。通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的连接。HTTP服务
器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如"HTTP/1.1200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。

1.2HTTP工作原理

1:首先是客户端连接到Web服务器:
浏览器对url进行域名解析拿到IP地址,然后就封装HTTP请求报文。
2:发送HTTP请求:
操作系统协议栈将应用层的HTTP报文,基于TCP协议再次封装,然后和服务器建立一个TCP套接字连接并且向Web服务器发送一个请求报文,报文由请求行、请求头、空行和请求数据组成。
3:服务器接受请求并且返回HTTP响应:
Web服务器解析请求,定位资源,最后将服务资源输出到TCP套接字中,由客户端浏览器读取。一个响应报文由响应行、响应头、空行、响应数据组成。
4:释放连接的TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求
5:客户端浏览器解析响应数据
浏览器会根据响应头中的数据,对应的就知道该怎样处理数据,若是HTML就渲染成网页,如果是文本就直接呈现在网页上。
图解其过程:
在这里插入图片描述

1.3HTTP报文格式

1.3.1请求报文

在这里插入图片描述

1.3.2响应报文

在这里插入图片描述

2.套接字(socket)

2.1Socket概览

在这里插入图片描述

2.2流socket概述

在网络通讯中,两个程序通过一个双端的通信连接管道实现数据交换,我们称这个管道叫Socket;一般是客户端创建socket连接服务端,与之相对应的服务端会建立一个服务端的socket服务进程等待客户端的socket连接!

图解过程:
在这里插入图片描述

2.3流socket原理

通过互联网进行通信,至少需要一对套接字,其中服务端的称之为ServerSocket,客户端的称为Socket,本质上讲服务端的套接字就是为了处理客户端的套接字的,每一个向服务端发起socket连接的都能称之为客户端。

通信过程图:
在这里插入图片描述

2.4流socket(网络编程)

了解流Socket以后,来简单的基于TCP来实现服务端与客户端的通信:


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * 服务端
 */
public class Test0721_1 {
    private  static  String name="服务端";
    public static void main(String[] args) throws IOException {
        //向系统注册一个套接字服务
        ServerSocket server=new ServerSocket(8888);
        Scanner scanner=new Scanner(System.in);
        //套接字阻塞监听在8888端口,等待处理来连接的套接字
        Socket accept = server.accept();
        //拿到输入流,就可以读取到套接字里面的内容
        InputStream in = accept.getInputStream();
        //拿到输出流,就可以往套接字里面写入内容
        OutputStream os = accept.getOutputStream();
        while (true){
            int len;
            byte[] data=new byte[1024];
            //服务端读取套接字里面的内容
            while ((len=in.read(data))!=-1){
                System.out.print("客户端:");
                System.out.println(new String(data,0,len));
                if(len<1024){ //读取的长度如果少于自定义的长度,说明一次读取完毕,跳出本次读取
                    break;
                }
            }
            System.out.print(name+":");
            //服务端往套接字里面写入内容
            os.write(scanner.next().getBytes());
        }
    }
}

===================================================================

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端
 */
public class Test0721_2 {
    private static String name="客户端";
    public static void main(String[] args) throws IOException {
        //创建一个套接字
        Socket socket=new Socket();
        Scanner scanner=new Scanner(System.in);
        //绑定连接的主机和端口
        socket.connect(new InetSocketAddress(8888));
        //拿到输出流,就可以往套接字里面写入内容
        OutputStream os = socket.getOutputStream();
        //拿到输入流,就可以读取套接字里面的内容
        InputStream in = socket.getInputStream();
        while (true){
            byte[] data=new byte[1024];
            int len;
            System.out.print(name+":");
            //客户端往套接字里面写入内容
            os.write(scanner.next().getBytes());
            while ((len=in.read(data))!=-1){ //客户端从套接字里面读取内容
                String s=new String(data,0,len);
                System.out.println("服务端:"+s);
                if(s.equals("bye")){ //模拟收到服务端关闭连接的信号
                    //关闭所有的资源
                    socket.shutdownInput();
                    socket.shutdownOutput();
                    socket.close();
                    os.close();
                    in.close();
                    break;
//                    System.exit(0);
                }
                if(len<1024){
                    break;
                }
            }
            if(socket.isClosed()){
                break;
            }

        }

    }
}


运行结果:
在这里插入图片描述

2.5数据报套接字(DatagramSocket)

在网络通讯中,数据包套接字,只要知道目的主机的IP地址与端口,即可进行通信,就是通过发送数据包给对方,接受数据包即可!

2.6数据报套接字(DatagramSocket)原理

在这里插入图片描述

2.7数据报套接字(网络编程)

了解数据报套接字以后,来简单的基于UDP来实现两台主角的通信:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class UdpSeverA {
    public static void main(String[] args) throws IOException {
        //创建一个数据报套接字
        DatagramSocket socket=new DatagramSocket(6666);
        //获得从键盘输入的流对象
       BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        while (true){
            byte[] data=new byte[1024];
            //准备一个数据包,也就是发送数据的载体
            DatagramPacket packet=new DatagramPacket(data,0,data.length);
            //阻塞的接收数据包
            socket.receive(packet);
            String msg=new String(packet.getData(),0,packet.getData().length);
            System.out.println(msg);
            if(msg.trim().equals("bye")){
                socket.close();
                break;
            }
            String msgsend=reader.readLine();
            byte[] sendData=msgsend.getBytes();
            //目的IP地址
            InetAddress localhost = InetAddress.getByName("localhost");
            //目的端口
            int port=5555;
            //准备数据包(发送数据包要绑定对方主机IP和端口)
            DatagramPacket packet1=new DatagramPacket(sendData,0,sendData.length,localhost,port);
            socket.send(packet1);

        }
        reader.close();
    }
}

===================================================================

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpServerB {
    private static String flag=null;
    public static void main(String[] args) throws Exception {
        //建立socket连接
        DatagramSocket socket=new DatagramSocket(5555);
        //准备从控制台读取数据
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            String data = reader.readLine();
            byte[] bytes = data.getBytes();
            //要发送的端口好
            int port = 6666;
            //要发送的主机IP地址
            InetAddress localhost = InetAddress.getByName("localhost");

            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, localhost, port);
            //发送数据
            socket.send(packet);
            if (data.equals("bye")) {
                break;
            }
            //为了也能接收到数据包和发送数据包同时进行,需要另外开辟一个线程来接收数据包
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    byte[] res = new byte[1024];
                    DatagramPacket packet1 = new DatagramPacket(res, 0, res.length);
                    try {
                        socket.receive(packet1);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    String msg = new String(packet1.getData(), 0, packet1.getData().length);
                    System.out.println(msg);
                    if ("bye".equals(msg)) {
                        flag=msg;
                        socket.close();

                    }
                }
            });
            if("bye".equals(flag)){
                //结束收数据线程
                t.interrupt();
            }
            t.start();
        }
        reader.close();
    }
}

运行结果:
在这里插入图片描述

2.8补充URL网络下载

通过网络连接到网络上的资源,根据URL可以定位某个资源再根据流,就可以把资源输出到我们想要的地方。

简单编写一个程序,下载网络上的资源:


import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class URLTest {
    public static void main(String[] args) throws Exception {
        //下载地址
        URL url = new URL("https://9uu33.com/20210311/bf6f2ac51d8f895372a48ed527d5d59a.mp4");
        //连接到这个资源
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
       //通过流来连接对象
        InputStream inputStream= urlConnection.getInputStream();

        FileOutputStream fos = new FileOutputStream("m.mp4");
        byte[] buffer = new byte[1024];
        int len;
        while ((len=inputStream.read(buffer))!=-1){
            fos.write(buffer,0, len);//写出数据
        }
        fos.close();
        inputStream.close();
        urlConnection.disconnect();
    }
}

3.手动实现一个简单的HTTP服务器

3.1 总体思路与过程如下:

在这里插入图片描述

3.2代码实现


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务器启动类
 */

public class Http {
    public static void main(String[] args) throws IOException {
        // 创建服务器
        ServerSocket server = new ServerSocket();
        // 绑定端口
        server.bind(new InetSocketAddress(6888));
        System.out.println("server is running! listening on port 6888!");
        // 开始监听,阻塞的方法
        while (true){
            Socket accept = server.accept();
            System.out.println("A person"+accept.getRemoteSocketAddress().toString()+" is coming!");

            // 使用线程池,提升性能
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            //来一个请求,丢到线程池里面去
            executorService.submit(new MyTask(accept));
        }
    }
}


===================================================================


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * 处理请求和响应的任务类
 */
public class MyTask implements Runnable {

    private Socket accept;

    public MyTask(Socket accept){
        this.accept = accept;
    }

    @Override
    public void run() {
        InputStream in = null;
        OutputStream out = null;

        try {
            StringBuilder sb = new StringBuilder();
            //拿到输入流,就可以读取套接字里面请求的内容
            in = accept.getInputStream();
            int len;
            byte[] buf = new byte[512];
            while ((len = in.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
                if (len < buf.length) {
                    accept.shutdownInput();
                }
            }
            // 构建一个请求对象
            Request request = Request.buildRequest(sb.toString());
            // 拿到输出流
            out = accept.getOutputStream();
            // 构建一个响应
            Response response = new Response();
            //根据请求的URL路径定位到资源后,把资源放到响应数据里面
            response.setData(HttpUtils.getPage(request.getUrl()));
            //把输出流放到响应对象里面去
            response.write(out);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (accept != null){
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

===================================================================
/**
 * 帮助定位资源的工具类
 */
public class HttpUtils {

    // 使用流获得页面的字符串
    public static String getPage(String url){
        //资源存放的文件
//        String rootPath="/javaDailyProctice/Test0720/MyTomcat";
        StringBuilder sb = new StringBuilder();
        String result = null;
        try {
            if ("".equals(url) || "/".equals(url) || url == null){
                url = "index.html";
            }
            // F:\桌面文件\Tomcat
            // 寻找觉对的父路劲  ==>"/D:/JavaPrectice/out/production/JavaPrectice/" ==>其实就是编译后的根路径的根文件而已
            String path = HttpUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
            path = path.substring(0,path.lastIndexOf("/")) + "/pages/";
            url = path + url;
            boolean exists = new File(url).exists();
            if (!exists){
                url = path +"404.html";
            }

            InputStream resource = new FileInputStream(url);
            byte[] buf = new byte[1024];
            int len;
            while ((len = resource.read(buf)) != -1){
                sb.append(new String(buf,0,len));
            }
             result = sb.toString().replace("{{name}}", "涉外—攀枝花");
        } catch (Exception e){
            e.printStackTrace();
        }
        return result ;
    }

}

===================================================================
import java.util.HashMap;
import java.util.Map;


/**
 * 封装请求类
 */
public class Request {
    //请求类型(get/post)
    private String type;
    //请求的URL资源路径
    private String url;
    //请求的协议版本
    private String protocol;
    //文本类型
    private String contentType;
    //存放请求头中的键值对数据
    private Map<String,String> headers = new HashMap<>(8);
    //请求数据
    private Map<String,String> attributes = new HashMap<>(8);

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public Map<String, String> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, String> attributes) {
        this.attributes = attributes;
    }

 }

    // 通过请求的报文字符串构建一个请求对象
    public static Request buildRequest(String requestStr){
        Request request = new Request();
        // 从请求中切分出请求行和请求头
        String[] split = requestStr.split("\r\n\r\n");
        //继续从中切分出请求头 lineAndHeader ==>请求行、[请求头的键值对]
        String[] lineAndHeader = split[0].split("\r\n");
        //继续切分出请求行,此时的lines==> 请求方法、URL、协议版本、
        String[] lines = lineAndHeader[0].split(" ");
        request.setType(lines[0]);
        request.setUrl(lines[1]);
        request.setProtocol(lines[2]);
        //必须从下标为1开始遍历,因为下标为0对应的是整个请求行的数据
        for (int i = 1; i < lineAndHeader.length; i++) { //遍历请求头
            String[] header = lineAndHeader[i].split(": ");
            request.getHeaders().put(header[0].trim().toLowerCase(),header[1].trim());
        }

        request.setContentType(request.getHeaders().get("content-type"));

//        // 处理请求体
//        if (split.length == 2){
//           暂且不做处理,因为简单发起HTTP请求,我们只需要截取到URL即可,
//           并不需要和后端进行数据交互,所以不处理
//        }

        return request;
    }
}
===================================================================

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 构造响应的类
 */

public class Response {
    //版本协议
    private String protocol = "HTTP/1.1";
    //状态码
    private Integer code = 200;
    //状态码描述
    private String msg = "OK";
    //响应的格式
    private String ContentType = "text/html;charset=utf-8";
    //响应报文的长度
    private String ContentLength;

    private Map<String,String > headers = new HashMap(){{
        put("content-type",ContentType);
    }};
    private String data;


    public Response(){}

    public Response(String protocol, Integer code, String msg) {
        this.protocol = protocol;
        this.code = code;
        this.msg = msg;
    }


    /**
     * 构建响应
     * @return
     */
    public String buildResponse(){
        StringBuilder sb = new StringBuilder();
        //根据响应的数据格式,构造响应
        sb.append(this.getProtocol()).append(" ")
                .append(this.getCode()).append(" ")
                .append(this.getMsg()).append("\r\n");
        for (Map.Entry<String,String> entry : this.getHeaders().entrySet()){
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
        }
        sb.append("\r\n").append(this.getData());
        return sb.toString();
    }

    /**
     * 输出响应
     * @param os
     */
    public void write(OutputStream os){
        try {
            //把响应数据输出到流套接字里面去
            os.write(buildResponse().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(os != null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 加一个响应头
     * @param key
     * @param value
     */
    public void addHeader(String key,String value){
        this.getHeaders().put(key,value);
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
        this.setContentLength(buildResponse().getBytes().length+"");
    }

    public String getContentType() {
        return this.getHeaders().get("content-type");
    }

    public void setContentType(String contentType) {
        this.getHeaders().put("content-type",contentType);
    }

    public String getContentLength() {
        return  this.getHeaders().get("content-length");
    }

    public void setContentLength(String contentLength) {
        this.getHeaders().put("content-length",this.data.getBytes().length + "");
    }
}

3.3打包验证

代码写好后,打成一个Jar包,然后去浏览器进行验证,过程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
将Jar包复制到非中文路径下,然后在编写启动脚本start.bat,内容如下:
在这里插入图片描述
最后,启动后,使用浏览器进行验证:
在这里插入图片描述
在这里插入图片描述

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:51:07  更:2021-07-24 11:51:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:37:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码