网络编程 Java Socket

网络编程

计算机网络
  • 互联网:(Internet)点与点向相连

  • 万维网(WWW-World Wide Web)端与端相连

  • 物联网:(IoT -Internet of things)物与物相连

  • 网络编程:让计算机与计算机之间建立连接,进行通信。

  • 网络分类
    1.局域网
    2.城域网
    3.广域网

网络模型
  • OSI(Open System Interconnection)开放式系统互联

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

TCP/IP模型
  • 一组用于实现网络互联的通信协议,将协议分为四个层次

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

TCP/UDP
  • TCP协议:Transmission Control Protocol传输控制协议
    • 是一种面向连接的,可靠的,基于字节流的传输层通信协议。数据大小无限制。建立连接的过程需要三次握手,断开连接的过程需要四次挥手。
  • UDP协议:User Datagram Protocol用户数据报协议
    • 是一种无连接的传输层协议,提供面向事物的简单不可靠信息传送服务,每个包的大小是64kb.
IP
  • IP协议:Internet Protocol Address 互联网协议地址/网络协议地址
    • 分配给互联网设备的数字标签(唯一标识)。
  • IP地址分为两种;
    • IPV4:4字节32位整数,并分成4段8位的二进制,每8位之间用圆点隔开,每8位整数可以转换为一个0-255的十进制整数。
      • 格式D.D.D.D 例如:127.0.0.1
    • IPV6:16字节128位整数,并分成8段十六进制数,每16位之间用圆点隔开。每16位整数可以转化为一个0-655355的十进制整数。冒号分十六进制
      • 格式:X:X:X:X:X:X:X:X
IPV4的应用分类
  • A类:政府机构,1.0.0.1-126.255.255.254
  • B类:中型企业,128.0.0.1-191.255.255.254
  • C类:个人用户,192.0.0.1-223.255.255.254
  • D类:用于组播,224.0.0.1-239.255.255.254
  • E类:用于实验,240.0.0.1-255.255.255.254
  • 回环地址:127.0.0.1,指本机,一般用于测试使用
  • 查看IP命令:ipconfig
  • 测试IP命令:ping D.D.D.D
Port
  • 端口号:在通信实体上进行网络通讯的程序的唯一标识,16位
  • 一个程序可以占用多个端口
  • 端口分类:
    • 公认端口:0-1023
    • 注册端口:1024-49151
    • 动态或私有端口:29152-65535
  • 常用端口:
    • Mysql:3306
    • Oracle:1521
    • Tomcat:8080
    • SMTP:25
    • Web服务器:80
    • FTP服务器:21
    • HTTPS 443
InetAddress类
  • 概念:表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法。

  • 方法:

    • public static InetAddress getLocalHost()获取本地主机地址对象。
    • public static InetAddress getByName(String host)根据主机名称获得地址对象
    • public static InetAddress[]getAllByName(String host)获得所有相关地址对象
    • public String getHostAddress()获取IP地址字符串
    • public String getHostName()获得IP地址主机名
    • public Boolean isReachable(int timeout):测试地址是否可达
  • InetAddress类的使用

  • 1.本机地址

  • 2.局域网地址

  • 3.外网地址

		//1.本机地址
        InetAddress inetAddress = InetAddress.getLocalHost();
        System.out.println("IP地址:" + inetAddress.getHostAddress() + " 主机名" + inetAddress.getHostName());
        InetAddress inetAddress1 = InetAddress.getByName("169.254.56.36");
        System.out.println("IP地址:" + inetAddress1.getHostAddress() + " 主机名" + inetAddress1.getHostName());
        InetAddress inetAddress2 = InetAddress.getByName("127.0.0.1");
        System.out.println("IP地址:" + inetAddress2.getHostAddress() + " 主机名" + inetAddress2.getHostName());

        //2.局域网
        InetAddress inetAddress3 = InetAddress.getByName("10.0.139.165");
        System.out.println("IP地址:" + inetAddress3.getHostAddress() + " 主机名" + inetAddress3.getHostName());
        System.out.println("3秒是否可达?"+inetAddress3.isReachable(3000));

        //3.外网地址
        InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
        System.out.println("3秒是否可达?"+inetAddress4.isReachable(3000));
        System.out.println("IP地址:" + inetAddress4.getHostAddress() + " 主机名" + inetAddress4.getHostName());

 		InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com");
        for (InetAddress inetAddress : allByName) {
            System.out.println(inetAddress.getHostAddress());
        }
