1、课程目标
- 掌握获取本机网络配置信息
- 学会使用Winsock初始化
- 理解主机名和IP地址转换
- 获取网络适配器信息
2、名词解释
| 术语 |
解释 |
| Winsock |
Windows Sockets API,网络编程接口 |
| WSAStartup |
Winsock初始化函数 |
| gethostname |
获取本机主机名 |
| gethostbyname |
通过主机名获取IP |
| getaddrinfo |
协议无关的地址解析 |
3、使用工具
| 工具 |
用途 |
| Visual Studio |
编译调试 |
| ipconfig |
查看网络配置 |
| Wireshark |
网络抓包 |
4、技术原理
4.1、Winsock架构
┌─────────────────────────────────────────────────────────────┐
│ Winsock架构 │
│ │
│ 应用程序 │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ Winsock2 │ ← ws2_32.dll │
│ │ API │ │
│ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ SPI │ ← Service Provider Interface │
│ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ TCP/IP │ ← 协议驱动 │
│ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ 网卡驱动 │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
5、代码实现
5.1、示例1:Winsock初始化
// WinsockInit.c - Winsock初始化
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
// Winsock初始化
BOOL InitializeWinsock() {
WSADATA wsaData;
int result;
// 请求版本2.2
result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
printf("WSAStartup failed: %d\n", result);
return FALSE;
}
// 验证版本
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Winsock version 2.2 not available\n");
WSACleanup();
return FALSE;
}
printf("Winsock initialized successfully\n");
printf("Version: %d.%d\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
printf("Description: %s\n", wsaData.szDescription);
printf("Status: %s\n", wsaData.szSystemStatus);
return TRUE;
}
// Winsock清理
void CleanupWinsock() {
WSACleanup();
printf("Winsock cleaned up\n");
}
int main() {
if (!InitializeWinsock()) {
return 1;
}
// 网络操作...
CleanupWinsock();
return 0;
}
5.2、示例2:获取主机名和IP
// HostInfo.c - 获取主机信息
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <iphlpapi.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
// 获取本机主机名
void GetLocalHostName() {
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0) {
printf("Hostname: %s\n", hostname);
} else {
printf("gethostname failed: %d\n", WSAGetLastError());
}
}
// 获取本机IP地址(旧API)
void GetLocalIPOld() {
char hostname[256];
struct hostent* host;
if (gethostname(hostname, sizeof(hostname)) != 0) {
return;
}
host = gethostbyname(hostname);
if (host == NULL) {
printf("gethostbyname failed: %d\n", WSAGetLastError());
return;
}
printf("Host name: %s\n", host->h_name);
// 遍历所有IP地址
for (int i = 0; host->h_addr_list[i] != NULL; i++) {
struct in_addr addr;
memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr));
printf("IP Address %d: %s\n", i, inet_ntoa(addr));
}
}
// 获取本机IP地址(新API)
void GetLocalIPNew() {
char hostname[256];
struct addrinfo hints, *result, *ptr;
char ipstr[INET6_ADDRSTRLEN];
if (gethostname(hostname, sizeof(hostname)) != 0) {
return;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // IPv4或IPv6
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(hostname, NULL, &hints, &result);
if (ret != 0) {
printf("getaddrinfo failed: %d\n", ret);
return;
}
printf("\nHost: %s\n", hostname);
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
void* addr;
char* ipver;
if (ptr->ai_family == AF_INET) {
struct sockaddr_in* ipv4 = (struct sockaddr_in*)ptr->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else {
struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)ptr->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
inet_ntop(ptr->ai_family, addr, ipstr, sizeof(ipstr));
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(result);
}
// DNS解析
void ResolveDNS(const char* hostname) {
struct addrinfo hints, *result, *ptr;
char ipstr[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(hostname, NULL, &hints, &result);
if (ret != 0) {
printf("DNS resolution failed for %s: %d\n", hostname, ret);
return;
}
printf("\nDNS Resolution for: %s\n", hostname);
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
void* addr;
if (ptr->ai_family == AF_INET) {
struct sockaddr_in* ipv4 = (struct sockaddr_in*)ptr->ai_addr;
addr = &(ipv4->sin_addr);
} else {
struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)ptr->ai_addr;
addr = &(ipv6->sin6_addr);
}
inet_ntop(ptr->ai_family, addr, ipstr, sizeof(ipstr));
printf(" -> %s\n", ipstr);
}
freeaddrinfo(result);
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
printf("=== Host Information ===\n\n");
GetLocalHostName();
printf("\n--- Using gethostbyname ---\n");
GetLocalIPOld();
printf("\n--- Using getaddrinfo ---\n");
GetLocalIPNew();
printf("\n--- DNS Resolution Examples ---\n");
ResolveDNS("www.google.com");
ResolveDNS("www.microsoft.com");
WSACleanup();
return 0;
}
5.3、示例3:获取网络适配器信息
// AdapterInfo.c - 网络适配器信息
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
// 获取适配器信息
void GetAdaptersInfo() {
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADAPTER_INFO pAdapter = NULL;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
DWORD dwRetVal = 0;
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) {
printf("Memory allocation failed\n");
return;
}
// 获取所需缓冲区大小
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("Memory allocation failed\n");
return;
}
}
dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (dwRetVal != NO_ERROR) {
printf("GetAdaptersInfo failed: %d\n", dwRetVal);
free(pAdapterInfo);
return;
}
pAdapter = pAdapterInfo;
printf("=== Network Adapters ===\n\n");
while (pAdapter) {
printf("Adapter Name: %s\n", pAdapter->AdapterName);
printf("Description: %s\n", pAdapter->Description);
printf("MAC Address: ");
for (UINT i = 0; i < pAdapter->AddressLength; i++) {
printf("%02X", pAdapter->Address[i]);
if (i < pAdapter->AddressLength - 1) printf("-");
}
printf("\n");
printf("IP Address: %s\n", pAdapter->IpAddressList.IpAddress.String);
printf("Subnet Mask: %s\n", pAdapter->IpAddressList.IpMask.String);
printf("Gateway: %s\n", pAdapter->GatewayList.IpAddress.String);
if (pAdapter->DhcpEnabled) {
printf("DHCP Enabled: Yes\n");
printf("DHCP Server: %s\n", pAdapter->DhcpServer.IpAddress.String);
} else {
printf("DHCP Enabled: No\n");
}
printf("\n");
pAdapter = pAdapter->Next;
}
free(pAdapterInfo);
}
// 获取扩展适配器地址(IPv4和IPv6)
void GetAdapterAddresses() {
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
ULONG outBufLen = 15000;
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
DWORD dwRetVal = 0;
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
free(pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);
}
if (dwRetVal != NO_ERROR) {
printf("GetAdaptersAddresses failed: %d\n", dwRetVal);
free(pAddresses);
return;
}
pCurrAddresses = pAddresses;
printf("\n=== Adapter Addresses (IPv4/IPv6) ===\n\n");
while (pCurrAddresses) {
printf("Adapter: %ws\n", pCurrAddresses->FriendlyName);
printf(" Description: %ws\n", pCurrAddresses->Description);
// MAC地址
if (pCurrAddresses->PhysicalAddressLength > 0) {
printf(" MAC: ");
for (UINT i = 0; i < pCurrAddresses->PhysicalAddressLength; i++) {
printf("%02X", pCurrAddresses->PhysicalAddress[i]);
if (i < pCurrAddresses->PhysicalAddressLength - 1) printf("-");
}
printf("\n");
}
// 遍历IP地址
pUnicast = pCurrAddresses->FirstUnicastAddress;
while (pUnicast) {
char ipstr[INET6_ADDRSTRLEN];
SOCKADDR* addr = pUnicast->Address.lpSockaddr;
if (addr->sa_family == AF_INET) {
struct sockaddr_in* sa_in = (struct sockaddr_in*)addr;
inet_ntop(AF_INET, &sa_in->sin_addr, ipstr, sizeof(ipstr));
printf(" IPv4: %s\n", ipstr);
} else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6* sa_in6 = (struct sockaddr_in6*)addr;
inet_ntop(AF_INET6, &sa_in6->sin6_addr, ipstr, sizeof(ipstr));
printf(" IPv6: %s\n", ipstr);
}
pUnicast = pUnicast->Next;
}
printf("\n");
pCurrAddresses = pCurrAddresses->Next;
}
free(pAddresses);
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
GetAdaptersInfo();
GetAdapterAddresses();
WSACleanup();
return 0;
}
5.4、示例4:网络连接状态
// ConnectionInfo.c - 网络连接状态
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
// 获取TCP连接表
void GetTcpConnections() {
PMIB_TCPTABLE2 pTcpTable;
ULONG ulSize = 0;
DWORD dwRetVal;
// 获取大小
GetTcpTable2(NULL, &ulSize, TRUE);
pTcpTable = (MIB_TCPTABLE2*)malloc(ulSize);
dwRetVal = GetTcpTable2(pTcpTable, &ulSize, TRUE);
if (dwRetVal != NO_ERROR) {
printf("GetTcpTable2 failed: %d\n", dwRetVal);
free(pTcpTable);
return;
}
printf("=== TCP Connections ===\n");
printf("%-22s %-22s %-12s %s\n", "Local Address", "Remote Address", "State", "PID");
for (DWORD i = 0; i < pTcpTable->dwNumEntries; i++) {
MIB_TCPROW2* row = &pTcpTable->table[i];
struct in_addr localAddr, remoteAddr;
localAddr.S_un.S_addr = row->dwLocalAddr;
remoteAddr.S_un.S_addr = row->dwRemoteAddr;
char localStr[32], remoteStr[32];
sprintf_s(localStr, sizeof(localStr), "%s:%d",
inet_ntoa(localAddr), ntohs((u_short)row->dwLocalPort));
sprintf_s(remoteStr, sizeof(remoteStr), "%s:%d",
inet_ntoa(remoteAddr), ntohs((u_short)row->dwRemotePort));
const char* state;
switch (row->dwState) {
case MIB_TCP_STATE_LISTEN: state = "LISTEN"; break;
case MIB_TCP_STATE_ESTAB: state = "ESTABLISHED"; break;
case MIB_TCP_STATE_TIME_WAIT: state = "TIME_WAIT"; break;
case MIB_TCP_STATE_CLOSE_WAIT: state = "CLOSE_WAIT"; break;
default: state = "OTHER"; break;
}
printf("%-22s %-22s %-12s %d\n", localStr, remoteStr, state, row->dwOwningPid);
}
free(pTcpTable);
}
// 获取UDP端点
void GetUdpEndpoints() {
PMIB_UDPTABLE_OWNER_PID pUdpTable;
ULONG ulSize = 0;
DWORD dwRetVal;
GetExtendedUdpTable(NULL, &ulSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
pUdpTable = (MIB_UDPTABLE_OWNER_PID*)malloc(ulSize);
dwRetVal = GetExtendedUdpTable(pUdpTable, &ulSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0);
if (dwRetVal != NO_ERROR) {
printf("GetExtendedUdpTable failed: %d\n", dwRetVal);
free(pUdpTable);
return;
}
printf("\n=== UDP Endpoints ===\n");
printf("%-22s %s\n", "Local Address", "PID");
for (DWORD i = 0; i < pUdpTable->dwNumEntries; i++) {
MIB_UDPROW_OWNER_PID* row = &pUdpTable->table[i];
struct in_addr localAddr;
localAddr.S_un.S_addr = row->dwLocalAddr;
char localStr[32];
sprintf_s(localStr, sizeof(localStr), "%s:%d",
inet_ntoa(localAddr), ntohs((u_short)row->dwLocalPort));
printf("%-22s %d\n", localStr, row->dwOwningPid);
}
free(pUdpTable);
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
GetTcpConnections();
GetUdpEndpoints();
WSACleanup();
return 0;
}
6、课后作业
- 实现类似ipconfig的网络信息显示工具
- 编写DNS批量解析程序
- 实现网络连接监控程序
- 获取并显示路由表信息
7、扩展阅读
- Winsock2 API参考
- IP Helper API详解
- 网络编程最佳实践