美文网首页
Java 网络

Java 网络

作者: 小石头呢 | 来源:发表于2019-08-29 17:23 被阅读0次

    网络

    • Socket 套接字

    • Socket 编程写数据到输出流:PrintWriter

    • Socket 模仿向服务端上传文件

    • Socket 实现单对单的聊天

    • Socket 实现多对多的聊天

    • Http协议

    • Get请求 VS Post请求

    • URL 简介

    • URL 发送Get请求

    • URL 发送POST请求

    • 参考文章

    一.Socket 套接字

    Socket底层学习---C语言


    1. java.net 包中提供了两种常见的网络协议的支持:

    • TCPTCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP

    • UDPUDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。


    2. 套接字实现网络通信:

    • 套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

    • 当连接建立时,服务器会创建一个Socket对象。客户端和服务器现在可以通过对Socket对象的写入和读取来进行通信。

    • java.net.Socket类代表一个套接字,并且 java.net.ServerSocket类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。


    3. 两台计算机之间使用套接字建立TCP连接步骤:

    • 服务器实例化一个ServerSocket对象,表示通过服务器上的端口通信。

    • 服务器调用ServerSocket类的accept()方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

    • 服务器正在等待时,一个客户端实例化一个Socket对象,指定服务器名称和端口号来请求连接。

    • Socket类的构造函数试图将客户端连接到指定的服务器和端口号。如果想建立通信,则在客户端创建一个能够与服务器进行通信的Socket对象。

    • 在服务器端,accept()方法返回服务器上一个新的socket引用,这个新的socket表示连接到服务端的一个socket

    • 连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。


    4. ServerSocket 类的方法

    方法 描述
    public ServerSocket(int port) throws IOException 构造方法:创建绑定到特定端口的服务器套接字。
    public int getLocalPort() 返回此套接字在其上侦听的端口
    public Socket accept() throws IOException 侦听并接受到此服务器套接字的连接

    5. Socket 类的方法

    方法 描述
    public Socket(String host, int port) throws UnknownHostException, IOException 构造方法:创建一个流套接字并将其连接到指定主机上的指定端口号
    public int getPort() 返回此套接字连接到的远程端口
    public int getLocalPort() 返回此套接字绑定到的本地端口
    public InputStream getInputStream() throws IOException 返回此套接字的输入流
    public OutputStream getOutputStream() throws IOException 返回此套接字的输出流
    public void shutdownOutput() throws IOException 只关闭OutputStream而在关闭的同时,并不关闭网络连接
    public void shutdownInput() throws IOException 只关闭InputStream,而在关闭的同时,并不关闭网络连接
    public void close() throws IOException 关闭此套接字

    6. 客户端和服务器端数据传输

    客户端和服务器端读取数据:

    • BuffereReader->InputStreamReader->socket.getInputStream()
    • 客户端读取服务器端的数据,socket就是创建用于通信的socket
    • 服务器端读取客户端的数据,socket就是连接客户端后获得的通信的socket
    • 客户端、服务器端读取终端数据,BuffereReader->InputStreamReader->System.in

    客户端和服务器端输出数据:

    • PrintStream->socket.getOutputStream
    • 客户端输出数据到服务器端,socket就是创建用于通信的socket
    • 服务器端输出数据到客户端,socket就是连接客户端后获得的通信的socket

    二.Socket编程写数据到输出流:PrintStream

    1. BufferedReader 和 BufferedWriter 方式

    //模拟客户端
    class Client{
       public static void main(String[] args) throws IOException {
           //创建用于通信的socket:
    
           // 1.指明和谁通信 ip地址 端口号
           Socket socket = new Socket("127.0.0.1",8989);
    
           //接受服务端的数据
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
           String line;
           while((line = bufferedReader.readLine()) != null){
               System.out.println(line);
           }
    
           //向服务器端发送数据
           BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
           bufferedWriter.write("请求数据");
           bufferedWriter.newLine();
           bufferedWriter.flush();
           socket.shutdownOutput();//输出完毕,关掉输出流,防止readLine一直等待读
       }
    }
    
    //模拟服务器端
    class Server{
       public static void main(String[] args) throws IOException {
           //1.创建服务器端的ServerSocket
           ServerSocket socket = new ServerSocket(8989);
    
           //2.获取连接的客户端的socket
           Socket cliensocket = socket.accept();
    
           //3.向客户端发送数据
           BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(cliensocket.getOutputStream()));
           bufferedWriter.write("接连服务器成功");
           bufferedWriter.newLine();
           bufferedWriter.flush();
           cliensocket.shutdownOutput();//输出完毕,关掉输出流,防止readLine一直等待读
    
           //4.接受客户端发送的数据
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cliensocket.getInputStream()));
           String line;
           while((line = bufferedReader.readLine()) != null){
               System.out.println(line);
           }
       }
    }
    

    2. BufferedReader 和 PrintStream 方式

    //模拟客户端
    class Client{
       public static void main(String[] args) throws IOException {
           //创建用于通信的socket:
    
           // 1.指明和谁通信 ip地址 端口号
           Socket socket = new Socket("127.0.0.1",8989);
    
           //接受服务端的数据
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
           String line;
           while((line = bufferedReader.readLine()) != null){
               System.out.println(line);
           }
    
           //向服务器端发送数据
           PrintStream printStream = new PrintStream(cliensocket.getOutputStream());
           printStream.println("连接服务器成功");
           socket.shutdownOutput();//输出完毕,关掉输出流,防止readLine一直等待读
       }
    }
    
    //模拟服务器端
    class Server{
       public static void main(String[] args) throws IOException {
           //1.创建服务器端的ServerSocket
           ServerSocket socket = new ServerSocket(8989);
    
           //2.获取连接的客户端的socket
           Socket cliensocket = socket.accept();
    
           //3.向客户端发送数据
           PrintStream printStream = new PrintStream(cliensocket.getOutputStream());
           printStream.println("连接服务器成功");
           cliensocket.shutdownOutput();//输出完毕,关掉输出流,防止readLine一直等待读
    
           //4.接受客户端发送的数据
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cliensocket.getInputStream()));
           String line;
           while((line = bufferedReader.readLine()) != null){
               System.out.println(line);
           }
       }
    }
    

    3. BufferedWriter 和 PrintStream 的详细使用:

    • BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符。通过write()方法可以将获取到的字符输出,然后通过newLine()进行换行操作或者加上\nBufferedWriter中的字符流必须通过调用flush方法才能将其刷出去。BufferedWriter只能对字符流进行操作,如果要对字节流操作,则使用BufferedInputStream

    • PrintStream:向文本输出流打印对象的格式化表示形式。PrintStream相对于BufferedWriter的好处在于,PrintStream中的字符流在遇到\n自动刷新,或者调用println也可以自动刷新,PrintStream只能对字节流进行操作,如果要对字节流操作,则使用PrintWriter。但是PrintWriter想要自动刷新需要在构造方法中设置相关参数,或者手动调用flush刷新。

    • 服务器接收不到客户端的信息:客户端中,write()的时候如果没有发送换行标识符,那么服务器在接收的时候用readLine()就读取不出来。因为readLine()是读取一行,没遇到换行就读取不出来。

    • Socket编程中,尽量用PrintStream取代BufferedWrite

    三.Socket 模仿向服务端上传文件

    • 客户端和服务端连接成功后

    • 客户端将文件数据写入到内存中后,将文件的内容一点点传给服务器

    • 服务端在内存中接收到文件数据后,将文件数据一点点写出到服务器端

    class Client{
       public static void main(String[] args) throws IOException {
           //连接服务器 获取socket
           Socket socket = new Socket("127.0.0.1", 8686);
    
           //创建服务端对应的输入流用于接受服务器端发来的数据
           BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
           System.out.println(bufferedReader.readLine());
    
           //向服务端发送文件(图片)
           //1.将文件写入到内存
           String path = "C:\\Users\\a2867\\Desktop\\壁纸\\1.jpg";
           FileInputStream fis = new FileInputStream(path);
           //2.将内容输出到服务器端
           //将文件的内容一点点传输给服务器
           BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
           byte[] buf = new byte[1024];
           int len;
           while((len = fis.read(buf)) != -1){
               bos.write(buf,0,len);
           }
           socket.shutdownOutput();
       }
    }
    
    class Server{
       public static void main(String[] args) throws IOException {
           //创建服务器端的ServerSocket
           ServerSocket serverSocket = new ServerSocket(8686);
    
           //监听客户端连接
           //当有客户端来连接这个服务器,就可以得到对应的socket
           //当没有客户端来连接,服务器一直在这里等待
           Socket accept = serverSocket.accept();
    
           //创建客户端对应的输出流 用于向这个客户端发送数据
           PrintStream printStream = new PrintStream(accept.getOutputStream());
           printStream.println("连接服务器成功");
           accept.shutdownOutput();
    
           //接受客户端传递过来的图片
           BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
           //文件对应的输出流
           String path = "C:\\Users\\a2867\\Desktop\\love.jpg";
           FileOutputStream fos = new FileOutputStream(path);
    
           byte[] buf = new byte[1024];
           int len;
           while((len = bis.read(buf)) != -1){
               fos.write(buf,0,len);
           }
       }
    }
    

    四. Socket实现单对单的聊天


    客户端:
    • 主线程:接收终端输入 将终端输入发送给服务器端
    • 子线程:接受服务端发过来的数据

    服务端:

    • 主线程:接收终端输入 将终端输入发送给客户端
    • 子线程:接受服务端发过来的数据
    //客户端
    class Client{
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket("127.0.0.1",8989);
    
            //用一个子线程处理服务器端数据
            new AcceptData(socket).start();
    
            //终端输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            //socket输出流
            PrintStream ps = new PrintStream(socket.getOutputStream());
            //读取终端的输入 将输入输出给服务器端
            String line;
            while ((line = br.readLine()) != null){
                ps.println(line);
            }
        }
    }
    
    //Server
    class Server{
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = new ServerSocket(8989);
    
            //获取连接的客户端的socket
            Socket accept = serverSocket.accept();
    
            //创建子线程 处理客户端输入信息
            new AcceptData(accept).start();
    
            //终端输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            //链接的socket输出流
            PrintStream ps = new PrintStream(accept.getOutputStream());
            //读取终端的输入 将输入输出给客户端
            String line;
            while ((line = br.readLine()) != null){
                ps.println(line);
            }
        }
    }
    
    //线程:处理对方传输的数据
    class AcceptData extends Thread{
        private Socket mSocket;
    
        //保存Socket对象
        public AcceptData(Socket socket){
            mSocket = socket;
        }
    
        @Override
        public void run() {
            BufferedReader br = null;
            try {
                //获取服务器端的输入流对象
                br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
    
                //读取数据
                String line ;
                while ((line = br.readLine()) != null){
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("网络出错");
                System.exit(-1);
            }finally {
                try {
                    if (br != null){
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                if (mSocket != null) {
                    try {
                        mSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    

    五. Socket实现多对多聊天

    实现功能:登录,群聊,私聊

    • 需要多个客户端连接服务器端,客户端发送消息会被服务器端接受。服务器端分析客户端发送过来的消息,做出相应的抉择私聊还是群发。

    • 多个客户端连接服务器端就需要服务器端可以接受发起连接的客户端并保存。

    • 客户端发送的消息要经过统一规范发送到服务器端,服务器端才能分析出来。

    客户端的需求在发送的字符里面体现,统一的规则如下:

    • 登录:u+登录名u+

    • 私聊:p+私聊的登录名&发送的内容p+

    • 群聊:a+群聊内容a+

    /**
     * 管理所有的常量变量
     */
    public interface ChatProtocol {
        //登录
        String LOGIN_FLAG = "u+";
        //私聊
        String PRIVATE_FLAG = "p+";
        //群聊
        String PUBLIC_FLAG = "a+";
    
        //分隔符
        String SPLITE_FLAG = "&";
    
        //成功的状态
        String SUCCESS = "登陆成功";
        //失败的状态
        String FAILURE = "已经登录了";
    }
    
    /**
     * 管理所有的登录用户(Map)
     * 判断某个用户是否已经登录
     */
    public class UserManager {
    
        //保存所有用户信息
        private  Map<String, Socket> users = new HashMap<>();
    
        /**
         * 判断用户是否已经登录
         * @param name 用户名
         * @return 是否登录
         */
        public synchronized  boolean isLogin(String name){
            //遍历key数组
            for (String userName : users.keySet()) {
                if (userName.equals(name)){
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 保存用户信息
         * @param name 用户名
         * @param socket 用户socket
         */
        public synchronized void saveUserMessage(String name,Socket socket){
            users.put(name,socket);
        }
    
        /**
         * 通过用户找到对应的Socket
         * @param name 用户名
         * @return Socket对象
         */
        public synchronized  Socket getSocketByName(String name){
            //默认连接
            return users.get(name);
        }
    
        /**
         * 通过Socket找到对应的用户
         * @param socket socket
         * @return 用户名
         */
        public synchronized  String getNameBySocket(Socket socket){
            //默认连接
            for (String key : users.keySet()) {
                //取出这个key对应的socket
                if(socket == users.get(key)){
                    return key;
                }
            }
            return null;
        }
    
        /**
         * 获取所有用户的socket
         * @return 所有socket的集合
         */
        public synchronized  Collection<Socket> getAllUsers(){
            return users.values();
        }
    
    }
    
    /**
     * 服务端
     */
    public class Server {
        //用户管理者
        public static UserManager manager = new UserManager();
    
        public static void main(String[] args) {
            //创建一个ServerSocket
            try(ServerSocket serverSocket = new ServerSocket(8888)) {
                //监听所有来链接的客户端
                while(true){
                    Socket accept = serverSocket.accept();
    
                    //让子线程处理这个Socket
                    new ServerThread(accept).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    class ServerThread extends Thread{
        private Socket mSocket;
    
        public ServerThread(Socket socket){
            mSocket = socket;
        }
    
        @Override
        public void run() {
            //登录 私聊 群聊 发文件
            BufferedReader br = null;
            PrintStream ps = null;
            try {
                //得到对应的输入流对象
                br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
                //得到对应的输出流对象
                ps = new PrintStream(mSocket.getOutputStream());
    
                String line = null;
                while((line = br.readLine()) != null){
                    //登录 u+...u+
                    if (line.startsWith(ChatProtocol.LOGIN_FLAG) && line.endsWith(ChatProtocol.LOGIN_FLAG)){
                        //分割
                        //String[] items = line.substring(2).split(ChatProtocol.LOGIN_FLAG);
                        //String name = items[0];
                        //u+jacku+ 2 6
                        //System.out.println(line);
                        String name = line.substring(2,line.length()-2);
                        //System.out.println(name);
                        if (Server.manager.isLogin(name)){
                            //登陆过了
                            //返回结果给客户端
                            ps.println(ChatProtocol.FAILURE);
                        }else {
                            //没有登录
                            ps.println(ChatProtocol.SUCCESS);
                            //保存当前登录的用户信息
                            Server.manager.saveUserMessage(name,mSocket);
                            System.out.println(name+"连接服务器了");
                        }
                    }else if (line.startsWith(ChatProtocol.PRIVATE_FLAG) && line.endsWith(ChatProtocol.PRIVATE_FLAG)){
                        //p+jack&hellop+
                        //获取信息
                        String msg = line.substring(2,line.length()-2);
                        //用户名
                        String[] items = msg.split(ChatProtocol.SPLITE_FLAG);
                        String name = items[0];
                        String content = items[1];
                        //通过用户名找到对应的socket
                        Socket socketName = Server.manager.getSocketByName(name);
                        PrintStream psPri = new PrintStream(socketName.getOutputStream());
    
                        //获取当前用户的名称
                        String currentName = Server.manager.getNameBySocket(mSocket);
    
                        //发送私聊消息
                        psPri.println(currentName+"向你发来私聊"+content);
                    }else if (line.startsWith(ChatProtocol.PUBLIC_FLAG) && line.endsWith(ChatProtocol.PUBLIC_FLAG)){
                        //群聊
                        //获取信息
                        String msg = line.substring(2,line.length()-2);
    
                        //获取用户名称
                        String currentName = Server.manager.getNameBySocket(mSocket);
    
                        //遍历所有用户信息
                        Collection<Socket> sockets = Server.manager.getAllUsers();
                        for (Socket socket : sockets) {
                            PrintStream temp = new PrintStream(socket.getOutputStream());
                            temp.println(currentName+":"+msg);
                            //temp.close();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 用户 可以复制多个该文件用作测试 模拟多个客户端
     */
    public class Client {
        public static void main(String[] args) {
            //接受终端输入信息
            BufferedReader br = null;
            PrintStream ps = null;
            BufferedReader brr = null;
    
            //创建Socket 连接服务器
            try(Socket socket = new Socket("127.0.0.1",8888)) {
                //登录
                while (true){
                    //接受终端输入流
                    br = new BufferedReader(new InputStreamReader(System.in));
                    //发给服务器输出流
                    ps = new PrintStream(socket.getOutputStream());
                    //接受服务器输入流
                    brr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    //接受终端输入信息
                    String line = JOptionPane.showInputDialog("请输入用户名:");
                    //拼接登录格式
                    String loginString = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
                    //发送给服务器端
    
                    ps.println(loginString);
                    //接受服务器端返回的结果
    
                    String result = brr.readLine();
                    if (result.equals(ChatProtocol.SUCCESS)){
                        System.out.println("登陆成功");
                        break;
                    }else {
                        System.out.println("用户名已存在,请重新登录");
                    }
                }
    
                //登陆成功
    
                //开启线程 处理服务器端的输入
                new ClientThread(socket).start();
    
                //接受终端输入发送给服务器端
                //从终端输入信息
                String line = null;
                while((line = br.readLine()) != null){
                    //发送给服务器
                    ps.println(line);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    class ClientThread extends Thread{
        private Socket mSocket;
    
        public ClientThread(Socket socket){
            mSocket = socket;
        }
    
        @Override
        public void run() {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
                String line = null;
                while((line = br.readLine()) != null){
                    System.out.println(line);
                }
            } catch (IOException e) {
                System.out.println("网络出错");
            }finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } 
        }
    }
    

    六. Http协议

    Http简介

    • HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

    • HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

    • 一个完整的http请求需要经历两个过程:客户端发送请求到服务器,然后服务器将结果返回给客户端

    1. 客户端->服务端

    客户端向服务器发送请求主要包含以下信息:请求的URL地址、请求头以及可选的请求体

    请求URL(Request URL)

    • 上图中的Request URL就是请求的URL地址,即https://www.baidu.com,该URL没有附加其他的参数。

    • 其实可以通过 ? 和 & 符号向URL地址后面追加一系列的键值对参数,比如地址https://www.baidu.com/s?ie=utf-8&wd=Android,该URL包含两个键值对,ie=utf-8,以及wd=Android,ie和wd是key,utf-8和Android分别是其对应的value,服务端可以获取ie和wd所对应的value的值。

    • 由此我们可以看出,URL可以携带额外的数据信息。一般情况下,URL的长度不能超过2048个字符,即2KB,超过此限制的话服务器可能就不识别。

    请求头(Request Headers)

    • 上图中Request Headers部分就是请求头,请求头其实也是一些键值对,不过这些键值通常都是W3C定义了的一些标准的Http请求头的名称,请求头包含了客户端想告诉服务端的一些元数据信息。

    • 注意是元数据,而不是数据,比如请求头User-Agent会告诉服务器这条请求来自于什么浏览器,再比如请求头Accept-Encoding会告诉服务器客户端支持的压缩格式。除了这些标准的请求头,我们还可以添加自定义的请求头。

    请求体(Request Body)

    • 之前我们提到,URL的最大长度就是2048个字符,如果我们发送的数据很大,超过了2KB怎么办?我们可以将很大的数据放到请求体中,GET请求不支持请求体,只有POST请求才能设置请求体。

    • 请求体中可以放置任意的字节流,从而可以很方便地发送任意格式的数据,服务端只需要读取该输入流即可。

    分析图

    http请求分析

    2. 服务器->客户端

    服务器接收到客户端发来的请求后,会进行相应的处理,并向客户端输出信息,输出的信息包括响应头和响应体。


    响应头

    • 响应头也是一些键值对,包含了服务器想要告诉客户端的一些元数据信息,注意不是数据,是元数据。

    • 比如通过响应头Content-Encoding告诉客户端服务器所采用的压缩格式,响应头Content-Type告诉客户端响应体是什么格式的数据,再比如服务端可以通过多个Set-Cookie响应头向客户端写入多条Cookie信息,等等。

    • 刚刚提到的几个请求头都是W3C规定的标准的请求头名称,我们也可以在服务端向客户端写入自定义的响应头。

    响应体

    • 响应体是服务端向客户端传输的实际的数据信息,本质就是一堆字节流,可以表示文本,也可以表示图片或者其他格式的信息。

    分析图

    http响应分析

    七.Get请求 VS Post请求

    Http协议支持的操作有GETPOSTHEADPUTTRACEOPTIONSDELETE,其中最最常用的还是GET和POST操作。

    1. GET请求:

    • GET请求可以被缓存。

    • 当发送键值对信息时,可以在URL上面直接追加键值对参数。当用GET请求发送键值对时,键值对会随着URL一起发送的。

    • 由于GET请求发送的键值对时随着URL一起发送的,所以一旦该URL被黑客截获,那么就能看到发送的键值对信息,所以GET请求的安全性很低,不能用GET请求发送敏感的信息(比如用户名密码)。

    • 由于URL不能超过2048个字符,所以GET请求发送数据是有长度限制的。

    • 由于GET请求较低的安全性,我们不应该用GET请求去执行增加、删除、修改等的操作,应该只用它获取数据。

    2. POST请求:

    • POST请求从不会被缓存。

    • POST请求的URL中追加键值对参数,不过这些键值对参数不是随着URL发送的,而是被放入到请求体中发送的,这样安全性稍微好一些。

    • 应该用POST请求发送敏感信息,而不是用GET。

    • 由于可以在请求体中发送任意的数据,所以理论上POST请求不存在发送数据大小的限制。

    • 当执行增减、删除、修改等操作时,应该使用POST请求,而不应该使用GET请求。

    八.URL

    1.URL简介

    URL(Uniform Resource Locator)中文名为统一资源定位符,有时也被俗称为网页地址,表示为互联网上的资源。

    格式:protocol://host:port/path?variable=value&variable=value...

    分析的例子:http://127.0.0.1/login.php?user_name=jack&user_password=123

    • 协议为(protocol):如http
    • 主机为(host:port):127.0.0.1
    • 端口号为(port): 80 ,上面的URL并未指定端口,因为 HTTP 协议默认的端口号为 80
    • 文件路径为(path):/login.php
    • 请求参数(variable=value):user_name=jack&user_password=123

    2.URL类的方法

    方法 描述
    public URL(String url) throws MalformedURLException 构造方法:通过给定的URL字符串创建URL
    public String getPath() 返回URL路径部分
    public String getQuery() 返回URL查询部分
    public int getPort() 返回URL端口部分
    public String getHost() 返回URL的主机
    public String getFile() 返回URL文件名部分
    public URLConnection openConnection() throws IOException 打开一个URL连接,并运行客户端访问资源

    3.URLConnections 类的方法

    方法 描述
    Object getContent() 检索URL链接内容
    String getContentEncoding() 返回头部 content-encoding 字段值
    int getContentLength() 返回头部 content-length字段值
    String getContentType() 返回头部 content-type 字段值
    public void setDoInput(boolean input) URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 true。
    public void setDoOutput(boolean output) URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输出,则将 DoOutput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 false。
    public InputStream getInputStream() throws IOException 返回URL的输入流,用于读取资源
    public OutputStream getOutputStream() throws IOException 返回URL的输出流, 用于写入资源
    public URL getURL() 返回 URLConnection 对象连接的URL

    4.HttpURLConnection类(URLConnection的子类)的方法

    方法 描述
    public void setRequestMethod(String method) throws ProtocolException 设置请求方式,默认方式是GET
    public int getResponseCode() throws IOException 请求的状态码,一个int代表三位HTTP状态代码。1xx:信息 ;2xx:成功 ;3xx:重定向 ;4xx:客户端错误 ;5xx:服务器错误
    public String getResponseMessage() throws IOException HTTP响应信息

    5. 简单的后台文件

    后台文件目录
    保存上传的文件目录

    login.php 文件

    <?PHP
       #获取用户输入的姓名和密码
       $name = $_GET["user_name"];
       $pwd = $_GET["user_password"];
       $user = array(
            "name"=>$name,
            "password"=>$pwd,
       );
    
       $result = array(
              "user"=>$user,
              "total"=>"2",
              "status"=>0,
        );
    
       #规定返回类型数据为JSON数据
       header('Content-Type:application/json');
       echo json_encode($result);
    ?>
    

    loginPOST.php 文件

    <?PHP
       #获取用户输入的姓名和密码
       $name = $_POST["user_name"];
       $pwd = $_POST["user_password"];
       $user = array(
            "name"=>$name,
            "password"=>$pwd,
       );
    
       $result = array(
              "user"=>$user,
              "total"=>"2",
              "status"=>0,
        );
    
       #规定返回类型数据为JSON数据
       header('Content-Type:application/json');
       echo json_encode($result);
    ?>
    

    upLoadFile.php 文件

    <?PHP
       //获取文件
       $file = $_FILES["file"];
       
       //获取文件信息
       if($file["error"] > 0){
    
           //读取文件出错
           echo "Error:".$file["error"]."<br/>";
       }else{
    
           //输出详细信息
           echo "上传的文件名:".$file["name"]."<br/>";
           echo "上传的文件类型:".$file["type"]."<br/>";
           echo "上传的文件大小:".($file["size"]/1024)."Kb<br/>";
           echo "临时路径:".$file["tmp_name"]."<br/>";
           
           //判断文件类型
           $type = $file["type"];
           $path;
           if($type == "image/jpeg" || $type == "image/png"){
               //图片
               $path = "upLoad/img/";
           }else if($type == "video/mp4"){
               //视频
               $path = "upLoad/video/";
           }else if($type == "text/plain"){
               //文本
               $path = "upLoad/file/";
           }
       }
      
       $filePath = $path.$file["name"];
       //判断文件是否存在
    
       if(file_exists($filePath)){
           //存在
           echo $file["name"]."已存在";
       }else{
           //不存在
           //将临时文件里面的文件移动到指定目录
           move_uploaded_file($file["tmp_name"],$filePath);
           
           echo "文件已保存在:".$filePath;
       }
    ?>
    

    界面提交返回JSON数据的运行结果

    上传和下载文件返回结果数据的运行结果

    九.URL 发送Get请求

    1.GET方式请求:带参数的下载

    public static void getParam() throws IOException {
    
       //1.创建URL
       String path = "http://127.0.0.1/login.php?user_name=jack&user_password=123";
       URL url = new URL(path);
    
       //2.创建请求方式 获取链接的对象 URLConnection封装了Socket 默认的请求方式是Get
       URLConnection urlConnection = url.openConnection();
       //HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
       //urlConnection.setRequestMethod("GET");
    
       //查看请求状态
       //System.out.println(urlConnection.getResponseCode());
    
       //3.发送数据-就在URL里面
    
       //4.接受服务器端的数据 一行一行的读
       BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    2.GET方式请求:不带参数下载

    public static void getImage() throws IOException {
       //创建URL
       URL url = new URL("http://127.0.0.1/upLoad/img/1.jpg");
    
       //获取与服务器连接的对象
       URLConnection urlConnection = url.openConnection();
    
       //读取下载的内容-获取输入流
       InputStream is = urlConnection.getInputStream();
    
       //创建文件保存的位置
       FileOutputStream fos = new FileOutputStream(""C:\\Users\\a2867\\Desktop\\1.jpg"");
       byte[] buf = new byte[1024];
       int len;
       while ((len = is.read(buf)) != -1){
           fos.write(buf,0,len);
       }
    }
    

    3.Get 请求总结

    • Get请求常常被用来下载数据,既可以下载服务器端响应的数据,也可以下载具体的文件。

    • Get请求也可以用来上传数据,但是Get请求是通过 "key=value" 的键值对格式添加到URL后面的形式上传数据,由于没有请求体导致上传的数据大小受到URL长度限制。

    十.URL 发送Post请求

    1. POST方式上传 "key=value" 的键值对数据

    //POST方式上传数据
    public static void postParam() throws IOException {
       //1.创建url
       URL url = new URL("http://127.0.0.1/loginPOST.php");
    
       //2.获取连接对象
       //HttpURLConnection的父类URLConnection
       //需要设定请求的内容(请求方式,上传内容)用HttpURLConnection
       HttpURLConnection collection = (HttpURLConnection)url.openConnection();
    
       //3.设置请求方式为POST
       collection.setRequestMethod("POST");
       //设置有输出流 需要上传
       collection.setDoOutput(true);
       //设置有输入流 需要下载 默认true
       collection.setDoInput(true);
    
       //4.准备上传的数据
       String data = "user_name=jack&user_password=123";
    
       //5.开始上传 输出流对象
       OutputStream os = collection.getOutputStream();
       os.write(data.getBytes());
       os.flush();
       os.close();
    
       //6.接受服务器端返回数据
       BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    2. POST方式下载图片

    public static void postForImg() throws IOException {
       //创建URL
       URL url = new URL("http://127.0.0.1/upLoad/img/1.jpg");
    
       //获取与服务器连接的对象
       HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
       urlConnection.setRequestMethod("POST");
    
       //读取下载的内容-获取输入流
       InputStream is = urlConnection.getInputStream();
    
       //创建文件保存的位置
       FileOutputStream fos = new FileOutputStream("C:\\Users\\a2867\\Desktop\\1.jpg");
       byte[] buf = new byte[1024];
       int len;
       while ((len = is.read(buf)) != -1){
           fos.write(buf,0,len);
       }
    }
    

    3. POST方式上传 txt 文件

    //POST方式上传文件-文本
    public static void postFile() throws IOException {
       //1.创建url
       URL url = new URL("http://127.0.0.1/upLoadFile.php");
    
       //2.获取连接对象
       //HttpURLConnection的父类URLConnection
       //需要设定请求的内容(请求方式,上传内容)用HttpURLConnection
       HttpURLConnection collection = (HttpURLConnection)url.openConnection();
    
       //3.设置请求方式为POST
       collection.setRequestMethod("POST");
       //设置有输出流 需要上传
       collection.setDoOutput(true);
       //设置有输入流 需要下载 默认true
       collection.setDoInput(true);
    
       //上传格式
       final String newLine = "\r\n";
       final String boundaryPrefix = "--";
       String boundary = "ABC";
       //collection.setRequestProperty("connection","Keep-Alive");
       //collection.setRequestProperty("Charset","UTF-8");
       collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
       StringBuilder sb = new StringBuilder();
       sb.append(boundaryPrefix);
       sb.append(boundary);
       sb.append(newLine);
       sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.txt\"" + newLine);
       sb.append("Content-Type:text/plain" + newLine);
       sb.append(newLine);
       OutputStream out = new DataOutputStream(collection.getOutputStream());
       out.write(sb.toString().getBytes());// 将参数头的数据写入到输出流中
    
       //4.准备上传的数据
       InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\开发者文档.txt");
       //5.开始上传 输出流对象
       byte[] buf = new byte[1024];
       while ((bis.read(buf)) != -1){
           out.write(buf,0,buf.length);
       }
       out.write(newLine.getBytes());
       bis.close();
       byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes();
       out.write(end_data);
       out.flush();
       out.close();
    
       //6.接受服务器端返回数据-响应
       BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    4. POST方式上传 图片 文件

    //POST方式上传文件-文本
    public static void postFile() throws IOException {
       //1.创建url
       URL url = new URL("http://127.0.0.1/upLoadFile.php");
    
       //2.获取连接对象
       //HttpURLConnection的父类URLConnection
       //需要设定请求的内容(请求方式,上传内容)用HttpURLConnection
       HttpURLConnection collection = (HttpURLConnection)url.openConnection();
    
       //3.设置请求方式为POST
       collection.setRequestMethod("POST");
       //设置有输出流 需要上传
       collection.setDoOutput(true);
       //设置有输入流 需要下载 默认true
       collection.setDoInput(true);
    
       //上传格式
       final String newLine = "\r\n";
       final String boundaryPrefix = "--";
       String boundary = "ABC";
       //collection.setRequestProperty("connection","Keep-Alive");
       //collection.setRequestProperty("Charset","UTF-8");
       collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
       StringBuilder sb = new StringBuilder();
       sb.append(boundaryPrefix);
       sb.append(boundary);
       sb.append(newLine);
       sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.jpeg\"" + newLine);
       sb.append("Content-Type:image/jpeg" + newLine);
       sb.append(newLine);
       OutputStream out = new DataOutputStream(collection.getOutputStream());
       out.write(sb.toString().getBytes());// 将参数头的数据写入到输出流中
    
       //4.准备上传的数据
       InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\壁纸\\3.jpeg");
       //5.开始上传 输出流对象
       byte[] buf = new byte[1024];
       while ((bis.read(buf)) != -1){
           out.write(buf,0,buf.length);
       }
       out.write(newLine.getBytes());
       bis.close();
       byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes();
       out.write(end_data);
       out.flush();
       out.close();
    
       //6.接受服务器端返回数据-响应
       BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    5. POST方式上传 视频 文件

    //POST方式上传文件-文本
    public static void postFile() throws IOException {
       //1.创建url
       URL url = new URL("http://127.0.0.1/upLoadFile.php");
    
       //2.获取连接对象
       //HttpURLConnection的父类URLConnection
       //需要设定请求的内容(请求方式,上传内容)用HttpURLConnection
       HttpURLConnection collection = (HttpURLConnection)url.openConnection();
    
       //3.设置请求方式为POST
       collection.setRequestMethod("POST");
       //设置有输出流 需要上传
       collection.setDoOutput(true);
       //设置有输入流 需要下载 默认true
       collection.setDoInput(true);
    
       //上传格式
       final String newLine = "\r\n";
       final String boundaryPrefix = "--";
       String boundary = "ABC";
       //collection.setRequestProperty("connection","Keep-Alive");
       //collection.setRequestProperty("Charset","UTF-8");
       collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
       StringBuilder sb = new StringBuilder();
       sb.append(boundaryPrefix);
       sb.append(boundary);
       sb.append(newLine);
       sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.mp4\"" + newLine);
       sb.append("Content-Type:video/mp4" + newLine);
       sb.append(newLine);
       OutputStream out = new DataOutputStream(collection.getOutputStream());
       out.write(sb.toString().getBytes());// 将参数头的数据写入到输出流中
    
       //4.准备上传的数据
       InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\壁纸\\testVideo.mp4");
       //5.开始上传 输出流对象
       byte[] buf = new byte[1024];
       while ((bis.read(buf)) != -1){
           out.write(buf,0,buf.length);
       }
       out.write(newLine.getBytes());
       bis.close();
       byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes();
       out.write(end_data);
       out.flush();
       out.close();
    
       //6.接受服务器端返回数据-响应
       BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    6. POST请求总结

    • POST请求常用来上传文件数据,下载数据一般用GET请求。

    • POST请求和GET请求一样都是可以用来提交表单数据,提交表单数据的时候默认类型是 "application/x-www-form-urlencoded" 也就是key=value的键值对格式。

    • "multipart/from-data" 和 "application/x-www-form-urlencoded" 一样都是一种进行表单提交时的消息格式,"multipart/from-data" 用于提交文件类型数据。

    7.POST请求报文格式

    参考格式图片1
    参考格式2

    8.文件对应的 MIMEType

    类型 文件拓展名 MIMEType
    文本 js application/javascript
    文本 pdf application/pdf
    文本 text/txt text/plain
    文本 json application/json
    文本 xml text/xml
    图片 png image/png
    图片 bmp\dip image/bmp
    图片 jpe\jpeg\jpg image/jpeg
    图片 gif image/gif
    多媒体 mp3 audio/mpeg
    多媒体 mp4\mpg4\m4v\mp4v video/mp4

    9. 上传方法总结

    public static void postFileToServer(String serverURL,String postKey,String fileName,String fileType,String myPath) throws IOException {
       //1.创建url
       URL url = new URL(serverURL);
    
       //2.获取连接对象
       //HttpURLConnection的父类URLConnection
       //需要设定请求的内容(请求方式,上传内容)用HttpURLConnection
       HttpURLConnection collection = (HttpURLConnection)url.openConnection();
    
       //3.设置请求方式为POST
       collection.setRequestMethod("POST");
       //设置有输出流 需要上传
       collection.setDoOutput(true);
       //设置有输入流 需要下载 默认true
       collection.setDoInput(true);
    
       //上传格式
       final String newLine = "\r\n";
       final String boundaryPrefix = "--";
       String boundary = "ABC";
       //collection.setRequestProperty("connection","Keep-Alive");
       //collection.setRequestProperty("Charset","UTF-8");
       collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
       StringBuilder sb = new StringBuilder();
       sb.append(boundaryPrefix);
       sb.append(boundary);
       sb.append(newLine);
       sb.append("Content-Disposition: form-data;name=\""+postKey+"\";filename=\""+fileName+"\"" + newLine);
       sb.append("Content-Type:" + fileType + newLine);
       sb.append(newLine);
       OutputStream out = new DataOutputStream(collection.getOutputStream());
       out.write(sb.toString().getBytes());// 将参数头的数据写入到输出流中
    
       //4.准备上传的数据
       InputStream bis = new FileInputStream(myPath);
       //5.开始上传 输出流对象
       byte[] buf = new byte[1024];
       while ((bis.read(buf)) != -1){
           out.write(buf,0,buf.length);
       }
       out.write(newLine.getBytes());
       bis.close();
       byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes();
       out.write(end_data);
       out.flush();
       out.close();
    
       //6.接受服务器端返回数据-响应
       BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream()));
       System.out.println(br.readLine());
    }
    

    调用例子:

    postFileToServer("http://127.0.0.1/upLoadFile.php","file","test.jpg","image/jpeg","C:\\Users\\a2867\\Desktop\\壁纸\\2.jpg");
    

    请求 "本地服务器的upLoadFile.php文件" 上传到 "本机桌面壁纸文件夹里面的2.jpg,类型是image/jpeg的图片" 到服务器端中,上传后的名字是test.jpg

    各个参数意义:

    • serverURL:请求那个URL即那个后台文件处理我们当前的请求

    • postKey:上传文件的Key,就是upLoad.php中$file = $_FILES["file"]中的"file"

    • fileName:上传后文件的名称

    • fileType:上传的文件类型

    • myPath:上传的文件在电脑上的路径

    参考文章:

    HTTP POST请求报文格式分析与Java实现文件上传

    HTTP请求报文与响应报文格式,含:get与post的区别

    Socket与ServerSocket的应用

    相关文章

      网友评论

          本文标题:Java 网络

          本文链接:https://www.haomeiwen.com/subject/clwvsctx.html