当前位置:网站首页>socket做的简单网络嗅探器
socket做的简单网络嗅探器
2022-04-21 13:51:00 【Red fog】
SniffDlg.h : 头文件
// SniffDlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#include "afxwin.h"
typedef struct _PROTN2T
{
int proto;
char *pprototext;
}PROTN2T;
#define PROTO_NUM 11
/* The IP header */
typedef struct _IPHEADER {
unsigned char header_len : 4;
unsigned char version : 4;
unsigned char tos; // type of service
unsigned short total_len; // length of the packet
unsigned short ident; // unique identifier
unsigned short flags;
unsigned char ttl;
unsigned char proto; // protocol ( IP , TCP, UDP etc)
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IPHEADER;
#define UDP_HEAD_LEN 8 /* UDP head length */
#define PSEUDO_HEAD_LEN 12 /* Pseudo head length */
#define ICMP_HEAD_LEN 4 /* ICMP head length */
struct TCPPacketHead {
WORD SourPort;
WORD DestPort;
DWORD SeqNo;
DWORD AckNo;
BYTE HLen;
BYTE Flag;
WORD WndSize;
WORD ChkSum;
WORD UrgPtr;
};
struct ICMPPacketHead {
BYTE Type;
BYTE Code;
WORD ChkSum;
};
struct UDPPacketHead {
WORD SourPort;
WORD DestPort;
WORD Len;
WORD ChkSum;
};
// CSniffDlg 对话框
class CSniffDlg : public CDialogEx
{
// 构造
public:
CSniffDlg(CWnd* pParent = NULL); // 标准构造函数
void AddData(CString s0, CString s1, CString s2, CString s3, CString s4, CString s5, CString s6);
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum {
IDD = IDD_SNIFF_DIALOG };
#endif
CListCtrl m_ctrList;
CButton m_start;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
DWORD m_ipsource; // IP in net format
DWORD m_iphostsource; // same IP in host format
DWORD m_iphost;
DWORD m_ipcheckedhost;
SOCKET m_s;
DWORD m_threadID;
BOOL m_Multihomed;
BOOL m_Local;
CDWordArray m_IPArr;
friend UINT threadFunc(LPVOID p);
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedLookup();
afx_msg void OnBnClickedOk();
private:
CComboBox m_cAdapter;
public:
afx_msg void OnSelendokComboAdapter();
};
SniffDlg.cpp : 实现文件
// SniffDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "Sniff.h"
#include "SniffDlg.h"
#include "afxdialogex.h"
#pragma comment(lib, "IPHLPAPI.lib")
#include "Iphlpapi.h"
#include "mstcpip.h"//SIO_RCVALL
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
PROTN2T aOfProto[PROTO_NUM + 1] =
{
{
IPPROTO_IP , "IP" },
{
IPPROTO_ICMP , "ICMP" },
{
IPPROTO_IGMP , "IGMP" },
{
IPPROTO_GGP , "GGP" },
{
IPPROTO_TCP , "TCP" },
{
IPPROTO_PUP , "PUP" },
{
IPPROTO_UDP , "UDP" },
{
IPPROTO_IDP , "IDP" },
{
IPPROTO_ND , "NP" },
{
IPPROTO_RAW , "RAW" },
{
IPPROTO_MAX , "MAX" },
{
NULL , "" }
};
char *get_proto_name(unsigned char proto)
{
BOOL bFound = FALSE;
int i;
for (i = 0; i < PROTO_NUM; i++)
{
if (aOfProto[i].proto == proto)
{
bFound = TRUE;
break;
}
}
if (bFound)
return aOfProto[i].pprototext;
return aOfProto[PROTO_NUM].pprototext;
}
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum {
IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CSniffDlg 对话框
CSniffDlg::CSniffDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_SNIFF_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_threadID = 0;
m_Multihomed = FALSE;
m_Local = TRUE;
}
void CSniffDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST, m_ctrList);
DDX_Control(pDX, IDC_LOOKUP, m_start);
DDX_Control(pDX, IDC_COMBO_ADAPTER, m_cAdapter);
}
BEGIN_MESSAGE_MAP(CSniffDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_LOOKUP, &CSniffDlg::OnBnClickedLookup)
ON_BN_CLICKED(IDOK, &CSniffDlg::OnBnClickedOk)
ON_CBN_SELENDOK(IDC_COMBO_ADAPTER, &CSniffDlg::OnSelendokComboAdapter)
END_MESSAGE_MAP()
// CSniffDlg 消息处理程序
BOOL CSniffDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
CHAR szHostName[128] = {
0 };
HOSTENT* pHost = NULL;
CHAR* pszIp = NULL;
int iNum = 0;
if (AfxSocketInit(NULL) == FALSE)
{
AfxMessageBox("Sorry, socket load error!");
return FALSE;
}
if (gethostname(szHostName, 128) == 0)
{
pHost = gethostbyname(szHostName);
if (pHost != NULL)
{
for (pszIp = *pHost->h_addr_list;pszIp != NULL;pszIp = *(pHost->h_addr_list + iNum))
{
m_cAdapter.InsertString(iNum++, inet_ntoa(*(in_addr*)pszIp));
}
pszIp = inet_ntoa(*(in_addr*)pHost->h_addr_list[0]);
m_ipsource = inet_addr(pszIp);
m_cAdapter.SetCurSel(0);
}
else
{
AfxMessageBox("pHost = NULL!");
}
}
else
{
AfxMessageBox("can't find host name!");
}
DWORD dwStyle = GetWindowLong(m_ctrList.GetSafeHwnd(), GWL_STYLE);
dwStyle &= ~LVS_TYPEMASK;
dwStyle |= LVS_REPORT;
SetWindowLong(m_ctrList.GetSafeHwnd(), GWL_STYLE, dwStyle);
m_ctrList.InsertColumn(0, "数据", LVCFMT_LEFT, 525);
m_ctrList.InsertColumn(0, "大小", LVCFMT_LEFT, 80);
m_ctrList.InsertColumn(0, "端口", LVCFMT_LEFT, 40);
m_ctrList.InsertColumn(0, "目的地址", LVCFMT_LEFT, 100);
m_ctrList.InsertColumn(0, "端口", LVCFMT_LEFT, 40);
m_ctrList.InsertColumn(0, "源地址", LVCFMT_LEFT, 100);
m_ctrList.InsertColumn(0, "协议", LVCFMT_LEFT, 50);
::SendMessage(m_ctrList.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
// Here read all IPs of this host
DWORD dwSize = 0;
GetIpAddrTable(NULL, &dwSize, FALSE);
PMIB_IPADDRTABLE pIpAddrTable = (PMIB_IPADDRTABLE)new BYTE[dwSize];
if (pIpAddrTable)
{
if (GetIpAddrTable((PMIB_IPADDRTABLE)pIpAddrTable, // // buffer for IP table
&dwSize, // size of buffer
FALSE // sort by IP address
) == NO_ERROR)
{
if (pIpAddrTable->dwNumEntries > 2) // Second is MS TCP loopback IP ( 127.0.0.1 )
{
m_Multihomed = TRUE;
char szIP[16];
for (int i = 0; i < (int)pIpAddrTable->dwNumEntries; i++)
{
in_addr ina;
ina.S_un.S_addr = pIpAddrTable->table[i].dwAddr;
char *pIP = inet_ntoa(ina);
strcpy(szIP, pIP);
if (stricmp(szIP, "127.0.0.1"))
m_IPArr.Add(pIpAddrTable->table[i].dwAddr);
}
}
}
delete[] pIpAddrTable;
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CSniffDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CSniffDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSniffDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
UINT threadFunc(LPVOID p)
{
CSniffDlg *pDlg = static_cast<CSniffDlg *>(p);
char buf[1000], *bufwork;
MSG msg;
int iRet;
DWORD dwErr;
char *pSource, *pDest;
IPHEADER *pIpHeader;
in_addr ina;
char szSource[16], szDest[16], szErr[50];
char *pLastBuf = NULL;
int HdrLen, totallen;
WORD sourport, destport;
struct TCPPacketHead *pTCPHead;
struct ICMPPacketHead *pICMPHead;
struct UDPPacketHead *pUDPHead;
BYTE *pdata = NULL;
/*---------------------------------------------------------------------*/
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); // Force to make the queue强制为该线程创建消息队列
pDlg->m_threadID = GetCurrentThreadId();
while (TRUE)
{
if (PeekMessage(&msg, 0, WM_CLOSE, WM_CLOSE, PM_NOREMOVE))
{
closesocket(pDlg->m_s);
pDlg->m_threadID = 0;
pDlg->m_start.EnableWindow(TRUE);
break;
}
memset(buf, 0, sizeof(buf));
iRet = recv(pDlg->m_s, buf, sizeof(buf), 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
continue;
}
else
{
if (*buf)
{
bufwork = buf;
pIpHeader = (IPHEADER *)bufwork;
WORD iLen = ntohs(pIpHeader->total_len);
while (TRUE)
{
if (iLen <= iRet)
{
ina.S_un.S_addr = pIpHeader->sourceIP;
pSource = inet_ntoa(ina);
strcpy(szSource, pSource);
ina.S_un.S_addr = pIpHeader->destIP;
pDest = inet_ntoa(ina);
strcpy(szDest, pDest);
CString str, strProto, strSourPort, strDestPort, strData, strSize;
strProto = get_proto_name(pIpHeader->proto);
/*-------------------zhuwei add(2002.11.9)-----------------------------*/
HdrLen = pIpHeader->header_len & 0xf;
HdrLen *= 4;
totallen = ntohs(pIpHeader->total_len);
totallen -= HdrLen;
switch (pIpHeader->proto)
{
case IPPROTO_ICMP:
{
pICMPHead = (struct ICMPPacketHead *)(buf + HdrLen);
//strL4.Format(" type:%d code:%d\n",pICMPHead->Type,pICMPHead->Code);
strSourPort = "-";
strDestPort = "-";
pdata = ((BYTE *)pICMPHead) + ICMP_HEAD_LEN;
totallen -= ICMP_HEAD_LEN;
break;
}
case IPPROTO_TCP:
{
pTCPHead = (struct TCPPacketHead *)(buf + HdrLen);
sourport = ntohs(pTCPHead->SourPort);
destport = ntohs(pTCPHead->DestPort);
//strL4.Format(" sour port:%d,dest port:%d",sourport,destport);
strSourPort.Format("%d", sourport);
strDestPort.Format("%d", destport);
HdrLen = (pTCPHead->HLen) >> 4; //in fact only 4 bits
HdrLen *= 4;
pdata = ((BYTE *)pTCPHead) + HdrLen;
totallen -= HdrLen;
break;
}
case IPPROTO_UDP:
{
pUDPHead = (struct UDPPacketHead *)(buf + HdrLen);
sourport = ntohs(pUDPHead->SourPort);
destport = ntohs(pUDPHead->DestPort);
//strL4.Format(" sour port:%d,dest port:%d",sourport,destport);
strSourPort.Format("%d", sourport);
strDestPort.Format("%d", destport);
pdata = ((BYTE *)pUDPHead) + UDP_HEAD_LEN;
totallen -= UDP_HEAD_LEN;
break;
}
}
if (pIpHeader->proto == IPPROTO_ICMP)
strData.Format("type:%d code:%d data:%s", pICMPHead->Type, pICMPHead->Code, pdata);
else strData.Format(" %s", pdata);
strSize.Format("%d", totallen);
pDlg->AddData(strProto, szSource, strSourPort, szDest, strDestPort, strSize, strData);
if (iLen < iRet)
{
iRet -= iLen;
bufwork += iLen;
pIpHeader = (IPHEADER *)bufwork;
}
else
break; // pIpHeader->total_len == iRet and go out
}
else
{
// read last part of buf. I wrote it , but always recv() read exactly
// the lenght of the packet
int iLast = iLen - iRet;
pLastBuf = new char[iLen];
int iReaden = iRet;
memcpy(pLastBuf, bufwork, iReaden);
iRet = recv(pDlg->m_s, pLastBuf + iReaden, iLast, 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
break;
}
else
{
bufwork = pLastBuf;
pIpHeader = (IPHEADER *)bufwork;
if (iRet == iLast)
iRet = iLen;
else
{
// read all last data
iReaden += iRet;
iLast -= iRet;
while (TRUE)
{
iRet = recv(pDlg->m_s, pLastBuf + iReaden, iLast, 0);
if (iRet == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error recv() = %ld ", dwErr);
break;
}
else
{
iReaden += iRet;
iLast -= iRet;
if (iLast <= 0)
{
/*iRet = iReaden;*/
break;
}
}
} // while
}
}
}
} // while
if (pLastBuf)
delete[] pLastBuf;
}
else
{
AfxMessageBox("No data on network");
continue;
}
}
}
return TRUE;
}
void CSniffDlg::OnBnClickedLookup()
{
char szErr[50], szHostName[MAX_PATH];
DWORD dwErr;
SOCKADDR_IN sa;
gethostname(szHostName, sizeof(szHostName));
m_iphostsource = m_ipsource;
m_ipcheckedhost = ntohl(m_iphost);
if (0 == m_threadID)
{
SetDlgItemText(IDC_LOOKUP, "停止查看!");
m_cAdapter.EnableWindow(false);
}
else
{
if (m_threadID)
{
PostThreadMessage(m_threadID, WM_CLOSE, 0, 0);
SetDlgItemText(IDC_LOOKUP, "开始查看!");
m_start.EnableWindow(FALSE);
m_cAdapter.EnableWindow(true);
}
return;
}
DWORD dwBufferLen[10];
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
m_s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (INVALID_SOCKET == m_s)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error socket() = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
int rcvtimeo = 5000;
if (setsockopt(m_s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&rcvtimeo, sizeof(rcvtimeo)) == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error WSAIoctl = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(7000);
sa.sin_addr.s_addr = m_iphostsource;
if (bind(m_s, (PSOCKADDR)&sa, sizeof(sa)) == SOCKET_ERROR)
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error bind() = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
if (SOCKET_ERROR != WSAIoctl(m_s, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),
&dwBufferLen, sizeof(dwBufferLen),
&dwBytesReturned, NULL, NULL))
AfxBeginThread(threadFunc, (LPVOID)this);
else
{
dwErr = WSAGetLastError();
sprintf(szErr, "Error WSAIoctl = %ld ", dwErr);
AfxMessageBox(szErr);
closesocket(m_s);
return;
}
}
void CSniffDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
if (NULL != m_threadID)
{
PostThreadMessage(m_threadID, WM_CLOSE, 0, 0);
}
if (m_IPArr.GetSize())
{
m_IPArr.RemoveAll();
}
CDialogEx::OnOK();
}
void CSniffDlg::AddData(CString s0, CString s1, CString s2, CString s3, CString s4, CString s5, CString s6)
{
int index;
index = m_ctrList.InsertItem(0, s0);
m_ctrList.SetItem(index, 1, LVIF_TEXT, s1, 0, 0, 0, 0);
m_ctrList.SetItem(index, 2, LVIF_TEXT, s2, 0, 0, 0, 0);
m_ctrList.SetItem(index, 3, LVIF_TEXT, s3, 0, 0, 0, 0);
m_ctrList.SetItem(index, 4, LVIF_TEXT, s4, 0, 0, 0, 0);
m_ctrList.SetItem(index, 5, LVIF_TEXT, s5, 0, 0, 0, 0);
m_ctrList.SetItem(index, 6, LVIF_TEXT, s6, 0, 0, 0, 0);
}
void CSniffDlg::OnSelendokComboAdapter()
{
CHAR pszIp[100];
int index=m_cAdapter.GetCurSel();
m_cAdapter.GetLBText(index, pszIp);
m_ipsource = inet_addr(pszIp);
}
DWORD_PTR a;
Sniff.rc
IDD_SNIFF_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "Sniff嗅探器"
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
CONTROL "",IDC_LIST,"SysListView32",LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,15,31,291,135
PUSHBUTTON "查看",IDC_LOOKUP,109,172,45,14
PUSHBUTTON "确定",IDOK,178,172,45,14
COMBOBOX IDC_COMBO_ADAPTER,15,14,291,86,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END
版权声明
本文为[Red fog]所创,转载请带上原文链接,感谢
https://blog.csdn.net/BooleanWater/article/details/106590893
边栏推荐
- 栈概念 转化为循环 括号匹配 逆波兰表达式 模拟实现 干货满满
- Zabbix5 series - monitoring Hikvision camera (VII)
- Number of objects in esgyndb SQL statistics mode
- Shandong University project training raspberry pie promotion plan phase II (III) SSH Remote Connection
- 如何在机房限制学生端摆脱老师的控制,实现自由上网
- Chapter IV key points for implementation of password application security assessment in commercial password application and security assessment - Summary
- < 2021SC@SDUSC > Application and practice of software engineering in Shandong University jpress code analysis (9)
- Oracle backup failed with error ora-04063: View sys KU_ RADM_ FPTM_ VIEW has errors
- 数据库管理工具
- Unittest unit test (III)
猜你喜欢