基于TCP的网络边恒
  • Socket编程:

    • Socket(套接字)是网络中的一个通信节点。
    • 分为客户端Socket与服务器ServerSocket.
    • 通信要求:IP地址+端口号
  • 开发步骤

    • 1.建立通信连接(回话):
      • 创建ServerSocket,指定端口号。
      • 调用accept等待客户端接入
    • 2.客户端请求服务器:
      • 创建Socket,指定服务器IP+端口号
      • 使用输出流,发送请求数据给服务器
      • 使用输入流,接受响应数据到客户端(等待)
    • 3.服务器响应客户端:
      • 使用输入流,接受请求数据到服务器(等待)
      • 使用输出流,发送响应数据给客户端
    客户端给服务端发信息
      //Server.java
      /**
       * 基于TCP协议的服务器端
       * 步骤:
       * 1.创建服务器套接字,并绑定端口号
       * 2.侦听客户端连接,返回socket
       * 3.获取输入,输出流
       * 4.处理数据
       * 5.关闭资源
       */
      public class TcpServer {
          public static void main(String[] args) throws IOException {
      //            * 1.创建服务器套接字,并绑定端口号
              ServerSocket listener = new ServerSocket(8080);
      //            * 2.侦听客户端连接,返回socket,阻塞方法,直到客户端连接
              Socket socket = listener.accept();
              System.out.println("Server start.");
      //            * 3.获取输入,输出流
              InputStream inputStream = socket.getInputStream();
              InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
              BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
      //            * 4.处理数据
              String info = bufferedReader.readLine();
              System.out.println("Client>>>>" + info);
      //            * 5.关闭资源
              listener.close();
              bufferedReader.close();
          }
      }
      
    
	
//Client.java

/**
 * 基于TCP协议的服务器
 * 四个步骤
 * 1.创建一个客户端套接字socket,并指定服务器的地址和端口号
 * 2.获取输入,输出流
 * 3.处理数据
 * 4.关闭资源
 */
public class TcpClient {
    public static void main(String[] args) throws IOException {
//        * 1.创建一个客户端套接字socket,并指定服务器的地址和端口号
        Socket socket = new Socket("169.254.56.38",8080);
//        * 2.获取输入,输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream,"utf-8"));

//        * 3.处理数据
        bufferedWriter.write("好久不见");
//        * 4.关闭资源
        bufferedWriter.close();
        socket.close();
    }
}

加强版
package com.demodong.java.test04;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

/**
 * author:demodong
 * 2020/8/10
 * 15:02
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
        int port = socket.getPort();
        InetAddress server = socket.getInetAddress();
        String name = server.getHostName();
        String address = server.getHostAddress();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("Client");
            bufferedWriter.write(scanner.nextLine());
            bufferedWriter.newLine();
            bufferedWriter.flush();
            String message = bufferedReader.readLine();
            System.out.println(address + name + port + ":" + message);
        }

    }
}

package com.demodong.java.test04;

import com.sun.org.apache.bcel.internal.generic.NEW;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * author:demodong
 * 2020/8/10
 * 14:57
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(8080);
        Socket socket = listener.accept();
        BufferedReader bufferedReader= new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF8"));
        BufferedWriter bufferedWriter =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF8"));
        InetAddress client = socket.getInetAddress();
        byte[] address = client.getAddress();
        String name = client.getHostName();
        int port = socket.getPort();
        Scanner scanner=new Scanner(System.in);
        while (true){
            String message = bufferedReader.readLine();
            System.out.println(address+name+":"+port+message);
            System.out.print("Server:");
            bufferedWriter.write(scanner.nextLine());
            bufferedWriter.newLine();
            bufferedWriter.flush();
        }
    }
}

文件

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(8080);
        Socket socket = listener.accept();
        InputStream inputStream = socket.getInputStream();
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\desktop\\demo_copy.txt");
        byte[] buffers = new byte[1024];
        int len = 0;
        while (true) {
            while ((len = inputStream.read(buffers)) != -1) {
                fileOutputStream.write(buffers, 0, len);
            }
            fileOutputStream.close();
        }
       
    }
}

package com.demodong.java.test06;

import com.sun.org.apache.bcel.internal.generic.NEW;

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

/**
 * author:demodong
 * 2020/8/10
 * 15:29
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);
        OutputStream outputStream = socket.getOutputStream();
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("d:\\desktop\\demo.txt"));
        byte[] buffers = new byte[1024];
        int len = 0;
        while ((len = bufferedInputStream.read(buffers)) != -1) {
            outputStream.write(buffers, 0, len);
        }
        bufferedInputStream.close();
    }
}

多线程服务器
//ChatRunnable.java
public class ChatRunnable implements Runnable {
    private Socket socket;

    public ChatRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = socket.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            String s = bufferedReader.readLine();
            System.out.println(s);
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8"));
            bufferedWriter.write("我收到了" + s);
            bufferedWriter.newLine();
            bufferedWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}
//Client.java
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        OutputStream outputStream = socket.getOutputStream();
                        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
                        bufferedWriter.write("nihaoya");
                        bufferedWriter.newLine();
                        bufferedWriter.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        InputStream inputStream = socket.getInputStream();
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
                        String message = bufferedReader.readLine();
                        System.out.println(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }
}
//ChatServer.java
public class ChatServer {
    public static void main(String[] args) throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        ServerSocket listener=new ServerSocket(8080);
        Socket socket = null;
        while (true){
            socket=listener.accept();
            executorService.submit(new ChatRunnable(socket));
        }
    }
}
UDP(Usr Datagram Protocol)
  • UPD特点:
    • 无连接,不可靠,每个数据包大小64k
    • 速度快,可实现广播发送
  • DatagramSocket:表示用来发送和接受数据包的套接字
  • DatagramPacket:表示数据包

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

UPD简单案例
//Sender.java
public class Sender {
    public static void main(String[] args) throws Exception {
        /**
         *
         * UDP协议的发送方
         */
        //1.创建DatagramSokcet,可以不提供端口号,自动分配一个端口号。
        DatagramSocket datagramSocket = new DatagramSocket();
        //2.创建DatagramPacket,发送数据报
        byte[] data = "好久不见".getBytes();
        DatagramPacket datagramPacket = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 9090);
        //3.发送
        datagramSocket.send(datagramPacket);
        //4.关闭
        datagramSocket.close();

    }
}
//Receiver.java

