网络编程和密码学基础001.zip

1. 线程基本操作

  1. #include <iostream>
  2. #include <windows.h>
  3. // 1. 什么是内核对象?
  4. // - 内核对象本质是一个内核层的结构体。只能使用 win 提供的
  5. // - api 操作结构体的内容。
  6. // 2. 内核对象的特性
  7. // - 操作内核对象需要使用句柄,每一个进程都有一张句柄表保存了自己的句柄
  8. // - 大多数的内核对象在操作的时候都需要提供指定的安全描述符(安全属性)。
  9. // - 内核对象的全局性,不同的进程可以通过id或名称打开同一个内核对象。
  10. // - 内核对象的引用计数。每一个内核对象都有引用计数,当引用计数为 0 的时候
  11. // 内核对象会被销毁。CloseHandle 的作用是将引用计数 -1.
  12. // 3. 什么是进程?什么是线程?
  13. // - 进程: 是内核对象,通常由一个可执行文件产生。最少由一块 4 GB 的虚拟空
  14. // 间,一个进程内核对象和一个线程内核对象以及需要用到的模块组成。
  15. // - 线程: 是内核对象,被用于执行代码。线程之间没有从属关系,但是我们把一个
  16. // 进程的第一个线程称为主线程,主线程一旦退出整个程序就会退出。线程最少
  17. // 由一个线程内核对象,和一个线程的栈帧组成。
  18. // 线程的回调函数,参数一般是一个指针
  19. DWORD WINAPI WorkerThread(LPVOID lpThreadParameter)
  20. {
  21. for (int i = 0; i < 100; ++i)
  22. {
  23. printf("hello %08X\n", lpThreadParameter);
  24. // 让出当前线程的时间片
  25. Sleep(1000);
  26. }
  27. return 0;
  28. }
  29. int main()
  30. {
  31. // 1. 如何创建一个线程
  32. // - 参数一: 安全属性,NULL 表示默认
  33. // - 参数二: 栈帧的大小,使用默认,通常是 4M
  34. // - 参数三: 回调函数,指明线程的起始位置
  35. // - 参数四: 传递给线程函数的参数
  36. // - 参数五: 线程的创建标志,使用的比较多的是挂起
  37. // - 参数六: 线程的 id ,不需要可以传递 NULL
  38. HANDLE ThreadHandle = CreateThread(NULL, NULL,
  39. WorkerThread, (LPVOID)0x1515, NULL, NULL);
  40. Sleep(2000); // 让出时间片给 ThreadHandle
  41. // 2. 挂起和恢复线程:每一个线程都有一个挂起计数,没挂
  42. // 起一次,计数 +1,当挂起计数为 0 线程继续运行
  43. SuspendThread(ThreadHandle);
  44. system("pause");
  45. ResumeThread(ThreadHandle);
  46. system("pause");
  47. TerminateThread(ThreadHandle, 0);
  48. // 3. 为了确保在主线程执行完毕之前所有的其它线程都
  49. // 正常退出,需要等待其他线程执行完毕
  50. if (ThreadHandle != NULL)
  51. WaitForSingleObject(ThreadHandle, INFINITE);
  52. return 0;
  53. }

