Java中网络编程

主要内容

详细内容

网络编程概念

网络模型

(1)产生背景:

网络传输需要不同的组件协同工作,不同的组件工作的事件不同,为了区分不同的组件的不同任务,便对组件的功能进行划分,从而诞生网络模型;

网络模型的每个层次都有特定的任务需要完成。

(2)OSI参考模型:

全称是Open System Interconnect,即开放式系统互联,该模型一共有七层:应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层。

上图表明了两台主机之间应用层数据的传输过程!

(3)TCP/IP参考模型

TCP/IP模型是对OSI参考模型的简化,主要是由四层组成:应用层、网络层、传输层以及物理层,与OSI参考模型对应的关系如下所示:

网络通讯要素

(1)IP地址:

IP地址是用于唯一的标识网络中的一个通信实体,IP地址等于网络号码加上主机地址;

IP地址被分为ABCDE五类,每个类别的网络标识和主机标识都有规则。

A 1.0.0.1---127.255.255.254 其中:10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)

B 128.0.0.1---191.255.255.254 其中:172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。

C 192.0.0.1---223.255.255.254 192.168.X.X是私有地址

D 224.0.0.1---239.255.255.254 

E 240.0.0.1---247.255.255.254

特殊地址:

127.0.0.1是回环地址,可以用于测试本机的网络是否有问题;

Ipconfig:用于查看本机的IP地址;

Xxx.xxx.xxx.0:网络地址;

Xxx.xxx.xxx.255:广播地址

(2)端口号:

通信实体可以有多个通信程序同时提供网络服务,便需要端口;

不同的应用程序处理不同端口上的数据,端口号范围是065535,可以分为以下三类:

公认端口:01023,用于绑定特定的服务;

注册端口:102449151,应用程序通常使用该范围内端口号;

动态端口:4915265535

(3)传输协议:

TCP协议和UDP协议的区别:

UDP传输

上图表示了Socket,是为网络服务提供的一种机制,通信的两端都有Socket,网络通信的本质就是Socket之间的通信;数据在两个Socket之间通过IO进行传输。

UDP传输是通过DatagramSocketDatagramPacket两个类,其中DatagramSocket类格式如下所示:

public class DatagramSocket extends Object

构造方法:

DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。 
DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。

方法摘要:

void close() 关闭此数据报套接字。
InetAddress getInetAddress() 返回此套接字连接的地址。 
InetAddress getLocalAddress() 获取套接字绑定的本地地址。
int getPort() 返回此套接字的端口。 
void receive(DatagramPacket p) 从此套接字接收数据报包。 
void send(DatagramPacket p) 从此套接字发送数据报包。

DatagramPacket类格式如下所示:

public final class DatagramPacket  extends Object

构造方法:

DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
方法摘要:
InetAddress getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。 
byte[] getData() 返回数据缓冲区。 
int getLength() 返回将要发送或接收到的数据的长度。
int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。	

发送端

步骤:

(1)建立DatagramSocket服务;

(2)提供数据,并将数据封装到字节数组中;

(3)创建DatagramPacket数据包,并把数据封装到包中,同时指定IP和接收端口;

(4)通过Socket服务,利用send方法将数据包发送出去;

(5)关闭DatagramSocketDatagramPacket服务。

接收端

步骤:

(1)建立DatagramSocket服务,并监听一个端口;

(2)定义一个字节数组和一个数据包,同时将数组封装进数据包中;

(3)通过DatagramPacketreceive方法,将接收的数据存入定义好的数据包中;

(4)通过DatagramPacket关闭的方法,获取发送数据包中的信息;

(5)关闭DatagramSocketDatagramPacket服务;

示例代码(1)如下所示:

//通过UDP传输方式将一段文字发送出去
//UDP发送端
class UDPSend
{
	public static void main(String[] args) throws Exception
	{
		// 1,创建UDP服务,通过DatagramSocket对象
		DatagramSocket ds = new DatagramSocket(8888);
		// 2,确定数据。并将数据封装成包
		byte[] buf = "UDP传输方式".getBytes();
		DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.154"), 10000);
		// 3,通过Socket服务,将已有的数据包发送出去,通过send方法
		ds.send(dp);
		// 4,关闭资源
		ds.close();
	}
}

