学习笔记11
第13章 TCP/IP和网络编程
知识点归纳
TCP/IP协议
- TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。TCP/IP的各个层级以及每个层级的代表性组之间及其功能如下:
- 互联网进行通信时,需要相应的网络协议,TCP/IP 原本就是为使用互联网而开发制定的协议族。因此,互联网的协议就是 TCP/IP,TCP/IP 就是互联网的协议。
IP主机和IP地址
- 一个IP地址的网络部分被称为网络号或者网络地址,主机可以与具有相同的网络号的设备直接通讯。
- IP地址是TCP/IP网络中用来唯一标识每台主机或设备的地址,IP地址由32位(共四个八位组)的二进制组成。IP地址分为两部分,左边网络编号部分用来标识主机所在的网络;右边部分用来标识主机本身,这部分称为主机地址。连接到同一网络的主机必须拥有相同的网络编号。
IP数据包格式:
IP协议
- IP协议是TCP/IP协议族的动力,它为上层协议提供无状态、无连接、不可靠的服务。
- 无状态是指IP通信双方不同步传输数据的状态信息,因此所有IP数据报的发送、传输和接收都是相互独立、没有上下文关系的。这种服务最大的缺点就是无法处理乱序和重复的IP数据报。面向连接的协议,比如TCP协议,能够自己处理乱序的、重复的报文段,它递交给上层协议的内容绝对是有序的、正确的。无状态服务的优点也很明显:简单、高效。我们无需为保持通信的状态而分配一些内核资源,也无需每次传输数据时都携带状态信息。
- 无连接是指IP通信双方都不长久地维持对方的任何信息。这样上层协议每次发送数据的时候,都必须明确指定对方的IP地址。
- 不可靠是指IP协议不能保证IP数据报准确地到达接收端,它只是承诺尽最大努力。很多情况都可以导致IP数据报发送失败。比如,某个中转路由器发现IP数据报在网络上存活地时间太长,那么它将丢弃该报文,并返回一个ICMP错误消息给发送端。因此,使用IP服务地上层协议需要自己实现数据确认、超时重传等机制以达到可靠传输的目的。
路由器的功能
- 路由器的功能就是将不同的子网之间的数据进行传递。 具体功能有以下几点:
1.实现IP、TCP、UDP、ICMP等网络的互连。
2.对数据进行处理。收发数据包,具有对数据的分组过滤、复用、加密、压缩及防护墙等各项功能。
3.依据路由表的信息,对数据包下一传输目的地进行选择。
4.进行外部网关协议和其他自治域之间拓扑信息的交换。
5.实现网络管理和系统支持功能。
套接字
-
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
-
套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。
struct sockaddr_in {
sa_family_t sin_family; // AF_INET for TCP/IP
in_port_t sin_port; // port number
struct in_addr sin_addr;// IP address
};
struct in_addr { // internet address
uint32_t s_addr; // IP address in network byte order
);
苏格拉底挑战
实践过程
server
// 服务器端(server.java)
package anquan;
import java.io.*;
import java.net.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.swing.SwingUtilities;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
public class server {
private static final String SECRET_KEY = "1234567890123456"; // 16字节的密钥
private static final String ALGORITHM = "AES";
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ServerFrame serverFrame = new ServerFrame();
serverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
serverFrame.setVisible(true);
});
}
private static class ServerFrame extends JFrame {
private JTextArea logArea;
public ServerFrame() {
setTitle("服务器");
setSize(400, 300);
logArea = new JTextArea();
logArea.setEditable(false);
add(new JScrollPane(logArea), BorderLayout.CENTER);
startServer();
}
private void startServer() {
try {
// 创建服务器套接字,监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
log("服务器监听在端口8080...");
while (true) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
log("客户端连接成功: " + clientSocket.getInetAddress());
// 为每个客户端创建一个新线程处理
new Thread(() -> handleClient(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleClient(Socket clientSocket) {
try {
// 获取输入流,用于读取客户端发送的数据
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String username = reader.readLine();
String password = reader.readLine();
// 用户身份验证
if (authenticateUser(username, password)) {
log("用户通过验证: " + username);
// 发送验证成功的消息
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
writer.println("身份验证成功。您现在已连接。");
// 获取Base64加密的数据
String base64EncryptedData = reader.readLine();
log("接收到Base64加密的数据: " + base64EncryptedData);
// 解密数据
String decryptedData = decryptBase64(base64EncryptedData);
log("解密后的数据: " + decryptedData);
// 在这里实现相应的业务逻辑或响应
SwingUtilities.invokeLater(() -> {
log("解密后的数据: " + decryptedData);
});
} else {
log("用户身份验证失败。关闭连接。");
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void log(String message) {
logArea.append(message + "\n");
logArea.setCaretPosition(logArea.getDocument().getLength());
}
// 用户身份验证方法
private boolean authenticateUser(String username, String password) {
// 在实际应用中,应该从数据库中获取用户信息并进行比较
// 这里只是为了演示目的使用硬编码的用户名和加密后的密码
boolean authenticationSuccess = "admin".equals(username) && "123".equals(password);
// 输出错误提示
if (!authenticationSuccess) {
log("错误:用户名或密码不匹配!");
}
return authenticationSuccess;
}
// 密码哈希方法
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashedBytes = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hashedBytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
// 解密Base64数据
private String decryptBase64(String base64Data) {
try {
byte[] encryptedData = Base64.getDecoder().decode(base64Data);
SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
}
client
// 客户端(client.java)
package anquan;
import java.io.*;
import java.net.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
public class client {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ClientFrame clientFrame = new ClientFrame();
clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clientFrame.setVisible(true);
});
}
private static class ClientFrame extends JFrame {
private JTextField usernameField;
private JPasswordField passwordField;
private JTextArea logArea;
public ClientFrame() {
setTitle("客户端");
setSize(400, 300);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 2));
JLabel usernameLabel = new JLabel("用户名:");
usernameField = new JTextField();
JLabel passwordLabel = new JLabel("密码:");
passwordField = new JPasswordField();
panel.add(usernameLabel);
panel.add(usernameField);
panel.add(passwordLabel);
panel.add(passwordField);
JButton connectButton = new JButton("连接服务器");
connectButton.addActionListener(new ConnectButtonListener());
panel.add(connectButton);
logArea = new JTextArea();
logArea.setEditable(false);
add(panel, BorderLayout.NORTH);
add(new JScrollPane(logArea), BorderLayout.CENTER);
}
private class ConnectButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
// 创建与服务器的Socket连接
Socket socket = new Socket("localhost", 8080);
// 获取输出流,用于向服务器发送数据
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// 发送用户名和密码
writer.println(usernameField.getText());
writer.println(new String(passwordField.getPassword()));
// 接收服务器的验证结果
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String serverResponse = reader.readLine();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 如果 serverResponse 不为 null 且身份验证成功,则继续
if (serverResponse != null && serverResponse.equals("身份验证成功。您现在已连接。")) {
// 准备需要加密的信息
String data = "Hello, Server!";
// 使用AES算法加密并转换为Base64编码
String base64EncryptedData = encryptBase64(data);
log("发送经Base64加密的数据: " + base64EncryptedData);
writer.println(base64EncryptedData);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
} else {
// 如果验证失败或没有收到响应,则显示错误消息
JOptionPane.showMessageDialog(ClientFrame.this, "错误:用户名或密码不匹配!", "身份验证失败", JOptionPane.ERROR_MESSAGE);
// 清除用户名和密码字段
usernameField.setText("");
passwordField.setText("");
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// 关闭Socket连接
socket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
// 使用AES算法加密数据,并转换为Base64编码
private String encryptBase64(String data) {
try {
// 使用密钥生成器生成AES密钥
SecretKeySpec secretKey = new SecretKeySpec("1234567890123456".getBytes(), "AES");
// 创建AES加密算法的Cipher对象
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 使用ECB模式和PKCS5Padding填充方式
// 初始化Cipher对象为加密模式,使用密钥
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 将数据补齐为16的倍数
int blockSize = cipher.getBlockSize();
int dataLength = data.getBytes().length;
int paddedLength = (dataLength / blockSize + 1) * blockSize;
byte[] paddedData = Arrays.copyOf(data.getBytes(), paddedLength);
// 对补齐后的数据进行加密
byte[] encryptedData = cipher.doFinal(paddedData);
// 将加密后的数据转换为Base64编码
return Base64.getEncoder().encodeToString(encryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void log(String message) {
logArea.append(message + "\n");
}
}
}