2. 多线程问题

  1. #include <iostream>
  2. #include <windows.h>
  3. int g_Number = 0; // 会被多个线程访问到的全局变量
  4. // 线程一: 给 g_Number 自增 100000 次
  5. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  6. {
  7. for (int i = 0; i < 100000; i++)
  8. {
  9. g_Number++;
  10. }
  11. return 0;
  12. }
  13. // 线程二: 给 g_Number 自增 100000 次
  14. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  15. {
  16. for (int i = 0; i < 100000; i++)
  17. {
  18. g_Number++;
  19. }
  20. return 0;
  21. }
  22. int main()
  23. {
  24. HANDLE hThread1 = NULL, hThread2 = NULL;
  25. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  26. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  27. if (hThread1 != NULL && hThread2 != NULL)
  28. {
  29. WaitForSingleObject(hThread1, INFINITE);
  30. WaitForSingleObject(hThread2, INFINITE);
  31. }
  32. printf("%d", g_Number);
  33. system("pause");
  34. return 0;
  35. }
  36. // 单条的 g_Number++ 被翻译成了三条汇编指令,如果不能保证三条指令的
  37. // [连续执行],就会由于线程的切换产生问题,最终结果出错。
  38. /*
  39. [1]: mov eax,dword ptr [g_Number (07AA138h)] // g_Number(0)
  40. [1]: add eax,1 // g_Number(0) eax(1)
  41. [1]: mov dword ptr [g_Number (07AA138h)],eax // g_Number(1) eax(1)
  42. [2]: mov eax,dword ptr [g_Number (07AA138h)] // g_Number(1)
  43. [2]: add eax,1 // g_Number(1) eax(2)
  44. [2]: mov dword ptr [g_Number (07AA138h)],eax // g_Number(2) eax(2)
  45. [1]: mov eax,dword ptr [g_Number (07AA138h)] // g_Number(2)
  46. [1]: add eax,1 // g_Number(2) eax(3)
  47. [2]: mov eax,dword ptr [g_Number (07AA138h)] // g_Number(2)
  48. [2]: add eax,1 // g_Number(2) eax(3)
  49. [2]: mov dword ptr [g_Number (07AA138h)],eax // g_Number(3) eax(3)
  50. [1]: mov dword ptr [g_Number (07AA138h)],eax // g_Number(3) eax(3)
  51. */

3. 原子操作

  1. #include <iostream>
  2. #include <windows.h>
  3. // 原子操作:本质上就是将C语言代码解释成了单条的汇编指令
  4. // 缺陷:只支持对最长8字节的整数类型执行算数运行
  5. // 应用场景: 在进行内联hook的时候可以解决线程安全
  6. // 函数: InterlockedXXXX 系列的函数
  7. long g_Number = 0; // 会被多个线程访问到的全局变量
  8. // 线程一: 给 g_Number 自增 100000 次
  9. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  10. {
  11. for (int i = 0; i < 100000; i++)
  12. {
  13. InterlockedIncrement(&g_Number);
  14. // lock inc dword ptr ds:[g_Number]
  15. }
  16. return 0;
  17. }
  18. // 线程二: 给 g_Number 自增 100000 次
  19. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  20. {
  21. for (int i = 0; i < 100000; i++)
  22. {
  23. InterlockedIncrement(&g_Number);
  24. }
  25. return 0;
  26. }
  27. int main()
  28. {
  29. HANDLE hThread1 = NULL, hThread2 = NULL;
  30. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  31. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  32. if (hThread1 != NULL && hThread2 != NULL)
  33. {
  34. WaitForSingleObject(hThread1, INFINITE);
  35. WaitForSingleObject(hThread2, INFINITE);
  36. }
  37. printf("%d", g_Number);
  38. system("pause");
  39. return 0;
  40. }

