注:以下习题参考 计算机网络(第八版)谢希仁 编著,数据结构与算法 王曙燕 主编。
一、计算机网络第7章 网络安全(下) 习题与解答
7-15 试述实现报文鉴别和实体鉴别的办法。
答案:
| 类型 | 实现方法 |
|---|---|
| 报文鉴别 | MAC(报文鉴别码)、数字签名、哈希函数(MD5、SHA) |
| 实体鉴别 | 口令、挑战-应答、一次性密码、数字证书、生物特征 |
7-16 结合第5章图5-6计算UDP的检验和的例子,说明这种检验和不能用来鉴别报文。

答案:
UDP检验和只检测传输错误(比特翻转),不能防止主动攻击。攻击者可修改数据并重新计算检验和,接收方无法区分是否被篡改,因此不能用于报文鉴别。
7-17 报文的机密性与完整性有何区别?什么是MD5?
答案:
-
机密性:防止信息泄露(加密)
-
完整性:防止信息被篡改(哈希、MAC)
MD5:128位哈希函数,用于完整性校验,现已不安全。
7-18 什么是重放攻击?怎样防止重放攻击?
答案:
重放攻击:攻击者记录并重复发送合法报文,欺骗接收方。
防范方法:
-
序号/时间戳
-
一次性随机数(nonce)
-
挑战-应答
-
报文有效期限制
7-19 图 7-11 的鉴别过程也有可能被骗子利用。假定 A 发送报文和 B 联系,但不巧被骗子P 截获了,于是 P 发送报文给 A:"我是 B"。接着,A 就发送图 7-11 中的第一个报文"A,RA ",这里 R 是不重数。本来,P必须也发给 A 另一个不重数,以及发回使用两人共同拥有的密钥 KAB加密的 RA,即KAB(RA)。但P根本不知道 K,只好就发送同样的 RA 作为自己的不重数。A 收到 RA 后,发给 P 报文"KAB(RA)",P 仍然不知道密钥 KAB,也照样发回报文"KAB(RA)"。接着 A 就把一些报文发送给P了。虽然 P不知道密钥KB,但可以慢慢设法攻破。试问 A能否避免这样的错误?

答案:
可以 。A 应验证对方返回的 RA 是否与自己发送的一致,并要求对方用共享密钥 KAB 加密。骗子无法提供正确响应,A 应立即终止连接。
7-20 什么是"中间人攻击"?怎样防止这种攻击?
答案:
中间人攻击:攻击者在通信双方之间拦截并篡改报文,双方以为直接通信。
防范方法:
-
公钥证书(PKI)
-
数字签名
-
认证交换(如 TLS)
7-21 试讨论Kerberos协议的优缺点。
答案:
| 优点 | 缺点 |
|---|---|
| 单点登录 | 需要时间同步 |
| 强安全性 | 密钥分发中心(KDC)是单点故障 |
| 支持双向认证 | 实现复杂 |
7-22 互联网的网络层安全协议族IPsec都包含哪些主要协议?
答案:
-
AH(认证头):提供完整性、数据源认证
-
ESP(封装安全载荷):提供机密性、完整性、认证
-
IKE(密钥交换协议):建立安全关联(SA)
7-23 用户A和B使用IPsec进行通信。A需要向 B接连发送6个分组。是否需要每发送一个分组之前,都先建立一次安全关联SA?
答案:
不需要。SA 建立后可供多个分组重复使用,直到过期或主动删除。
7-24 在图 7-18(b)中,公司总部和业务员之间先建立了 TCP 连接,然后使用 sec 进行通信。假定有一个 TCP报文段丢失了。后来在重传该序号的报文段时,相应的Psec安全数据报是否也要使用同样的 IPsec 序号呢?

答案:
不能。IPsec序号用于防重放攻击,重传报文段应使用新序号,否则会被接收方丢弃。
7-25 试简述协议TLS的工作过程。
答案:
-
客户端发送支持的加密算法、随机数
-
服务器返回证书、选择算法、随机数
-
客户端验证证书,生成预主密钥,用服务器公钥加密发送
-
双方计算会话密钥
-
开始加密通信
7-26 在图 7-21 中,假定在第一步,顾客(客户A)发送报文给经销商(服务器B)时,误将报文发送到一个骗子处,而骗子就接着冒充经销商继续下面的步骤。试问在报文交互到第几个步骤时,顾客可以发现对方并不是真正的经销商?