public class Receiver {
    public static void main(String[] args) throws Exception {
        //1.创建DatagramSocket,指定端口
        DatagramSocket datagramSocket = new DatagramSocket(9090);
        //2.创建DatagramPacket,接受数据报包
        byte[] buf = new byte[1024 * 4];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);
        //3.接受 ,阻塞方法,接收到数据才返回。
        System.out.println("hava alreay to receive");
        datagramSocket.receive(datagramPacket);
        //4.处理数据
        String data = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
        System.out.println(data);
        //5.关闭
        datagramSocket.close();
    }
}

UDP简单实现聊天
//ChatReciever.java

public class ChatReciever {
    public static void main(String[] args) throws Exception {
        //1.创建DatagramSocket
        DatagramSocket datagramSocket = new DatagramSocket(9090);
        //2.创建数据报包
        byte[] buffers = new byte[1024 * 10];
        DatagramPacket datagramPacket = new DatagramPacket(buffers, buffers.length);
        //3.接受
        while (true) {
            datagramSocket.receive(datagramPacket);
            //4.处理数据
            String data = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
            System.out.println(data);

        }
        //5.关闭
    }
}
//ChatSender.java

public class ChatSender {
    public static void main(String[] args) throws Exception {
        //1.创建DatagramSocket
        DatagramSocket datagramSocket = new DatagramSocket();
        //2.创建发送数据报
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String data = scanner.next();
            DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getByName("127.0.0.1"), 9090);

            datagramSocket.send(datagramPacket);
            if (data.equals("886")) {
                datagramSocket.close();
            }
        }
    }
}

可多次发送信息聊天
//ChatReciever.java
public class ChatReciever {
    public static void main(String[] args) throws Exception {
        //1.创建DatagramSocket
        DatagramSocket datagramSocket = new DatagramSocket(8899);
        //2.创建数据报包
        byte[] buffers = new byte[1024 * 10];
        DatagramPacket datagramPacket = new DatagramPacket(buffers, buffers.length);
        //3.接受
        while (true) {
            datagramSocket.receive(datagramPacket);
            //4.处理数据
            String data = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
            System.out.println(datagramPacket.getAddress());
            System.out.println(data);

        }
        //5.关闭
    }
}

//ChatSender.java
public class ChatSender {
    public static void main(String[] args) throws Exception {
        //1.创建DatagramSocket
        DatagramSocket datagramSocket = new DatagramSocket();
        //2.创建发送数据报
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String data = scanner.next();
            DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getByName("10.0.139.255"),8899);

            datagramSocket.send(datagramPacket);
            if (data.equals("886")) {
                datagramSocket.close();
            }
        }
    }
}

课堂作业
//Client.java
package com.qf.gp2002_4;

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

/*
 * wgy
 * 2020/8/10
 * 17:26
 */

