04_网络编程
网络编程
什么是网络编程
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
java.net.* 包下提供了网络编程的解决方案
通信的基本架构
- CS 架构(Client 客户端 / Server 服务端)
- BS 架构(Browser 客户端 / Server 服务端)
网络通信三要素
- IP 地址
- 端口号
- 协议
IP 地址
import java.net.InetAddress;
public class Test {
public static void main(String[] args) throws Exception {
// 1. 获取本机IP地址对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName()); // TOMATO
System.out.println(ip1.getHostAddress()); // 10.233.86.87
// 2. 获取指定IP或者域名的IP地址对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName()); // www.baidu.com
System.out.println(ip2.getHostAddress()); // 120.232.145.185
// 3. 判断6000毫秒内,是否能够连通百度
System.out.println(ip2.isReachable(6000)); // true
}
}
端口号
- 标记正在计算机设备上运行的应用程序,被规定为一个16位的二进制,范围是 0 ~ 65535
通信协议
- 网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议
- UDP(User Datagram Protocol):用户数据报协议;
- TCP(Transmission Control Protocol):传输控制协议
UDP
- 特点:无连接,不可靠通信
快速入门
// 客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建客户端对象
DatagramSocket socket = new DatagramSocket();
// 2. 创建数据包对象封装要发出去的数据
/*
public DatagramPacket(byte buf[], int length, SocketAddress address) {
参数一 : 封装要发出去的数据
参数二 : 发送出去的数据大小(字节个数)
参数三 : 服务端的IP地址(找到服务端主机)
参数四 : 服务端的端口
}
*/
byte[] bytes = "我是客户端".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length,
InetAddress.getLocalHost(), 6666);
// 3. 开始正式发送这个数据包的数据出来了
socket.send(packet);
System.out.println("客户端数据发送完毕!");
// 4. 关闭连接,释放其所占用的网卡资源
socket.close();
}
}
// 服务端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
// 1. 创建一个服务端对象
DatagramSocket socket = new DatagramSocket(6666); // 参数:端口号
// 2. 创建一个数据包对象,用于接收数据
byte[] buffer = new byte[1024 * 64]; // 64KB
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 参数:
// 3. 正式使用数据包来接收客户端发来的数据
socket.receive(packet);
// 客户端ip
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
// 4. 从字节数组中,把接收到的数据直接打印出来
// 接收多少就倒出多少
// 获取本次数据包接收了多少数据
int len = packet.getLength();
String res = new String(buffer, 0, len);
System.out.println(res);
}
}
多发多收
// 客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建客户端对象
DatagramSocket socket = new DatagramSocket();
// 2. 创建数据包对象封装要发出去的数据
/*
public DatagramPacket(byte buf[], int length, SocketAddress address) {
参数一 : 封装要发出去的数据
参数二 : 发送出去的数据大小(字节个数)
参数三 : 服务端的IP地址(找到服务端主机)
参数四 : 服务端的端口
}
*/
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入>>> ");
String msg = sc.nextLine();
// 如果用户输入exit命令,就执行退出
if ("exit".equals(msg)) {
System.out.println("欢迎再来!");
socket.close(); // 4. 关闭连接,释放其所占用的网卡资源
break;
}
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length,
InetAddress.getLocalHost(), 6666);
// 3. 开始正式发送这个数据包的数据出来了
socket.send(packet);
System.out.println("客户端数据发送完毕!");
}
}
}
// 服务端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
// 1. 创建一个服务端对象
DatagramSocket socket = new DatagramSocket(6666); // 参数:端口号
// 2. 创建一个数据包对象,用于接收数据
byte[] buffer = new byte[1024 * 64]; // 64KB
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 参数:
while (true) {
// 3. 正式使用数据包来接收客户端发来的数据
socket.receive(packet);
// 客户端ip
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
// 4. 从字节数组中,把接收到的数据直接打印出来
// 接收多少就倒出多少
// 获取本次数据包接收了多少数据
int len = packet.getLength();
String res = new String(buffer, 0, len);
System.out.println(res);
System.out.println("-------------------");
}
}
}
TCP
- 特点:面向连接,可靠通信
快速入门
// 客户端
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建 Socket 对象,并同时请求与服务器程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 2. 从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
// 3. 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
// 4. 开始写数据出去了
dos.writeUTF("我们还是好朋友,对吧?");
dos.close(); // 关闭数据输出流管道
socket.close(); // 释放连接资源
}
}
// 服务端
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
// 1. 创建 ServerSocket 的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
// 2. 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
// 3. 从 socket 通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4. 把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
// 5. 使用数据输入流读取客户端发送过来的消息
String res = dis.readUTF();
System.out.println(res);
// 输出客户端的IP地址
System.out.println(socket.getRemoteSocketAddress());
dis.close();
socket.close();
}
}
多发多收
// 客户端
package login;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建 Socket 对象,并同时请求与服务器程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 2. 从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
// 3. 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入>>> ");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("欢迎再来!");
dos.close(); // 关闭数据输出流管道
socket.close(); // 释放连接资源
break;
}
// 4. 开始写数据出去了
dos.writeUTF(msg);
dos.flush();
}
}
}
// 服务端
package login;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
// 1. 创建 ServerSocket 的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
// 2. 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
// 3. 从 socket 通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4. 把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
// 5. 使用数据输入流读取客户端发送过来的消息
String res = dis.readUTF();
System.out.println(res);
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"断开了连接");;
socket.close();
break;
}
}
}
}
多个客户端同时使用服务端
// 客户端
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 创建 Socket 对象,并同时请求与服务器程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 2. 从 socket 通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
// 3. 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入>>> ");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("欢迎再来!");
dos.close(); // 关闭数据输出流管道
socket.close(); // 释放连接资源
break;
}
// 4. 开始写数据出去了
dos.writeUTF(msg);
dos.flush();
}
}
}
// 服务端
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
// 创建 ServerSocket 的对象,同时为服务端注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 使用 serverSocket 对象,调用一个 accept 方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "连接到了服务端");
// 使用一个独立的线程,把当前的 socket 对象交给它负责处理
new ServerReaderThread(socket).start();
}
}
}
class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 从 socket 通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
// 使用数据输入流读取客户端发送过来的消息
String res = dis.readUTF();
System.out.println(res);
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "断开了连接");
dis.close(); // 关闭流管道
socket.close(); // 关闭连接管道
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}