答案:
在第 2 步(服务器返回证书或挑战时),顾客可发现证书无效或无法正确响应。
7-27 电子邮件的安全协议PGP主要都包含哪些措施?
答案:
-
数字签名(防篡改、防否认)
-
加密(机密性)
-
Base64编码
-
压缩
-
公钥/私钥管理(Web of Trust)
7-28 试述防火墙的工作原理和所提供的功能。什么叫作网络级防火墙和应用级防火墙?
答案:
| 类型 | 原理 | 功能 |
|---|---|---|
| 网络级防火墙 | 根据IP、端口过滤 | 包过滤、NAT |
| 应用级防火墙 | 分析应用层协议 | 内容过滤、代理 |
功能:隔离内外网、访问控制、防攻击、日志审计。
二、数据结构第9章 排序(下) 算法设计题
(1) 使用监视哨实现直接插入排序
核心思路
将待排元素放在 A[0] 作为监视哨,减少比较次数。
C语言
void insertSort(int A[], int n) {
for (int i = 2; i <= n; i++) {
A[0] = A[i];
int j = i - 1;
while (A[j] > A[0]) {
A[j + 1] = A[j];
j--;
}
A[j + 1] = A[0];
}
}
C++
void insertSort(vector<int>& A) {
A.insert(A.begin(), 0);
for (int i = 2; i < A.size(); i++) {
A[0] = A[i];
int j = i - 1;
while (A[j] > A[0]) {
A[j + 1] = A[j];
j--;
}
A[j + 1] = A[0];
}
A.erase(A.begin());
}
(2) 判断完全二叉树是否为大顶堆
核心思路
检查所有非叶结点:A[i] >= A[2i] && A[i] >= A[2i+1]
C语言
int isMaxHeap(int A[], int n) {
for (int i = 1; i <= n / 2; i++) {
if (2 * i <= n && A[i] < A[2 * i]) return 0;
if (2 * i + 1 <= n && A[i] < A[2 * i + 1]) return 0;
}
return 1;
}
C++
bool isMaxHeap(const vector<int>& A) {
int n = A.size() - 1;
for (int i = 1; i <= n / 2; i++) {
if (2 * i <= n && A[i] < A[2 * i]) return false;
if (2 * i + 1 <= n && A[i] < A[2 * i + 1]) return false;
}
return true;
}
(3) 编写算法,用基数排序方法将一组等长(含字母个数相同)的英文单词按字典顺序排列。
核心思路
-
从最后一个字母到第一个字母依次排序
-
每一轮按当前字母分配到26个桶中
-
再按桶顺序收集回原数组
-
重复直到所有字母处理完毕
C语言代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORDS 100
#define WORD_LEN 10
#define ALPHABET 26
void radixSortWords(char words[][WORD_LEN + 1], int n, int len) {
char buckets[ALPHABET][MAX_WORDS][WORD_LEN + 1];
int bucketSize[ALPHABET] = {0};
for (int pos = len - 1; pos >= 0; pos--) {
// 分配
for (int i = 0; i < n; i++) {
int idx = words[i][pos] - 'a';
strcpy(buckets[idx][bucketSize[idx]++], words[i]);
}
// 收集
int idx = 0;
for (int i = 0; i < ALPHABET; i++) {
for (int j = 0; j < bucketSize[i]; j++) {
strcpy(words[idx++], buckets[i][j]);
}
bucketSize[i] = 0;
}
}
}
C++代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
void radixSortWords(vector<string>& words, int len) {
for (int pos = len - 1; pos >= 0; pos--) {
vector<vector<string>> buckets(26);
for (const string& w : words) {
buckets[w[pos] - 'a'].push_back(w);
}
words.clear();
for (auto& bucket : buckets) {
words.insert(words.end(), bucket.begin(), bucket.end());
}
}
}
(4) 编写算法,以顺序队列实现基数排序。
核心思路
-
使用队列数组作为桶
-
入队操作替代分配到桶
-
出队操作替代收集
C语言代码
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
#define RADIX 10
typedef struct {
int data[MAX];
int front, rear;
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = 0;
}
int isEmpty(Queue* q) {
return q->front == q->rear;
}
void enqueue(Queue* q, int val) {
q->data[q->rear++] = val;
}
int dequeue(Queue* q) {
return q->data[q->front++];
}
void radixSortQueue(int arr[], int n, int maxDigits) {
Queue buckets[RADIX];
for (int i = 0; i < RADIX; i++) initQueue(&buckets[i]);
int exp = 1;
for (int d = 0; d < maxDigits; d++) {
// 分配
for (int i = 0; i < n; i++) {
int digit = (arr[i] / exp) % 10;
enqueue(&buckets[digit], arr[i]);
}
// 收集
int idx = 0;
for (int i = 0; i < RADIX; i++) {
while (!isEmpty(&buckets[i])) {
arr[idx++] = dequeue(&buckets[i]);
}
}
exp *= 10;
}
}
C++代码
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
void radixSortQueue(vector<int>& arr, int maxDigits) {
int exp = 1;
for (int d = 0; d < maxDigits; d++) {
vector<queue<int>> buckets(10);
for (int num : arr) {
int digit = (num / exp) % 10;
buckets[digit].push(num);
}
arr.clear();
for (auto& bucket : buckets) {
while (!bucket.empty()) {
arr.push_back(bucket.front());
bucket.pop();
}
}
exp *= 10;
}
}
(5) 计数排序(Counting Sort)
核心思路
-
对每个元素,统计有多少个元素比它小
-
该统计值就是它在结果数组中的位置
C语言代码
#include <stdlib.h>
void countingSort(int A[], int B[], int n) {
for (int i = 0; i < n; i++) {
int count = 0;
for (int j = 0; j < n; j++) {
if (A[j] < A[i]) count++;
}
B[count] = A[i];
}
}
C++代码
#include <vector>
using namespace std;
vector<int> countingSort(const vector<int>& A) {
int n = A.size();
vector<int> B(n);
for (int i = 0; i < n; i++) {
int count = 0;
for (int j = 0; j < n; j++) {
if (A[j] < A[i]) count++;
}
B[count] = A[i];
}
return B;
}
(6) 表插入排序(静态链表实现)
核心思路
-
使用静态链表结构(数组 + next指针)
-
插入时只修改next指针,不移动元素
-
最后按next顺序输出
C语言代码
#include <stdio.h>
#define MAX 100
typedef struct {
int data;
int next;
} StaticLink;
void tableInsertSort(StaticLink list[], int n) {
// 初始化头结点
list[0].next = 1;
list[1].next = -1;
for (int i = 2; i <= n; i++) {
int p = 0;
int q = list[p].next;
while (q != -1 && list[q].data < list[i].data) {
p = q;
q = list[q].next;
}
list[p].next = i;
list[i].next = q;
}
}
C++代码
#include <iostream>
#include <vector>
using namespace std;
struct StaticLink {
int data;
int next;
};
void tableInsertSort(vector<StaticLink>& list) {
int n = list.size() - 1;
list[0].next = 1;
list[1].next = -1;
for (int i = 2; i <= n; i++) {
int p = 0;
int q = list[p].next;
while (q != -1 && list[q].data < list[i].data) {
p = q;
q = list[q].next;
}
list[p].next = i;
list[i].next = q;
}
}
(7) 数组循环右移k位(O(n)时间,O(1)空间)
核心思路
三步反转法:
-
反转整个数组
-
反转前k个元素
-
反转后n-k个元素
C语言代码
void reverse(int A[], int l, int r) {
while (l < r) {
int temp = A[l];
A[l] = A[r];
A[r] = temp;
l++; r--;
}
}
void rightRotate(int A[], int n, int k) {
if (n == 0) return;
k = k % n;
if (k == 0) return;
reverse(A, 0, n - 1);
reverse(A, 0, k - 1);
reverse(A, k, n - 1);
}
C++代码
#include <algorithm>
#include <vector>
using namespace std;
void rightRotate(vector<int>& A, int k) {
int n = A.size();
if (n == 0) return;
k %= n;
if (k == 0) return;
reverse(A.begin(), A.end());
reverse(A.begin(), A.begin() + k);
reverse(A.begin() + k, A.end());
}
(8) 鸡尾酒排序(双向冒泡排序)
核心思路
-
每趟交替方向
-
奇数趟从左到右冒泡(最大沉底)
-
偶数趟从右到左冒泡(最小上浮)
C语言代码
void cocktailSort(int A[], int n) {
int left = 0, right = n - 1;
int swapped = 1;
while (left < right && swapped) {
swapped = 0;
// 从左到右
for (int i = left; i < right; i++) {
if (A[i] > A[i + 1]) {
int temp = A[i];
A[i] = A[i + 1];
A[i + 1] = temp;
swapped = 1;
}
}
right--;
if (!swapped) break;
// 从右到左
for (int i = right; i > left; i--) {
if (A[i] < A[i - 1]) {
int temp = A[i];
A[i] = A[i - 1];
A[i - 1] = temp;
swapped = 1;
}
}
left++;
}
}
C++代码
void cocktailSort(vector<int>& A) {
int left = 0, right = A.size() - 1;
bool swapped = true;
while (left < right && swapped) {
swapped = false;
for (int i = left; i < right; i++) {
if (A[i] > A[i + 1]) {
swap(A[i], A[i + 1]);
swapped = true;
}
}
right--;
if (!swapped) break;
for (int i = right; i > left; i--) {
if (A[i] < A[i - 1]) {
swap(A[i], A[i - 1]);
swapped = true;
}
}
left++;
}
}
(9) 荷兰国旗问题(红、白、蓝三色排序)
核心思路
三指针法:
-
red指向红色区域的末尾 -
white当前扫描指针 -
blue指向蓝色区域的开头
C语言代码
typedef enum { RED, WHITE, BLUE } Color;
void dutchFlag(Color arr[], int n) {
int red = 0, white = 0, blue = n - 1;
while (white <= blue) {
switch (arr[white]) {
case RED:
Color temp = arr[red];
arr[red] = arr[white];
arr[white] = temp;
red++;
white++;
break;
case WHITE:
white++;
break;
case BLUE:
temp = arr[white];
arr[white] = arr[blue];
arr[blue] = temp;
blue--;
break;
}
}
}
C++代码
#include <vector>
using namespace std;
enum Color { RED, WHITE, BLUE };
void dutchFlag(vector<Color>& arr) {
int red = 0, white = 0, blue = arr.size() - 1;
while (white <= blue) {
switch (arr[white]) {
case RED:
swap(arr[red], arr[white]);
red++;
white++;
break;
case WHITE:
white++;
break;
case BLUE:
swap(arr[white], arr[blue]);
blue--;
break;
}
}
}
(10) 求两个有序数组的中位数(O(log n))
核心思路
-
在较短的数组上二分查找切分点
-
保证左半部分长度 = (n+m+1)/2
-
调整切分位置使左半部分最大值 ≤ 右半部分最小值
C语言代码
#include <limits.h>
double findMedianSortedArrays(int A[], int m, int B[], int n) {
if (m > n) return findMedianSortedArrays(B, n, A, m);
int left = 0, right = m;
while (left <= right) {
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int maxLeftA = (i == 0) ? INT_MIN : A[i - 1];
int minRightA = (i == m) ? INT_MAX : A[i];
int maxLeftB = (j == 0) ? INT_MIN : B[j - 1];
int minRightB = (j == n) ? INT_MAX : B[j];
if (maxLeftA <= minRightB && maxLeftB <= minRightA) {
if ((m + n) % 2 == 0) {
return (double)(max(maxLeftA, maxLeftB) + min(minRightA, minRightB)) / 2;
} else {
return (double)max(maxLeftA, maxLeftB);
}
} else if (maxLeftA > minRightB) {
right = i - 1;
} else {
left = i + 1;
}
}
return 0.0;
}
C++代码
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
double findMedianSortedArrays(const vector<int>& A, const vector<int>& B) {
int m = A.size(), n = B.size();
if (m > n) return findMedianSortedArrays(B, A);
int left = 0, right = m;
while (left <= right) {
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int maxLeftA = (i == 0) ? INT_MIN : A[i - 1];
int minRightA = (i == m) ? INT_MAX : A[i];
int maxLeftB = (j == 0) ? INT_MIN : B[j - 1];
int minRightB = (j == n) ? INT_MAX : B[j];
if (maxLeftA <= minRightB && maxLeftB <= minRightA) {
if ((m + n) % 2 == 0) {
return (max(maxLeftA, maxLeftB) + min(minRightA, minRightB)) / 2.0;
} else {
return max(maxLeftA, maxLeftB);
}
} else if (maxLeftA > minRightB) {
right = i - 1;
} else {
left = i + 1;
}
}
return 0.0;
}
注:以上习题解答的理解和计算,如果有任何错误,希望各位读者和大佬指出改正,非常感谢!!!