4. 临界区

  1. #include <iostream>
  2. #include <windows.h>
  3. // 临界区(关键段): 是一个结构体,通过结构体内的一些字段判断
  4. // 执行当前代码的线程是否是对应的线程,如果不是,就阻塞。拥
  5. // 有(线程拥有者)的概念
  6. // 优点: 可以保护一段代码,执行速度快
  7. // 缺点: 拥有该临界区的线程一旦崩溃,就会产生死锁。
  8. // 会被多个线程访问到的全局变量
  9. long g_Number = 0;
  10. // 临界区结构体,是一个不确定的结构体,使用前必须初始化
  11. // LONG LockCount: 如果被使用了就是 -2 否则是 -1
  12. // LONG RecursionCount: 表示当前被进入的多少次
  13. // HANDLE OwningThread: 当前被哪一个线程使用了
  14. CRITICAL_SECTION CriticalSection = { 0 };
  15. // 线程一: 给 g_Number 自增 100000 次
  16. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  17. {
  18. for (int i = 0; i < 100000; i++)
  19. {
  20. // 进入临界区,写在需要被保护的代码块的上方
  21. EnterCriticalSection(&CriticalSection);
  22. // 需要被保护的代码
  23. g_Number++;
  24. // 离开临界区,写在需要被保护的代码块的下方
  25. // 进入了一个临界区多少次,就应该相应的离开多少次
  26. LeaveCriticalSection(&CriticalSection);
  27. }
  28. return 0;
  29. }
  30. // 线程二: 给 g_Number 自增 100000 次
  31. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  32. {
  33. for (int i = 0; i < 100000; i++)
  34. {
  35. EnterCriticalSection(&CriticalSection);
  36. g_Number++;
  37. LeaveCriticalSection(&CriticalSection);
  38. }
  39. return 0;
  40. }
  41. int main()
  42. {
  43. HANDLE hThread1 = NULL, hThread2 = NULL;
  44. // 初始化临界区
  45. InitializeCriticalSection(&CriticalSection);
  46. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  47. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  48. if (hThread1 != NULL && hThread2 != NULL)
  49. {
  50. WaitForSingleObject(hThread1, INFINITE);
  51. WaitForSingleObject(hThread2, INFINITE);
  52. }
  53. // 使用后需要释放
  54. DeleteCriticalSection(&CriticalSection);
  55. printf("%d", g_Number);
  56. system("pause");
  57. return 0;
  58. }

6. 互斥体

  1. #include <iostream>
  2. #include <windows.h>
  3. // 互斥体: 是一个内核对象
  4. // 优点: 拥有临界区的特性(线程拥有者),但是不会产生死锁,且跨进程
  5. // 缺点: 慢。
  6. // 应用场景: 用于进行防双开
  7. // 相关函数:
  8. // - 保护: WaitForSingleObject
  9. // - 离开: RealseMutex
  10. // 会被多个线程访问到的全局变量
  11. long g_Number = 0;
  12. // 创建互斥体的函数
  13. // 参数1: 安全属性
  14. // 参数2: 是否设置当前线程为拥有者(设置了就是无信号)
  15. // 参数3: 互斥体的名称,用于打开
  16. HANDLE Mutex = CreateMutex(NULL, FALSE, L"my_mutex");
  17. // 初始(有信号) -> 等待函数(有信号->无信号) -> Release(无信号->有信号)
  18. // 线程一: 给 g_Number 自增 100000 次
  19. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  20. {
  21. for (int i = 0; i < 100000; i++)
  22. {
  23. // 进入受保护的代码(有信号->无信号)
  24. WaitForSingleObject(Mutex, INFINITE);
  25. // 需要被保护的代码
  26. g_Number++;
  27. // 将互斥体设置为无信号有信号
  28. ReleaseMutex(Mutex);
  29. }
  30. return 0;
  31. }
  32. // 线程二: 给 g_Number 自增 100000 次
  33. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  34. {
  35. for (int i = 0; i < 100000; i++)
  36. {
  37. WaitForSingleObject(Mutex, INFINITE);
  38. g_Number++;
  39. ReleaseMutex(Mutex);
  40. }
  41. return 0;
  42. }
  43. // 对线程拥有者的测试
  44. DWORD WINAPI ThreadPro3(LPVOID lpThreadParameter)
  45. {
  46. for (int i = 0; i < 100000; i++)
  47. {
  48. // 拥有指定互斥体的线程,可以无限次的等待互斥体
  49. WaitForSingleObject(Mutex, INFINITE);
  50. g_Number++;
  51. }
  52. return 0;
  53. }
  54. int main()
  55. {
  56. HANDLE hThread1 = NULL, hThread2 = NULL;
  57. // 测试线程拥有者的
  58. HANDLE hThread = CreateThread(NULL, NULL, ThreadPro3, NULL, NULL, NULL);
  59. WaitForSingleObject(hThread, INFINITE);
  60. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  61. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  62. if (hThread1 != NULL && hThread2 != NULL)
  63. {
  64. WaitForSingleObject(hThread1, INFINITE);
  65. WaitForSingleObject(hThread2, INFINITE);
  66. }
  67. printf("%d", g_Number);
  68. system("pause");
  69. return 0;
  70. }

