异步选择模型是基于窗口实现

窗口的创建如下:

1.创建窗口结构体

typedef struct tagWNDCLASSEXW {UINT      cbSize;UINT      style;WNDPROC   lpfnWndProc;int       cbClsExtra;int       cbWndExtra;HINSTANCE hInstance;HICON     hIcon;HCURSOR   hCursor;HBRUSH    hbrBackground;LPCWSTR   lpszMenuName;LPCWSTR   lpszClassName;HICON     hIconSm;
} WNDCLASSEXW, *PWNDCLASSEXW, *NPWNDCLASSEXW, *LPWNDCLASSEXW;

2.注册窗口结构体

ATOM RegisterClassExA(const WNDCLASSEXA *unnamedParam1
);

3.创建窗口

HWND CreateWindowExA(DWORD     dwExStyle,LPCSTR    lpClassName,LPCSTR    lpWindowName,DWORD     dwStyle,int       X,int       Y,int       nWidth,int       nHeight,HWND      hWndParent,HMENU     hMenu,HINSTANCE hInstance,LPVOID    lpParam
);

4.显示窗口

BOOL ShowWindow(HWND hWnd,int  nCmdShow
);

5.更新窗口

BOOL UpdateWindow(HWND hWnd
);

6.消息循环

// 获取消息
BOOL GetMessage(LPMSG lpMsg,HWND  hWnd,UINT  wMsgFilterMin,UINT  wMsgFilterMax
);
// 翻译消息
BOOL TranslateMessage(const MSG *lpMsg
);
// 分发消息
LRESULT DispatchMessage(const MSG *lpMsg
);

7.回调函数

LRESULT LRESULT DefWindowProcW(HWND   hWnd,UINT   Msg,WPARAM wParam,LPARAM lParam
);

服务端代码:(桌面应用程序)