// UDP接收端
class UDPRecive
{
	public static void main(String[] args) throws Exception
	{
		// 1,创建UDP Socket,建立端点
		DatagramSocket ds = new DatagramSocket(1000);
		while (true)
		{
			// 2,定义数据包用于存储数据
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf, buf.length);
			// 3,通过服务的receive方法将收到的数据存储到数据包中
			ds.receive(dp);
			// 4,通过数据包的方法获取其中的数据
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(), 0, dp.getLength());
			int port = dp.getPort();
			System.out.println("IP地址为: " + ip + ",端口为:" + port + ",传送的数据为:" + data);
		}
	}
}

示例代码(2)如下所示:

//UDP键盘录入数据,并发送给接收端
//UDP发送端
class UDPSend2
{
	public static void main(String[] args) throws Exception
	{
		// 1,创建UDP服务,通过DatagramSocket对象
		DatagramSocket ds = new DatagramSocket(8888);
		// 2,通过缓冲区从键盘读取数据
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			if ("over".equals(line))
			{
				break;
			}
			// 3,确定数据。并将数据封装成包
			byte[] buf = line.getBytes();
			DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.254"), 10000);
			// 4,通过Socket服务,将已有的数据包发送出去,通过send方法
			ds.send(dp);
		}
		// 5,关闭资源
		ds.close();
	}
}
// UDP接收端
class UDPReceive2
{
	public static void main(String[] args) throws Exception
	{
		// 1,创建UDP Socket,建立端点
		DatagramSocket ds = new DatagramSocket(1000);
		while (true)
		{
			// 2,定义数据包用于存储数据
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf, buf.length);
			// 3,通过服务的receive方法将收到的数据存储到数据包中
			ds.receive(dp);
			// 4,通过数据包的方法获取其中的数据
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(), 0, dp.getLength());
			int port = dp.getPort();
			System.out.println("IP地址为: " + ip + ",端口为:" + port + ",传送的数据为:" + data);
		}
	}
}

示例代码(3)如下所示:

//编写简单的聊天工具
public class UDPTest
{
	public static void main(String[] args) throws Exception
	{
		DatagramSocket sendSocket = new DatagramSocket();
		DatagramSocket receiveSocket = new DatagramSocket(10000);
		new Thread(new UDPSend3(sendSocket)).start();
		new Thread(new UDPReceive3(receiveSocket)).start();
	}
}
// 发送端
class UDPSend3 implements Runnable
{
	private DatagramSocket ds;
	public UDPSend3()
	{
		super();
	}
	public UDPSend3(DatagramSocket ds)
	{
		super();
		this.ds = ds;
	}
	@Override
	public void run()
	{
		// 使用缓冲区提高效率
		BufferedReader bufr = null;
		try
		{
			// 1,通过缓冲区从键盘读取数据
			bufr = new BufferedReader(new InputStreamReader(System.in));

			String line = null;
			while ((line = bufr.readLine()) != null)
			{
				if ("over".equals(line))
				{
					break;
				}
				// 2,确定数据。并将数据封装成包
				byte[] buf = line.getBytes();
				// 3,通过Socket服务,将已有的数据包发送出去,通过send方法
				DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), 1000);
				ds.send(dp);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("发送失败!");
		}
	}
}
// 接收端
class UDPReceive3 implements Runnable
{
	private DatagramSocket ds;
	public UDPReceive3()
	{
		super();
	}
	public UDPReceive3(DatagramSocket ds)
	{
		super();
		this.ds = ds;
	}
	@Override
	public void run()
	{
		try
		{
			while (true)
			{
				// 1,定义数据包用于存储数据
				byte[] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf, buf.length);
				// 2,通过服务的receive方法将收到的数据存储到数据包中
				ds.receive(dp);
				// 3,通过数据包的方法获取其中的数据
				// 获取发送端的IP
				String ip = dp.getAddress().getHostAddress();
				// 获取发送端的数据
				String data = new String(dp.getData(), 0, dp.getLength());
				// 获取发送端的端口
				int port = dp.getPort();
				System.out.println("IP地址为: " + ip + ",端口为:" + port + ",传送的数据为:" + data);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("接收失败!");
		}
	}
}