7. 事件

  1. #include <iostream>
  2. #include <windows.h>
  3. // 互斥: 通常是多个线程访问同一个资源
  4. // 同步: 通常是需要多个线程按照指定顺序执行
  5. // 事件对象: 是一个内核对象
  6. // 特点: 可以设置为手动操作,也可以设置为自动操作
  7. // 缺点: 慢。
  8. // 相关函数:
  9. // - 保护: WaitForSingleObject
  10. // - 离开: RealseMutex
  11. // 会被多个线程访问到的全局变量
  12. long g_Number = 0;
  13. // 创建事件的函数
  14. // 参数1: 安全属性
  15. // 参数2: 是否是手动状态,一旦设置成手动状态,等待函数就没有副作用了
  16. // 参数3: 事件的初始信号
  17. // 参数3: 互斥体的名称,用于打开
  18. HANDLE Event = CreateEvent(NULL, TRUE, TRUE, L"my_event");
  19. // 当设置为手动状态之后,通常就不能用于进行互斥了,会被用于同步
  20. // 原因是, ResetEvent 函数本身并不是原子操作
  21. // 自动:初始(有信号) -> 等待函数(有信号->无信号) -> Release(无信号->有信号)
  22. // 手动:初始(有信号) -> 等待函数(有信号) -> Release(有信号)
  23. // 线程一: 给 g_Number 自增 100000 次
  24. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  25. {
  26. for (int i = 0; i < 100000; i++)
  27. {
  28. // 进入受保护的代码(有信号->无信号)
  29. WaitForSingleObject(Event, INFINITE);
  30. // 需要被保护的代码
  31. g_Number++;
  32. // 将互斥体设置为(无信号->有信号)
  33. SetEvent(Event);
  34. }
  35. return 0;
  36. }
  37. // 线程二: 给 g_Number 自增 100000 次
  38. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  39. {
  40. for (int i = 0; i < 100000; i++)
  41. {
  42. WaitForSingleObject(Event, INFINITE);
  43. g_Number++;
  44. SetEvent(Event);
  45. }
  46. return 0;
  47. }
  48. int main()
  49. {
  50. HANDLE hThread1 = NULL, hThread2 = NULL;
  51. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  52. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  53. if (hThread1 != NULL && hThread2 != NULL)
  54. {
  55. WaitForSingleObject(hThread1, INFINITE);
  56. WaitForSingleObject(hThread2, INFINITE);
  57. }
  58. printf("%d", g_Number);
  59. system("pause");
  60. return 0;
  61. }

5. 等待函数

  1. #include <iostream>
  2. #include <windows.h>
  3. // 等待函数的使用: 等待指定的一个或一组内核对象变为有信号状态
  4. // 使用的函数:
  5. // - 一个: WaitForSingleObject(内核对象句柄,等待时长)
  6. // - 一组: WaitForMultipleObjects()
  7. // 等待函数的副作用:
  8. // - 改变被等待内核对象的信号状态(有信号->无信号)
  9. // - 基于这个原理,才能实现后面的内核对象同步
  10. // 不断的输出传入的参数
  11. DWORD WINAPI WorkerThread1(LPVOID param)
  12. {
  13. for (int i = 0; i < 5; ++i)
  14. {
  15. printf("%d - %d\n", (DWORD)param, i);
  16. Sleep(500);
  17. }
  18. return 0;
  19. }
  20. // 不断的输出传入的参数
  21. DWORD WINAPI WorkerThread2(LPVOID param)
  22. {
  23. for (int i = 0; i < 5; ++i)
  24. {
  25. printf("%d - %d\n", (DWORD)param, i);
  26. Sleep(100);
  27. }
  28. return 0;
  29. }
  30. int main()
  31. {
  32. HANDLE Threads[10] = { 0 };
  33. for (int i = 0; i < 10; ++i)
  34. {
  35. if (i % 2)
  36. Threads[i] = CreateThread(NULL, NULL, WorkerThread1, (LPVOID)(i + 1), NULL, NULL);
  37. else
  38. Threads[i] = CreateThread(NULL, NULL, WorkerThread2, (LPVOID)(i + 1), NULL, NULL);
  39. }
  40. // 1. 需要等待的内核对象的数量(数组的元素个数)
  41. // 2. 保存所有需要等待的内核对象的数组
  42. // 3. 是否等待所有的内核对象成为有信号状态
  43. // 4. 等待时长
  44. WaitForMultipleObjects(10, Threads, true, INFINITE);
  45. // 什么时候函数会退出等待
  46. // 1. 目标对象变成有信号(激发)状态
  47. // 2. 达到了等待时长,函数返回等待失败
  48. return 0;
  49. }