/*
* 注意事项:1.创建的工程是窗口2.要将当前项目改成多字符集,要不然接收客户端发来的信息时,会出现乱码TCP/IP网络模型之异步选择模型是基于窗口实现的异步选择模型核心:消息队列:操作系统为每个窗口创建一个消息队列并且维护所以想要使用消息队列,就必须创建一个窗口异步选择模型步骤:1.将socket绑定到一个消息上,并投递给系统,由系统帮我们进行监视WSAAsyncSelect2.创建socket数组,将所有的socket装进数组中,并定义一个变量进行记录装进socket数组的个数3.通过回调函数中的参数2处理分类处理消息4.通过参数3获取socket5.通过参数4中的高位字节获取对应的错误码 HOWORD(lParam)6.通过参数4中的低位字节获取对应的消息,进行分类处理事件 FD_ACCEPT FD_READ FD_WRITE FD_CLOSE网络模型中的 异步选择模型与事件选择模型的机制比较:异步选择模型是处理 消息队列 基于消息机制事件选择模型是处理 事件集合 基于事件机制
*/#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")// 自定义 函数 WSAAsyncSelect() 函数中参数3所需的宏 消息
#define UM_EVENTSELECTMSG WM_USER + 1  // WM_USER + 1 表示防止自定义的消息ID与系统的消息ID冲突// 定义客户区HDC 中的y坐标
int y = 0; // 每一次使用之后要 y += 15 防止数据覆盖在一起
// 创建socket数组用于装所有的socket 定义成全局的,方便使用
SOCKET g_allsocks[1024];
// 定义记录将socket往socket数组装的个数变量
int g_count;/*LRESULT CALLBACK WindowProc(_In_ HWND   hwnd,   hwnd获取可操作的客户区 HDC 用于TextOut所需的参数1_In_ UINT   uMsg,   处理uMsg参数传入的消息 进行分类处理消息_In_ WPARAM wParam, 处理通过wParam传入的socket_In_ LPARAM lParam  处理lParam,通过HIWORD(lParam)处理错误 通过LOWORD(lParam)进行分类处理事件FD_ACCEPT FD_READ FD_WRITE FD_CLOSE
);*/
// 声明窗口结构体中成员所需的回调函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);// 将打开网络库、检验版本、创建socket、绑定地址与端口号、开始监听 封装成函数
SOCKET createSocket(HWND hwnd);// 窗口的主函数是WinMain
// 参数1:当前窗口的实例句柄(实例句柄是应用程序的名字或者叫ID编号),
// 参数2:上一个的实例句柄,已经无效,在这里只起到占位作用
// 参数3:命令行参数,
// 参数4:显示方式(窗口是最小化显示还是其他方式显示)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrehInstance, LPSTR lpCmdLine, int nShowCmd)
{// 1.创建窗口的结构体WNDCLASSEX ws;// 给窗口结构体成员赋值ws.cbClsExtra = 0;                                            // 窗口类的额外空间ws.cbSize = sizeof(ws);                                     // 窗口结构体大小ws.lpfnWndProc = WindowProc;                             // 回调函数ws.hInstance = hInstance;                                   // 当前窗口的实例句柄 填WinMain中的参数1ws.hCursor = NULL;                                           // 光标(鼠标的样式)ws.lpszClassName = "基于窗口的异步选择模型";                  // 窗口类名 显示窗口左上角的名字ws.cbWndExtra = 0;                                           // 当前窗口的额外空间 ws.hbrBackground = CreateSolidBrush(RGB(227, 231, 249));  // 背景颜色ws.hIcon = NULL;                                            // 左上角的图标ws.hIconSm = NULL;                                            // 窗口图标,程序的图标(显示在桌面下的图标)ws.lpszMenuName = NULL;                                       // 窗口菜单名ws.style = CS_HREDRAW | CS_VREDRAW;                            // 窗口移动之后的样式:水平刷新和垂直刷新// 2.注册窗口结构体RegisterClassEx(&ws);// 3.创建窗口 CreateWindowEx();// 参数1:窗口额外的风格, 参数2:窗口类的名字,注意参数2必须和上面的lpszClassName窗口类名一致,如果不一致会出Bug 点击运行打不开窗口// 参数3:窗口名字, 参数4:窗口基本的风格// 参数5,6:窗口的起始位置, 参数7,8:窗口宽高,参数9:父窗口,参数10:菜单栏,参数11:当前窗口实例句柄,参数12:回调函数的参数HWND hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "基于窗口的异步选择模型", "TCP/IP-服务端", WS_OVERLAPPEDWINDOW,100, 0, 500, 1000, NULL, NULL, hInstance, NULL);// 判断是否成功创建窗口if (NULL == hwnd){return 0;}// 4.显示窗口ShowWindow(hwnd, nShowCmd);// 5.更新窗口UpdateWindow(hwnd);// 创建客户区可操作的HDC 用完之后记得要释放HDC hdc = GetDC(hwnd);// 在创建窗口进行消息循环之前调用封装好的创建socket等网络步骤SOCKET socketServer = createSocket(hwnd);// 7.将socket绑定到一个消息上,并投递给系统 WSAAsyncSelect();/* int WSAAsyncSelect(SOCKET s,      参数1:要绑定的socketHWND   hWnd,   参数2:创建窗口类型,标识在网络事件发生时要接收的窗口句柄u_int  wMsg, 参数3:自定义信号,用于在回调函数中参数 (UINT uMsg) 要进行处理的自定义消息long   lEvent 参数4:要处理的网络事件,FD_ACCEPT FD_READ FD_WRITE FD_CLOSE 这里因为绑定的是服务端的socket,所以事件只填FD_ACCEPT);*/int nAsyncSel = WSAAsyncSelect(socketServer, hwnd, UM_EVENTSELECTMSG, FD_ACCEPT);// 检验是否成功绑定事件并投递给系统if (SOCKET_ERROR == nAsyncSel){int a = WSAGetLastError();// 将int类型转成char 类型char buf[10] = { 0 };_itoa(a, buf, 10);TextOut(hdc, 0, y, buf, strlen(buf));//释放socketclosesocket(socketServer);//清理网络库WSACleanup();return 0;}// 将socket装进到socket集合中g_allsocks[g_count] = socketServer;g_count++;    // 注意个数要++// 声明GetMessage函数中参数1所需的消息MSG msg;// 6.消息循环 注意GetMessage函数如果参数2填的是创建窗口的类型 hwnd 的话关闭当前窗口当前应用程序没有被关闭// 进程没有结束,还在运行,所以要参数2要填NULLwhile (GetMessage(&msg, NULL, 0, 0)){// 翻译消息 将消息进行处理一下TranslateMessage(&msg);// 分发消息 再将消息变量msg传给windows,让windows来调用消息处理函数DispatchMessage(&msg);}// 释放客户区HDCReleaseDC(hwnd, hdc);return 0;
}// 实现窗口结构体中成员所需的回调函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{// hwnd获取可操作的客户区 HDC 用于TextOut所需的参数1HDC hdc = GetDC(hwnd);    // 记得用完之后要释放// 分类处理通过uMsg参数传入的事件switch (uMsg){case UM_EVENTSELECTMSG:   // 处理自定义事件{// 获取通过参数wParam 传入的socketSOCKET sock = (SOCKET)wParam;// 获取消息 通过参数lParam传递进来的消息// LOWORD(lParam);   低位存具体的消息// HIWORD(lParam);  高位存对应的错误码// 1.通过HIWORD(lParam)判断是否出错if (0 != HIWORD(lParam)){if (WSAECONNABORTED == HIWORD(lParam)){// 处理客户端下线操作TextOut(hdc, 0, y, "client close!!!", strlen("client close!!!"));y += 15;// 关闭对应的socket上的消息,直接将函数WSAAsyncSelect();中的后两个参数置0就可以WSAAsyncSelect(sock, hwnd, 0, 0);// 从socket数组中删除对应的socket for (int i = 0; i < g_count; i++){// 从socket数组中找sock if (sock == g_allsocks[i]){// 删除对应的sock在数组中的对应的元素 可以直接将最后第一个数组元素赋给当前sock在数组所在的位置,也就是覆盖g_allsocks[i] = g_allsocks[g_count - 1];// 注意记录的数组个数要--g_count--;break;}}}break;}// 2.通过LOWORD(lParam)获取具体的消息 // 分类处理事件 FD_ACCEPT FD_READ FD_WRITE FD_CLOSEswitch (LOWORD(lParam)){case FD_ACCEPT:       // 处理FD_ACCEPT{// 调用accept函数接收连接 本质创建客户端socket 注意这里的sock 是服务端的socketSOCKET socketClient = accept(sock, NULL, NULL);// 检验是否成功创将客户端socketif (INVALID_SOCKET == socketClient){int a = WSAGetLastError();break;}// 将成功创建出来的客户端socket绑定相应的消息,并投递给系统int retAsyncSel =  WSAAsyncSelect(socketClient, hwnd, UM_EVENTSELECTMSG, FD_READ | FD_WRITE | FD_CLOSE);// 检验是否成功绑定并投递if (SOCKET_ERROR == retAsyncSel){// 绑定出错int a = WSAGetLastError();// 关闭成功创建出来的socketclosesocket(socketClient);break;}// 打印成功调用accept创建客户端socket的提示TextOut(hdc, 0, y, "accept ok", strlen("accept ok"));y += 15;// 将成功绑定并投递的客户端socket装进到socket数组中,方便之后释放使用g_allsocks[g_count] = socketClient;g_count++;   // 注意个数要++}break;case FD_READ:        // 处理FD_READ{// 调用recv函数读取客户端发来的信息// 定义接收数据的缓存区字符数组char recvBuf[1024] = { 0 };int retRecv = recv(sock, recvBuf, sizeof(recvBuf), 0);  // 注意这里的sock表示的是客户端的socket// 判断是否成功接收消息if (SOCKET_ERROR == retRecv){// 接收消息出错int a = WSAGetLastError();TextOut(hdc, 0, y, "处理FD_READ 调用recv出错", strlen("处理FD_READ 调用recv出错"));y += 15;break;}// 打印接收到客户端发来的信息TextOut(hdc, 0, y, recvBuf, strlen(recvBuf));y += 15;}break;case FD_WRITE:      // 处理FD_WRITE// 可以直接send消息个客户端// 这里直接打印write ok 表示可以进行sendTextOut(hdc, 0, y, "write ok", strlen("write ok"));y += 15;break;case FD_CLOSE:     // 处理FD_CLOSE// 处理客户端下线操作TextOut(hdc, 0, y, "client close!!!", strlen("client close!!!"));y += 15;// 关闭对应的socket上的消息,直接将函数WSAAsyncSelect();中的后两个参数置0就可以WSAAsyncSelect(sock, hwnd, 0, 0);// 从socket数组中删除对应的socket for (int i = 0; i < g_count; i++){// 从socket数组中找sock if (sock == g_allsocks[i]){// 删除对应的sock在数组中的对应的元素 可以直接将最后第一个数组元素赋给当前sock在数组所在的位置,也就是覆盖g_allsocks[i] = g_allsocks[g_count - 1];// 注意记录的数组个数要--g_count--;break;}}break;}}break;case WM_CREATE: // 初始化,只执行一次break;case WM_DESTROY:   // 销毁窗口PostQuitMessage(0);  // 终止线程请求,并清理内存break;}// 释放hdcReleaseDC(hwnd, hdc);// 注意DefWindowProc函数参数要和上面WindowProc函数中的参数一一对应return DefWindowProc(hwnd, uMsg, wParam, lParam);
}// 将打开网络库、检验版本、创建socket、绑定地址与端口号、开始监听 封装成函数
// 传入的参数用于创建客户区可操作的 HDC
SOCKET createSocket(HWND hwnd)
{// 创建客户区可操作的HDC 用完之后记得要释放HDC hdc = GetDC(hwnd);//1.打开网络库WORD wVersion = MAKEWORD(2, 2);//打开2.2版本WSADATA WSAData;int nRes = WSAStartup(wVersion, &WSAData);//判断WSAStartup函数的返回值if (0 != nRes){switch (nRes){case WSASYSNOTREADY:// 注意窗口用printf 打印不了 要使用 TextOut//printf("请重新启动电脑试试,或者检查网络库\n");TextOut(hdc, 0, y, "请重新启动电脑试试,或者检查网络库", strlen("请重新启动电脑试试,或者检查网络库"));y += 15;break;case WSAVERNOTSUPPORTED://printf("请更新网络库\n");TextOut(hdc, 0, y, "请更新网络库", strlen("请更新网络库"));y += 15;break;case WSAEINPROGRESS://printf("请重新启动\n");TextOut(hdc, 0, y, "请重新启动", strlen("请重新启动"));y += 15;break;case WSAEPROCLIM://printf("请关闭不用的软件,给网络运行提供充足的资源\n");TextOut(hdc, 0, y, "请关闭不用的软件,给网络运行提供充足的资源", strlen("请关闭不用的软件,给网络运行提供充足的资源"));y += 15;break;}return 0;}//2、检验版本if (2 != HIBYTE(WSAData.wVersion) || 2 != LOBYTE(WSAData.wVersion)){int a = WSAGetLastError();// 将int类型转成char 类型char buf[10] = { 0 };// 将int类型转成char类型的字符串_itoa(a, buf, 10);TextOut(hdc, 0, y, buf, strlen(buf));//清理网络库WSACleanup();return 0;}//3.创建socketSOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//判读socket的返回值if (INVALID_SOCKET == socketServer){int a = WSAGetLastError();// 将int类型转成char 类型char buf[10] = { 0 };_itoa(a, buf, 10);TextOut(hdc, 0, y, buf, strlen(buf));y += 15;//清理网络库WSACleanup();return 0;}//4.绑定地址与端口struct sockaddr_in si;si.sin_family = AF_INET;si.sin_port = htons(12345);si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si))){int a = WSAGetLastError();// 将int类型转成char 类型char buf[10] = { 0 };_itoa(a, buf, 10);TextOut(hdc, 0, y, buf, strlen(buf));y += 15;//释放socketclosesocket(socketServer);//清理网络库WSACleanup();return 0;}//5.开始监听if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)){int a = WSAGetLastError();// 将int类型转成char 类型char buf[10] = { 0 };_itoa(a, buf, 10);TextOut(hdc, 0, y, buf, strlen(buf));//释放socketclosesocket(socketServer);//清理网络库WSACleanup();return 0;}// 释放客户区HDCReleaseDC(hwnd, hdc);return socketServer;
}