TCP传输

public class Socket extends Object

通过Socket建立客户端。

1构造方法:

Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
(2)方法摘要:
void close() 关闭此套接字。
InetAddress getInetAddress() 返回套接字连接的地址。
InputStream getInputStream() 返回此套接字的输入流。
OutputStream getOutputStream() 返回此套接字的输出流。 
int getPort() 返回此套接字连接到的远程端口。
void shutdownInput() 此套接字的输入流置于“流的末尾”。 
void shutdownOutput() 禁用此套接字的输出流。 
String toString() 将此套接字转换为 String。
public class ServerSocket extends Object

通过ServerSocket建立服务器端;

(1)构造方法:

ServerSocket() 创建非绑定服务器套接字。 
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
(2)方法摘要:
Socket accept() 侦听并接受到此套接字的连接。
void close() 关闭此套接字。 
InetAddress getInetAddress() 返回此服务器套接字的本地地址。
建立连接之后,通过 Socket 中的 IO 流进行数据的传输;传输完成之后关闭 socket

客户端与服务器端是两个独立的应用程序。

UDP传输的比较

前面已做表格比较,此处省略。

客户端

步骤:

(1)建立Socket服务,并指定要连接的主机和端口;

(2)获取Socket流中的输出流OutputStream,将数据写入流中,通过网络发送给服务端;

(3)获取Socket流中的输出流InputStream,获取服务端的反馈信息;

(4)关闭资源。

服务端

步骤:

(1)建立ServerSocket服务,并监听一个端口;

(2)通过ServerSocket服务的accept方法,获取Socket服务对象;

(3)使用客户端对象的读取流获取客户端发送过来的数据;

(4)通过客户端对象的写入流反馈信息给客户端;

(5)关闭资源。

示例代码(1)如下所示:

// 客户端给服务端发送数据,服务端接收到后反馈信息给客户端
// 客户端
class TCPClient
{
	public static void main(String[] args) throws Exception
	{
		// 建立Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("127.0.0.1", 10000);
		// 获取Socket流中的输出流,并将数据写入流中
		OutputStream os = s.getOutputStream();
		// 通过网络发送给服务端
		os.write("这是TCP发送的数据".getBytes());
		// 获取Socket流中的输出流
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf, 0, len));
		// 关闭资源
		s.close();
	}
}

// 服务器端
class TCPServer
{
	public static void main(String[] args) throws Exception
	{
		// 建立ServerSocket服务,并监听一个端口;
		ServerSocket ss = new ServerSocket(10000);
		// 通过ServerSocket服务的accept方法,获取Socket服务对象;
		Socket s = ss.accept();
		// 使用客户端对象的读取流获取客户端发送过来的数据;
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP : " + ip);
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf, 0, len));
		// 关闭资源。
		s.close();
		ss.close();
	}
}

示例代码(2)如下所示:

// 建立一个文本转换服务端,客户给服务端发送文本,服务端将数据转换成大写后返回给客户端,当客户端输入over时,转换结束
// 客户端
class TransTextClient
{
	public static void main(String[] args) throws Exception
	{
		// 建立Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("127.0.0.1", 10000);
		// 通过缓冲区从键盘获取数据
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		// 将数据写入到Socket输出流中,发送给服务器端
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		// 定义一个Socket读取流,读取服务器端返回的大写信息
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			if ("over".equals(line))
			{
				break;
			}
			out.println(line);
			String returnString = bufIn.readLine();
			System.out.println("从服务器端返回的信息是: " + returnString);
		}
		// 关闭资源
		bufr.close();
		s.close();
	}
}
// 服务器端
class TransTextServer
{
	public static void main(String[] args) throws Exception
	{
		// 建立ServerSocket服务,并监听一个端口;
		ServerSocket ss = new ServerSocket(10000);
		// 通过ServerSocket服务的accept方法,获取Socket服务对象;
		Socket s = ss.accept();
		// 使用客户端对象的读取流获取客户端发送过来的数据;
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP : " + ip);
		// 读取Socket读取流中的数据
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		// 将大写数据写入到Socket输出流中,并发送给客户端
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		String line = null;
		while ((line = bufIn.readLine()) != null)
		{
			out.println(line.toUpperCase());
		}
		// 关闭资源。
		s.close();
		ss.close();
	}
}

