Java 网络

网络

  • 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的应用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容