客户端代码:(控制台应用程序)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Winsock.h>
#pragma comment(lib,"ws2_32.lib")WSADATA WSAData;
// 1,打开网络库
void OpenWSAStarup()
{// 1,打开网络库WORD version = MAKEWORD(2, 2);   //设置要使用的库的版本 MAKEWORD(主版本,副版本); int n = WSAStartup(version, &WSAData);// 检验是否成功打开网络库if (0 != n){// 打开网络库失败,通过n 返回的错误码,给出提示switch (n){case WSASYSNOTREADY:printf("重启电脑试试,或者检查ws_2_32库是否存在\n");break;case WSAVERNOTSUPPORTED:printf("当前库版本号不支持,尝试更换版本试试\n");break;case WSAEPROCLIM:printf("已达到对Windows套接字实现支持的任务数量的限制\n");break;case WSAEINPROGRESS:printf("正在阻止Windows Sockets 1.1操作,当前函数运行期间,因为某些原因造成阻塞\n");break;case WSAEFAULT:printf("lpWASData参数不是有效的指针,参数写错了\n");break;default:break;}}
}
// 2.检验版本
void CheckVersion()
{if (2 != HIBYTE(WSAData.wVersion) || 2 != LOBYTE(WSAData.wVersion)){// 打开版本号 2.2 失败printf("打开版本号 2.2 失败, 正在退出程序...\n");// 关闭网络库WSACleanup();// 退出程序return;}
}
// 3.创建socket
SOCKET CreateSocket()
{// 3.创建套接字 socket 创建服务端的socketSOCKET socketCreate = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 检验是否成功创建套接字if (INVALID_SOCKET == socketCreate){// 创建套接字失败// 获取错误码int error = WSAGetLastError();printf("创建套接字失败,返回错误号为:%d\n", error);// 关闭网络库WSACleanup();// 退出程序return 0;}return socketCreate;
}
// 4.连接服务器
void Connection(SOCKET socket_)
{// 4.连接到服务器// 创建connect() 函数中参数2所需的结构体 用来装IP地址和端口号sockaddr_in client_s;//给结构体成员赋值client_s.sin_family = AF_INET;       //IP地址类型client_s.sin_port = htons(12345);  //端口号client_s.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");int nCon = connect(socket_, (struct sockaddr*)&client_s, sizeof(client_s));// 检验是否成功连接到服务端if (SOCKET_ERROR == nCon){// 连接服务端失败// 获取错误码int error = WSAGetLastError();printf("连接服务端失败,返回错误号为:%d\n", error);// 关闭套接字closesocket(socket_);// 关闭网络库WSACleanup();// 退出程序return;}
}// 5.与服务端发送信息
void RecvAndSend(SOCKET socket_)
{// 客户端只进行发送消息,因为服务端代码没有给客户端send消息,所以就没有进行接收消息处理printf("我是客户端,正在发送信息,请稍等...\n");// 5.与服务端进行收发信息while (1){// 发信息char sendBuf[1024] = { 0 };scanf_s("%s", sendBuf, 1024);// 设置输入 0 时自动退出if ('0' == sendBuf[0]){break;    //退出循环}send(socket_, sendBuf, 1024, 0);}
}int main()
{// 1,打开网络库OpenWSAStarup();// 2.校验版本CheckVersion();// 3.创建socketSOCKET socketServer = CreateSocket();// 4.连接服务器Connection(socketServer);// 5.与服务端发送信息RecvAndSend(socketServer);// 关闭套接字closesocket(socketServer);// 关闭网络库WSACleanup();system("pause");return 0;
}

程序运行结果:

TCP/IP网络模型之异步选择模型相关推荐

  1. 【网络】之TCP/IP 网络模型有哪几层

    TCP/IP 网络模型有哪几层 对于同一台设备上的进程间通信,有很多种方式,比如有管道.消息队列.共享内存.信号等方式,而对于不同设备上的进程间通信,就需要网络通信,而设备是多样性的,所以要兼容多种多 ...

  2. 【TCP/IP网络模型】TCP/IP网络模型的四层结构

    TCP/IP网络模型的目的是什么呢? 为了应用不同设备之间的通信,我sdf们就需要一套通用的网络协议,就是TCP/IP网络模型. 如下图1所示: 图1 TCP/IP的四层协议 应用层 应用层协议定义的 ...

  3. TCP/IP 网络模型

    前言 互联网是怎么构成的,又是怎么运作的?什么是 TCP/IP 网络?为什么远隔万里的计算机可以互相通信?计算机网络作为 IT 行业的基石,是工程师永远绕不开的话题. 计算机网络的分层体系结构 计算机 ...

  4. 一文读懂 TCP/IP 网络模型

    本文是网络转载原文链接: https://www.toutiao.com/i6819659519954518541/? 简介 互联网是怎么构成的,又是怎么运作的?什么面试官老喜欢问 TCP/IP 网络 ...

  5. tcp硬件校验和rxtx开启是啥意思_一文读懂 TCP/IP 网络模型

    前言 互联网是怎么构成的,又是怎么运作的?什么面试官老喜欢问 TCP/IP 网络?为什么远隔万里的计算机可以互相通信?计算机网络作为 IT 行业的基石,是工程师永远绕不开的话题. 网络的分层体系结构 ...

  6. TCP/IP网络模型

    TCP/IP网络模型 计算机与网络设备需要相互通信,双方就必须基于相同的方法.如何探测到目标.由哪一边先发起通信.使用哪种语言进行通信.怎样结束通信等规则都需要提前确定.不同的硬件.不同的操作系统之间 ...

  7. 【网络编程】基于TCP/IP协议的C/S模型

    相关视频--C3程序猿-windows网络编程:第一部分tcp/ip 我的小站--半生瓜のblog 基于TCP/IP协议的C/S模型 基于TCP/IP协议的C/S模型 TCP/IP协议 Client/ ...

  8. 计算机网络漫谈:OSI七层模型与TCP/IP四层(参考)模型(转载)

    PS:原文写的太好了,忍不住转载了 <计算机网络漫谈:OSI七层模型与TCP/IP四层(参考)模型> 文章目录 一.七层?四层? 1.为什么需要协议? 2.OSI七层模型是干什么的? 3. ...

  9. 详解TCP/IP网络模型

    目录 第一层:物理层 第二层(数据链路层) 第三层(网络层) 第四层(传输层) 第五层(应用层) 网络模型有好几种,其中最常讨论的有俩种:OSI七层参考模型.TCP/IP四层参考模型.OSI模型是国际 ...

最新文章

  1. webgl值得重视的基础构建
  2. python做图像识别好还是c++好_OpenCV人脸检测(C++/Python)
  3. Launcher结构之home screen
  4. mysql having关键字可以对group by后的结果再进行筛选
  5. OpenCV-计时函数cv::getTickCountcv::getTickFrequency
  6. ora-00119和ora-00132解决方案
  7. 第二季-专题14-串口来做控制台
  8. java学士后课程_java学士后课程
  9. 【技术沙龙】星火计划 | 腾讯自研Kona JDK技术分享沙龙火热报名中
  10. android扇形动画弹出icon的功能
  11. 编写程序,输出如下图形2
  12. wow转服务器微信支付,魔兽打团本成就,打到一半,发微信支付宝要钱,真国服之耻!...
  13. Arcgis利用dem数据生成等高线
  14. ROMP:Monocular, One-stage, Regression of Multiple 3D People
  15. 你的团队需要一个会讲故事的人读书笔记
  16. 【全栈开发实战小草看书之开篇】
  17. 【Qt5开发及实例】16、实现一个简单的文本编辑器(over)
  18. 用YOLO玩「吃鸡」?搭载AI的自瞄外挂来了!一枪爆头!又快又准...
  19. 对于大部分Java开发者说“60W年薪是无法逾越沟壑”你赞同吗?
  20. 傲腾内存不支持linux吗,英特尔一面优化傲腾可持续内存性能 一面不忘科普

热门文章

  1. STM VCP移植笔记
  2. 华为云服务器还需要确定位置吗,有了云服务器还需要主机吗
  3. wxpython中表格顶角怎么设置_wxpython编程之 grid(数据表格) | 学步园
  4. 安装linux系统提示acpi,安装Linux系统时的ACPI和APIC问题
  5. 如何使文字和图片垂直居中对齐
  6. Meeting Rooms II -- LeetCode
  7. turtle乌龟模块画长方形
  8. 为上次渲染的三角形添加颜色
  9. DSS 代码分析【启动、初始化流程】
  10. Android下载图片并添加图片水印