示例代码(3)如下所示:

// 上传文件
// 客户端
class UploadTextClient
{
	public static void main(String[] args) throws Exception
	{
		// 建立Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("127.0.0.1", 10000);
		// 通过缓冲区从指定目录获取文件
		BufferedReader bufr = new BufferedReader(new FileReader("source//buf.txt"));
		// 将数据写入到Socket输出流中,发送给服务器端
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			out.println();
		}
		// 停止输出
		s.shutdownOutput();
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String returnStr = bufIn.readLine();
		System.out.println("从服务器端返回的信息是: " + returnStr);
		// 关闭资源
		bufr.close();
		s.close();
	}
}

// 服务器端
class UploadTextServer
{
	public static void main(String[] args) throws Exception
	{
		// 建立ServerSocket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(10000);
		// 通过ServerSocket服务的accept方法,并获取Socket服务对象
		Socket s = ss.accept();
		// 使用客户端对象的读取流获取客户端发送过来的数据
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP : " + ip);
		// 读取Socket读取流中的数据
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		// 将文件内容写入到Socket输出流中,并发送消息给客户端
		PrintWriter out = new PrintWriter(new FileWriter("source//copy.txt", true));
		String line = null;
		while ((line = bufIn.readLine()) != null)
		{
			out.write(line);
		}
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		pw.println("文件上传成功!");
		// 关闭资源
		out.close();
		s.close();
		ss.close();

	}
}

示例代码(4)如下所示:

// 上传文件
// 客户端
class UploadPicClient
{
	public static void main(String[] args) throws Exception
	{
		// 建立Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("127.0.0.1", 10000);
		// 通过缓冲区从指定目录获取文件
		BufferedReader bufr = new BufferedReader(new FileReader("source//buf.txt"));
		// 将数据写入到Socket输出流中,发送给服务器端
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		String line = null;
		while ((line = bufr.readLine()) != null)
		{
			out.println();
		}
		// 停止输出
		s.shutdownOutput();
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String returnStr = bufIn.readLine();
		System.out.println("从服务器端返回的信息是: " + returnStr);
		// 关闭资源
		bufr.close();
		s.close();
	}
}

// 服务器端
class UploadPicServer
{
	public static void main(String[] args) throws Exception
	{
		// 建立ServerSocket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(10000);
		// 通过ServerSocket服务的accept方法,并获取Socket服务对象
		Socket s = ss.accept();
		// 使用客户端对象的读取流获取客户端发送过来的数据
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP : " + ip);
		// 读取Socket读取流中的数据
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		// 将文件内容写入到Socket输出流中,并发送消息给客户端
		PrintWriter out = new PrintWriter(new FileWriter("source//copy.txt", true));
		String line = null;
		while ((line = bufIn.readLine()) != null)
		{
			out.write(line);
		}
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		pw.println("文件上传成功!");
		// 关闭资源
		out.close();
		s.close();
		ss.close();
	}
}

示例代码(5)如下所示:

