前言

  • Java进阶课程的第五篇,网络编程相关内容。

  • 学习完基础之后就是进阶的内容了。


包含的知识

  1. 网络编程基础
  • InetAddress类:用于获取主机的IP地址信息。
  • getLocalHost():返回本地主机的InetAddress对象。
  • getByName(String host):通过域名获取远程主机的InetAddress对象。
  • isReachable(int timeout):尝试确定是否可以通过网络到达该地址。
  1. UDP通信
  • DatagramSocket类:代表一个UDP套接字,允许发送和接收数据报文。
  • DatagramPacket类:封装了UDP数据报,包含要发送的数据和目的地或接收到的数据及来源。
  • 构造函数用于创建数据包,指定数据、长度、目标地址和端口(对于发送);或者仅指定缓冲区大小(对于接收)。
  • send(DatagramPacket p):从DatagramSocket发送数据报。
  • receive(DatagramPacket p):在DatagramSocket上接收数据报。
  1. TCP通信
  • Socket类:表示TCP连接的一个端点,用于客户端与服务器之间的双向通信。
  • 构造函数可以指定服务器的IP地址和端口号来建立到服务器的连接。
  • getInputStream()和getOutputStream():分别获取输入输出流以读写数据。
  • ServerSocket类:监听特定端口上的连接请求,创建新的Socket实例来处理每个连接。
  • accept():阻塞方法,等待并接受下一个连接请求,返回一个新的Socket实例。
  1. 数据传输
  • DataInputStream/DataOutputStream类:提供方便的方法来读取和写入基本数据类型和字符串。
  • writeUTF(String str)和readUTF():用于写入和读取可变长的Unicode字符串。
  • writeInt(int v)和readInt():用于写入和读取整数。
  1. 多线程处理
  • Thread类和Runnable接口:用于创建和管理线程,以便并发处理多个客户端连接。
  • start():启动一个新线程执行run()方法中的代码。
  • ThreadPoolExecutor:实现了一个线程池,可以管理和分配线程资源给任务。
  1. BS架构(浏览器-服务器架构)
  • 实现了一个简单的HTTP服务器,能够响应来自浏览器的请求,并发送HTML内容作为回复。
  • 使用了HTTP响应头和状态码(例如HTTP/1.1 200 OK)。
  • 设置了响应的内容类型(如Content-Type:text/html;charset=utf-8)。
  • 构建了完整的HTML文档结构来作为响应体。

具体代码

客户端全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package ADV_0;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class ADVZc4K {
public static void main(String[] args) throws Exception {
System.out.println("======客户端开启======");
System.out.println("======UDP通信一发一收======");
// 创建发送端对象
DatagramSocket socket0 = new DatagramSocket(); // 随机端口
// 创建数据包对象封装要发送的数据
byte[] bytes = "客户端".getBytes();
//DatagramPacket参数:发送的数据(字节数组),发送的字节长度,目的地的IP地址,服务端程序的端口号
DatagramPacket packet0 = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8080);

//让发送端对象发送数据包的数据
socket0.send(packet0);
socket0.close();// 关闭socket

System.out.println("======UDP通信多发多收======");
// 创建发送端对象
DatagramSocket socket1 = new DatagramSocket(); // 随机端口

Scanner sc0 = new Scanner(System.in);
while (true) {
// 创建数据包对象封装要发送的数据
System.out.println("请说:");
String msg = sc0.nextLine();
// 如果用户输入的是 exit,则退出
if ("exit".equals(msg)) {
System.out.println("======退出======");
socket1.close();// 关闭socket
break;
}
byte[] bytes1 = msg.getBytes();
DatagramPacket packet1 = new DatagramPacket(bytes1, bytes1.length,
InetAddress.getLocalHost(), 8080);
// 让发送端对象发送数据包的数据
socket1.send(packet1);
}


System.out.println("===实现TCP通信下一发一收===");
// 1、常见Socket管道对象,请求与服务端的Socket链接。可靠链接
Socket socket2 = new Socket("127.0.0.1", 9999);

// 2、从socket通信管道中得到一个字节输出流。
OutputStream os0 = socket2.getOutputStream();

// 3、特殊数据流。
DataOutputStream dos0 = new DataOutputStream(os0);
dos0.writeInt(1);
dos0.writeUTF("在吗?(小丑)");
socket2.close();// 关闭socket


System.out.println("===实现TCP通信下多发多收===");
// 1、常见Socket管道对象,请求与服务端的Socket链接。可靠链接
Socket socket3 = new Socket("127.0.0.1", 9999);

// 2、从socket通信管道中得到一个字节输出流。
OutputStream os1 = socket3.getOutputStream();

// 3、特殊数据流。
DataOutputStream dos1 = new DataOutputStream(os1);

Scanner sc1 = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc1.nextLine();
if ("exit".equals(msg)) {
System.out.println("退出成功!");
dos1.close(); // 关闭输出流
socket3.close(); // 关闭socket
break;
}
dos1.writeUTF(msg); // 发送数据
dos1.flush();
}
}
}

UDP通信的服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package ADV_0;

import java.net.*;