8. 信号量

  1. #include <iostream>
  2. #include <windows.h>
  3. // 互斥: 通常是多个线程访问同一个资源
  4. // 同步: 通常是需要多个线程按照指定顺序执行
  5. // 信号量: 是一个内核对象
  6. // 特点: 可以进行多次的上锁操作
  7. // 缺点: 慢。
  8. // 应用场景:控制同时执行的线程的最大个数
  9. // 信号量通常不会单独使用,一般要结合互斥体或事件
  10. // 会被多个线程访问到的全局变量
  11. long g_Number = 0;
  12. // 创建信号量的函数
  13. // 参数1: 安全属性
  14. // 参数2: 初始的信号个数
  15. // 参数3: 总的信号个数(数量没有限制)
  16. // 参数3: 互斥体的名称,用于打开
  17. HANDLE Semaphore = CreateSemaphore(NULL, 20, 20, L"my_semaphore");
  18. //初始(有n个信号) -> 等待函数(信号n->无信号n-1) -> Release(信号n->信号n+1)
  19. // 线程一: 给 g_Number 自增 100000 次
  20. DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter)
  21. {
  22. for (int i = 0; i < 100000; i++)
  23. {
  24. // 进入受保护的代码(将信号量的信号数量 - 1)
  25. WaitForSingleObject(Semaphore, INFINITE);
  26. // 需要被保护的代码
  27. g_Number++;
  28. // 参数2: 将信号数量加上价格
  29. // 参数3: 增加信号之前,一共由多少个信号,NULL 表示不需要知道
  30. ReleaseSemaphore(Semaphore, 1, NULL);
  31. // 将信号量的信号数量 + 1
  32. }
  33. return 0;
  34. }
  35. // 线程二: 给 g_Number 自增 100000 次
  36. DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter)
  37. {
  38. for (int i = 0; i < 100000; i++)
  39. {
  40. WaitForSingleObject(Semaphore, INFINITE);
  41. g_Number++;
  42. ReleaseSemaphore(Semaphore, 1, NULL);
  43. }
  44. return 0;
  45. }
  46. // 输出参数
  47. DWORD WINAPI ThreadPro3(LPVOID lpThreadParameter)
  48. {
  49. while (true)
  50. {
  51. WaitForSingleObject(Semaphore, INFINITE);
  52. printf("%d\n", lpThreadParameter);
  53. Sleep(1000);
  54. ReleaseSemaphore(Semaphore, 1, NULL);
  55. }
  56. return 0;
  57. }
  58. int main()
  59. {
  60. // 控制线程的数量
  61. HANDLE Threads[1000] = { 0 };
  62. for (int i = 0; i < 1000; ++i)
  63. Threads[i] = CreateThread(NULL, NULL, ThreadPro3, (LPVOID)(i + 1), NULL, NULL);
  64. WaitForMultipleObjects(1000, Threads, true, INFINITE);
  65. // 线程的互斥
  66. HANDLE hThread1 = NULL, hThread2 = NULL;
  67. hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL);
  68. hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL);
  69. if (hThread1 != NULL && hThread2 != NULL)
  70. {
  71. WaitForSingleObject(hThread1, INFINITE);
  72. WaitForSingleObject(hThread2, INFINITE);
  73. }
  74. printf("%d", g_Number);
  75. system("pause");
  76. return 0;
  77. }