public class Client {
    public static void main(String[] args) {
        System.out.println("------------1注册 2登录--------------");
        System.out.println("请选择");
        Scanner input=new Scanner(System.in);
        int choice=input.nextInt();
        try {
            switch (choice){
                case 1:
                    regist();
                    break;
                case 2:
                    break;
                default:
                    System.out.println("输入有误");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void regist() throws Exception{
        User user = getInfo();
        //1 创建Scoket
        Socket socket=new Socket("10.0.139.49", 6666);
        //2 获取输入输出流
        BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
       // BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //3 处理数据
        //3.1写入
//        System.out.println(user.toString());
//        bw.write(user.toString());
//        bw.newLine();
//        bw.flush();
        //使用对象流
        ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());
        oos.writeObject(user);
        oos.flush();
        //3.2读取
        String s = br.readLine();
        System.out.println("服务器回复:"+s);
        //4 关闭
        oos.close();
        br.close();
        socket.close();

    }

    //id : ”1001”, name :“tom”, pwd : ”123”, age : 20 , score : 98.5
    public static User getInfo(){
        Scanner input=new Scanner(System.in);
        System.out.println("请输入用户id");
        int id=input.nextInt();
        System.out.println("请输入姓名");
        String name=input.next();
        System.out.println("请输入密码");
        String pwd=input.next();
        System.out.println("请输入年龄");
        int age=input.nextInt();
        System.out.println("请输入成绩");
        double score=input.nextDouble();
        User user=new User(id, name, pwd,age,score);
        return user;

    }

    public static void login(){

    }
}

//LoginServer.java
package com.qf.gp2002_4;

/*
 * wgy
 * 2020/8/10
 * 17:13
 */
public class LoginServer implements Runnable {
    @Override
    public void run() {
        //实现登录
    }
}

//RegisteSery.java
package com.qf.gp2002_4;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;

/*
 * wgy
 * 2020/8/10
 * 17:13
 */
public class RegistServer implements Runnable {
    @Override
    public void run() {
        //注册功能
        try {
            //(1)创建ServerSocket,并指定端口号
            ServerSocket listener=new ServerSocket(6666);
            System.out.println("注册服务器已启动...");
            //(2)侦听,返回socket
            Socket socket = listener.accept();
            //(3)获取输入输出流
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //(4)处理数据
            String data = br.readLine();
            //1001 = {id : 1001, name :tom, pwd : 123, age : 20 , score : 98.5}
            //4.1获取id
            String[] arr = data.split("=");
            String id=arr[0];
            //4.2读取Properties
            File file=new File("users.properties");
            //判断文件是否存在
            Properties properties=new Properties();
            if(file.exists()) {
                FileInputStream fis=new FileInputStream(file);
                properties.load(fis);
                fis.close();
            }
            //4.3判断id是否存在
            if(properties.containsKey(id)){
                bw.write(id+"已经存在,请重新注册");
            }else{
                //注册成功
                properties.setProperty(id, arr[1]);
                //保存
                FileOutputStream fos=new FileOutputStream(file);
                properties.store(fos,"用户信息");
                fos.close();
                bw.write("注册成功");
            }
            bw.newLine();
            bw.flush();
            //5关闭资源
            br.close();
            bw.close();
            socket.close();
            listener.close();

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

    }
}

//RegistServer2.java
package com.qf.gp2002_4;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;


/*
 * wgy
 * 2020/8/10
 * 17:13
 */

public class RegistServer2 implements Runnable {
    @Override
    public void run() {
        //注册功能
        try {
            //(1)创建ServerSocket,并指定端口号
            ServerSocket listener=new ServerSocket(6666);
            System.out.println("注册服务器已启动...");
            //(2)侦听,返回socket
            Socket socket = listener.accept();
            //(3)获取输入输出流
            ObjectInputStream ois=new ObjectInputStream(socket.getInputStream());
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //(4)处理数据
            User user = (User) ois.readObject();
            //4.2读取Properties
            File file=new File("users.properties");
            //判断文件是否存在
            Properties properties=new Properties();
            if(file.exists()) {
                FileInputStream fis=new FileInputStream(file);
                properties.load(fis);
                fis.close();
            }
            //4.3判断id是否存在
            if(properties.containsKey(user.getId())){
                bw.write(user.getId()+"已经存在,请重新注册");
            }else{
                //注册成功
                properties.setProperty(user.getId()+"", user.toString().split("=")[1]);
                //保存
                FileOutputStream fos=new FileOutputStream(file);
                properties.store(fos,"用户信息");
                fos.close();
                bw.write("注册成功");
            }
            bw.newLine();
            bw.flush();
            //5关闭资源
            ois.close();
            bw.close();
            socket.close();
            listener.close();

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

    }
}

//Server.java
package com.qf.gp2002_4;

/*
 * wgy
 * 2020/8/10
 * 17:14
 */
public class Server {
    public static void main(String[] args) {
        new Thread(new RegistServer2()).start();
        new Thread(new LoginServer()).start();
    }
}

//User.java
package com.qf.gp2002_4;

import java.io.Serializable;

/*
 * wgy
 * 2020/8/10
 * 17:31
 */
public class User implements Serializable {
    private int id;
    private String name;
    private String pwd;
    private int age;
    private double score;

    public User(int id, String name, String pwd, int age, double score) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
        this.age = age;
        this.score = score;
    }

    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return this.id+"={" +
                "id:" + id +
                ", name:'" + name + '\'' +
                ", pwd:'" + pwd + '\'' +
                ", age:" + age +
                ", score:" + score +
                '}';
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值