Zabbix5系列-接入Grafana面板 (十七)

《商用密码应用与安全性评估》第四章 密码应用安全性评估实施要点-小结

园区网架构

< 2021SC@SDUSC > Application and practice of software engineering in Shandong University jpress code analysis (6)

Software engineering - Fundamentals

Chapter IV key points for implementation of password application security assessment in commercial password application and security assessment - Summary

Zabbix5系列-创建自动发现模板 (十六)

Pits to be avoided when Networkx and pyg calculate degree degree: self loop and multiple edges

Deep analysis of JVM bytecode file structure

如何在机房限制学生端摆脱老师的控制,实现自由上网
随机推荐
Zabbix5 series - sound alarm, mail alarm (XIV)
<2021SC@SDUSC>山东大学软件工程应用与实践JPress代码分析(三)
软件测试常见问题 开发模型 PC端qq登录测试用例 BUG的相关问题 测试用例设计的常用方法
Unittest unit test (V)
volatile 关键字
ForkJoin
Summary of several problems of esgyndb using JDBC UDR to access remote trafodion
networkx与PyG计算度数degree时需避免的坑:自环selfloop和多重边
网络端口号和协议号(大全)
Synchronized of concurrent locking mechanism
ssh服务器--密钥认证
墨墨背单词--通过安装包提取它的所有离线单词
< 2021SC@SDUSC Software engineering application and practice of Shandong University jpress code analysis (14)
2021-10-21软件测试理论
数据库基础篇
Number of objects in esgyndb SQL statistics mode
山东大学项目实训树莓派提升计划二期(八)数组和ArrayList
<2021SC@SDUSC>山东大学软件工程应用与实践JPress代码分析(十四)
Zabbix5系列-监控华为、H3C交换机(snmpv2c/snmpv3/snmptrap) (二)
vite. Config configuration file