public class PicThread implements Runnable
{
	private Socket s;
	public PicThread(Socket s)
	{
		super();
		this.s = s;
	}
	@Override
	public void run()
	{
		int count = 1;
		String ip = s.getInetAddress().getHostAddress();
		try
		{
			System.out.println("IP : " + ip);
			InputStream in = s.getInputStream();
			File dir = new File("source\\pics");
			File file = new File(dir, ip + "(" + (count) + ")" + ".jpg");
			while (file.exists())
			{
				file = new File(dir, ip + "(" + (count++) + ")" + ".jpg");
			}
			FileOutputStream fos = new FileOutputStream(file);
			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = in.read(buf)) != -1)
			{
				fos.write(buf, 0, len);
			}
			OutputStream out = s.getOutputStream();
			out.write("图片上传成功!".getBytes());
			fos.close();
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip + "图片上传失败!");
		}
	}
}
// 上传文件
// 客户端
class UploadPicByThreadClient
{
	public static void main(String[] args) throws Exception
	{
		// 对文件进行一系列判断
		if (args.length != 1)
		{
			System.out.println("请选择一个jpg格式的图片!");
			return;
		}
		File file = new File(args[0]);
		if (!(file.exists() && file.isFile()))
		{
			System.out.println("该文件存在问题!请更换!");
			return;
		}
		if (!file.getName().endsWith(".jpg"))
		{
			System.out.println("图片格式有误!");
			return;
		}
		if (file.length() > 1024 * 1024 * 5)
		{
			System.out.println("文件大小限制在5M!");
			return;
		}
		// 建立Socket服务,并指定要连接的主机和端口
		Socket s = new Socket("127.0.0.1", 10000);
		FileInputStream fis = new FileInputStream(file);
		// 将数据写入到Socket输出流中,发送给服务器端
		OutputStream out = s.getOutputStream();
		byte[] buf = new byte[1024];
		int len = 0;
		while ((len = fis.read(buf)) != -1)
		{
			out.write(buf, 0, len);
		}
		// 停止输出,告诉服务器数据已经写完
		s.shutdownOutput();
		InputStream in = s.getInputStream();
		byte[] bufIn = new byte[1024];
		int num = in.read(bufIn);
		System.out.println(new String(bufIn, 0, num));
		// 关闭资源
		fis.close();
		s.close();
	}
}
// 服务器端,将每个客户端封装到一个单独的线程中,这样便可以同时处理多个客户端的请求
class UploadPicByThreadServer
{
	public static void main(String[] args) throws Exception
	{
		// 建立ServerSocket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(10000);
		while (true)
		{
			// 通过ServerSocket服务的accept方法,并获取Socket服务对象
			Socket s = ss.accept();
			new Thread(new PicThread(s)).start();
		}

	}
}

示例代码(6)如下所示:

/*
 1,客户端通过键盘录入用户名。服务端对这个用户名进行校验。
 2, 如果该用户存在,在服务端显示xxx,已登陆。并在客户端显示 xxx,欢迎光临。
 3, 如果该用户存在,在服务端显示xxx,尝试登陆。并在客户端显示 xxx,该用户不存在。
 4, 最多就登录三次。
 */

public class UserThread implements Runnable
{
	private Socket s;
	public UserThread(Socket s)
	{
		super();
		this.s = s;
	}
	@Override
	public void run()
	{
		String ip = s.getInetAddress().getHostAddress();
		System.out.println("IP : " + ip);
		try
		{
			for (int x = 0; x < 3; x++)
			{
				BufferedReader bufIn = new BufferedReader(new InputStreamReader(System.in));
				String name = bufIn.readLine();
				if (name == null)
				{
					break;
				}
				BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));
				PrintWriter out = new PrintWriter(s.getOutputStream());
				String line = null;
				boolean flag = false;
				while ((line = bufr.readLine()) != null)
				{
					if (line.equals(name))
					{
						flag = true;
						break;
					}
					else
					{
						System.out.println(name + ",尝试登录");
						out.println(name + ",用户名不存在");
					}
				}
			}
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip + "校验失败");
		}
	}
}
class LoginClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("127.0.0.1", 10000);
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = new PrintWriter(s.getOutputStream(), true);
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		for (int x = 0; x < 3; x++)
		{
			String line = bufr.readLine();
			if (line == null) break;
			out.println(line);
			String info = bufIn.readLine();
			System.out.println("info:" + info);
			if (info.contains("欢迎")) break;
		}
		bufr.close();
		s.close();
	}
}
class LoginServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10000);
		while (true)
		{
			Socket s = ss.accept();
			new Thread(new UserThread(s)).start();
		}
	}
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值