public class ADVZc40 {
public static void main(String[] args) throws Exception {
System.out.println("======InetAddress获取IP对象======");
try {
// 获取本机IP对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());
// 获取对方IP对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
// 判断本机与对方是否互通
System.out.println(ip2.isReachable(5000)); // false ping
}catch (Exception e){
e.printStackTrace();
}

System.out.println("======UDP通信一发一收:服务端======");
// 创建接收端对象,注册端口
DatagramSocket socket1 = new DatagramSocket(8080);
// 创建一个数据包对象负责接收数据
byte[] buf0 = new byte[1024 * 64];
//DatagramPacket参数:接收数据的字节数组,接收数据的字节数组长度
DatagramPacket packet1 = new DatagramPacket(buf0, buf0.length);

//接收数据,将数据封装到数据包对象的字节数组中去
socket1.receive(packet1);

// 数据是否收到
int len = packet1.getLength(); // 获取当前收到的数据长度
String data = new String(buf0, 0 , len);
System.out.println("服务端收到了:" + data);

// 获取对方的ip对象和程序端口
String ip = packet1.getAddress().getHostAddress();
int port = packet1.getPort();
System.out.println("对方ip:" + ip + " 对方端口:" + port);
socket1.close();


System.out.println("======UDP通信多发多收:服务端======");
// 创建接收端对象,注册端口
DatagramSocket socket3 = new DatagramSocket(8080);

// 创建一个数据包对象负责接收数据。
byte[] buf1 = new byte[1024 * 64];
DatagramPacket packet2 = new DatagramPacket(buf1, buf1.length);

while (true) {
// 接收数据,将数据封装到数据包对象的字节数组中去
socket3.receive(packet2); // 等待式接收数据

//数据是否收到
int len1 = packet2.getLength(); // 获取当前收到数据长度
String data1 = new String(buf1, 0 , len1);
System.out.println("服务端收到了:" + data1);

// 获取对方的ip对象和程序端口
String ip1 = packet2.getAddress().getHostAddress();
int port1 = packet2.getPort();
System.out.println("对方ip:" + ip1 + " 对方端口:" + port1);
socket3.close();
}
}
}

TCP通信的服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package ADV_0;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ADVZc41 {
public static void main(String[] args) throws Exception {
System.out.println("======服务端开启======");
System.out.println("===实现TCP通信下一发一收===");
// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket s = new ServerSocket(9999);
// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象
Socket socket0 = s.accept();
// 3、获取输入流,读取客户端发送的数据
InputStream is0 = socket0.getInputStream();
// 4、把字节输入流包装成特殊数据输入流
DataInputStream dis0 = new DataInputStream(is0);
// 5、读取数据
int id = dis0.readInt();
String msg0 = dis0.readUTF();
System.out.println("id=" + id + ",收到的客户端msg=" + msg0);
// 6、客户端的ip和端口
System.out.println("客户端的ip=" + socket0.getInetAddress().getHostAddress());
System.out.println("客户端的端口=" + socket0.getPort());

System.out.println("===实现TCP通信下多发多收===");
// 1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket ss = new ServerSocket(9999);
// 2、调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象
Socket socket1 = ss.accept();
// 3、获取输入流,读取客户端发送的数据
InputStream is1 = socket1.getInputStream();
// 4、把字节输入流包装成特殊数据输入流
DataInputStream dis1 = new DataInputStream(is1);
while (true) {
// 5、读取数据
String msg1 = dis1.readUTF(); // 等待读取客户端发送的数据
System.out.println("收到的客户端msg=" + msg1);
// 6、客户端的ip和端口
System.out.println("客户端的ip=" + socket1.getInetAddress().getHostAddress());
System.out.println("客户端的端口=" + socket1.getPort());
}
}
}

TCP通信的服务端,支持多个客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package ADV_0;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ADVZc42 {
public static void main(String[] args) throws Exception {
System.out.println("===TCP通信下多发多收,支持多个客户端开发===");
// 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket sss = new ServerSocket(9999);

while (true) {
// 调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象
Socket socket = sss.accept();
System.out.println("客户端上线:" + socket.getInetAddress().getHostAddress());
// 把这个客户端管道交给一个独立的子线程专门负责接收这个管道的消息。
new ServerReader(socket).start();
}
}
}
class ServerReader extends Thread{
private Socket socket;
public ServerReader(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
// 读取管道的消息
// 获取输入流,读取客户端发送的数据
InputStream is = socket.getInputStream();
// 把字节输入流包装成特殊数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
// 读取数据
String msg = dis.readUTF(); // 等待读取客户端发送的数据
System.out.println("收到的客户端msg=" + msg);
// 客户端的ip和端口
System.out.println("客户端的ip=" + socket.getInetAddress().getHostAddress());
System.out.println("客户端的端口=" + socket.getPort());
}
} catch (Exception e) {
System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());
}
}
}

BS架构的原理服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package ADV_0;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class ADVZc43 {
public static void main(String[] args) throws Exception {
System.out.println("===BS架构的原理===服务端启动===");
// 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket ss = new ServerSocket(8080);

// 创建线程池
ExecutorService pool = new ThreadPoolExecutor(3, 10, 10, TimeUnit.SECONDS
, new ArrayBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

while (true) {
// 调用accept方法,阻塞等待客户端连接,一旦有客户端链接会返回一个Socket对象
Socket socket = ss.accept();
System.out.println("客户端上线:" + socket.getInetAddress().getHostAddress());
// 把这个客户端管道包装成一个任务交给线程池处理
pool.execute(new ServerReaderRunnable(socket));
}
}
}
class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
try {
// 给当前对应的浏览器管道响应一个网页数据回去。
OutputStream os = socket.getOutputStream();
// 通过字节输出流包装写出去数据给浏览器
// 把字节输出流包装成打印流。
PrintStream ps = new PrintStream(os);
// 写响应的网页数据出去
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charset=utf-8");
ps.println(); // 必须换一行
ps.println("<html>");
ps.println("<head>");
ps.println("<meta charset='utf-8'>");
ps.println("<title>");
ps.println("你好世界。。。");
ps.println("</title>");
ps.println("</head>");
ps.println("<body>");
ps.println("<h1 style='color:red;font-size=20px'>===如何学习Java===</h1>");
ps.println("</body>");
ps.println("</html>");
ps.close();
socket.close();
} catch (Exception e) {
System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());
}
}
}