Winpcap進行抓包,分析數據包結構並統計IP流量

2020年華科計算機網絡實驗
文末有完整代碼,僅限參考

一.實驗目的

隨着計算機網絡技術的飛速發展,網絡爲社會經濟做出越來越多的貢獻,可以說計算機網絡的發展已經成爲現代社會進步的一個重要標誌。但同時,計算機犯罪、黑客攻擊、病毒入侵等惡性事件也頻頻發生。網絡數據包捕獲、監聽與分析技術是網絡安全維護的一個基本技術同時也是網絡入侵的核心手段。通過基於網絡數據包的截獲和協議分析,對網絡上傳輸的數據包進行捕獲,可以獲取網絡上傳輸的非法信息,對維護網絡安全起到重大作用。
本次實驗的主要目的有:

  1. 理解協議在通信中的作用;
  2. 掌握抓包軟件的開發;
  3. 掌握協議解析的編程方法。
  4. 利用所學的知識,製作一款流量統計軟件

二.實驗要求

軟件功能:

  • 利用winpcap捕獲數據包,並可根據要求進行數據包過濾。
  • 根據IP協議,解析每個數據包的PCI,展示其在不同網絡層次所使用的協議結構和具體信息。
  • 根據IP地址,統計源自該IP地址的流量,即捕獲到的數據包的數量。

三.實驗設計

1. 協議棧分析

由於TCP/IP協議採用分層的結構,這樣在傳輸數據時,在網絡數據的發送端是一個封裝的過程,而在數據接收端則是分解的過程。
數據封裝過程如圖3.1所示
圖3.1

而對接收到的包進行解析,就是上述封裝的逆過程。

1. 以太網協議

在以太網的發展過程中,有很多種幀格式,其中以太網Ⅱ格式應用最爲廣泛,現在幾乎是以太網的標準,它是由RFC894所定義。其幀格式如圖3.2所示。
圖3.2

ARP協議

ARP/RARP協議是進行IP地址和MAC地址相互轉換的協議,網絡通信中,鏈路層使用MAC地址進行實際的數據通信,而在網絡層使用IP地址進行機器定位尋址。APR協議把IP地址映射爲MAC地址,RARP相反。格式如圖3.3所示。
圖3.3

IP協議

IP協議是Internet的核心協議,它工作在網絡層,提供了不可靠無連接的數據傳送服務。協議格式如圖3.4所示。

圖3.4

ICMP協議

Internet控制信息協議(Internet Control Message Protocol),它提供了很多Internet的信息描述服務,能夠檢測網絡的運行狀況,通知協議有用的網絡狀態信息。ICMP是基於IP協議的,ICMP協議格式如圖3.5 所示。
圖3.5

TCP協議

TCP協議是基於連接的可靠的協議,它負責發收端的協定,然後保持正確可靠的數據傳輸服務。它是在IP協議上運行的,而IP無連接的協議,所以TCP豐富了IP協議的功能,使它具有可靠的傳輸服務。TCP協議格式如圖3.6所示。
圖3.6

UDP協議

用戶數據報協議UDP是在IP協議上的傳輸層協議,它提供了無連接的協議服務,它在IP協議基礎上提供了端口的功能,這樣既可讓應用程序進行通信了。UDP協議格式如圖3.7所示。
圖3.7

2. 協議處理

Winpcap系統處理流程圖3.8:
圖3.8

協議分析流程圖3.9:
圖3.9

Ethernet_package_handler():用來解析以太網幀
IP_v4_package_handler():用來解析IPv4數據報
IP_v6_package_handler():用來解析IPv6數據報
Arp_package_handler():用來解析ARP協議
ICMP_package_handler():用來解析ICMP協議
Tcp_package_handler():用來解析TCP報文段
Udp_package_handler():用來解析UDP報文段

3. 流量統計

流量統計過程如3.10所示
圖3.10
在對網絡層協議進行解析時,以IP地址爲鍵,流量爲值存入map中,如果該IP值已經存在,則流量加一,最後的值即表示該IP的流量

四.軟件實現

本軟件採用C++實現,在VS 2017上編寫

1.實現效果:

選擇設備

在這裏插入圖片描述

選擇捕獲數據包的數量

在這裏插入圖片描述

ARP協議解析

在這裏插入圖片描述

IP協議解析

在這裏插入圖片描述

UDP協議解析

在這裏插入圖片描述

ICMP協議解析

在這裏插入圖片描述

最終效果

在這裏插入圖片描述
在這裏插入圖片描述

2.頭文件設計

#ifndef _PAC_ANA_H
#define _PAC_ANA_H

#ifdef _MSC_VER
/*
 * we do not want the warnings about the old deprecated and unsecure CRT functions
 * since these examples can be compiled under *nix as well
 */

#define _CRT_SECURE_NO_WARNINGS
#endif

/*set the environment head files*/
#define WIN32
#pragma comment (lib, "ws2_32.lib")  //load ws2_32.dll

/*set the C++ head files*/
#include <iostream>
#include <stdio.h>
#include <map>
#include <string>
#include <iomanip>
#include <sstream>

/*set the wpcap head files*/
#include "pcap.h"
#include <WinSock2.h>


#define DIVISION "--------------------"
#define B_DIVISION "==================="


 /* 4 bytes IP address */
typedef struct ip_v4_address ip_v4_address;

/* 16 bytes IP address */
typedef struct ip_v6_address ip_v6_address;

/*8 bytes MAC addresss*/
typedef struct mac_address mac_address;

/*ethernet header*/
typedef struct ethernet_header ethernet_header;

/* IPv4 header */
typedef struct ip_v4_header ip_v4_header;

/*IPv6 header*/
typedef struct ip_v6_header ip_v6_header;

/*arp header*/
typedef struct arp_header arp_header;

/*TCP header*/
typedef struct tcp_header tcp_header;

/* UDP header*/
typedef struct udp_header udp_header;

/*ICMP header*/
typedef struct icmp_header icmp_header;

/* prototype of the packet handler */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the ethernet packet*/
void ethernet_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the IPv4 packet*/
void ip_v4_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the IPv6 packet*/
void ip_v6_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the arp packet*/
void arp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the udp packet*/
void udp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the tcp packet*/
void tcp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*analysis the icmp packet*/
void icmp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data);

/*count the package with c++ std::map*/
void add_to_map(std::map<std::string, int> &counter, ip_v4_address ip);
void add_to_map(std::map<std::string, int> &counter, ip_v6_address ip);

/*print the map info*/
void print_map(std::map<std::string, int> counter);

#endif // !_PAC_ANA_H

3.具體實現

#include "pac_ana.h"

using namespace std;

/*ip counter*/
std::map<std::string, int> counter;

/*header structure*/
struct ip_v4_address
{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
};

struct ip_v6_address
{
	u_short part1;
	u_short part2;
	u_short part3;
	u_short part4;
	u_short part5;
	u_short part6;
	u_short part7;
	u_short part8;
};

struct mac_address
{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
	u_char byte5;
	u_char byte6;
};

struct ethernet_header
{
	mac_address des_mac_addr;
	mac_address src_mac_addr;
	u_short type;
};

struct ip_v4_header
{
	u_char	ver_ihl;		// Version (4 bits) + Internet header length (4 bits)
	u_char	tos;			// Type of service 
	u_short tlen;			// Total length 
	u_short identification; // Identification
	u_short flags_fo;		// Flags (3 bits) + Fragment offset (13 bits)
	u_char	ttl;			// Time to live
	u_char	proto;			// Protocol
	u_short checksum;			// Header checksum
	ip_v4_address	src_ip_addr;		// Source address
	ip_v4_address	des_ip_addr;		// Destination address
	u_int	op_pad;			// Option + Padding
};

struct ip_v6_header 
{
	u_int32_t ver_trafficclass_flowlabel;
	u_short payload_len;
	u_char next_head;
	u_char ttl;
	ip_v6_address src_ip_addr;
	ip_v6_address dst_ip_addr;
};

struct arp_header
{
	u_short hardware_type;
	u_short protocol_type;
	u_char hardware_length;
	u_char protocol_length;
	u_short operation_code;
	mac_address source_mac_addr;
	ip_v4_address source_ip_addr;
	mac_address des_mac_addr;
	ip_v4_address des_ip_addr;
};

struct tcp_header
{
	u_short sport;
	u_short dport;
	u_int sequence;
	u_int acknowledgement;
	u_char offset;
	u_char flags;
	u_short windows;
	u_short checksum;
	u_short urgent_pointer;
};

struct udp_header
{
	u_short sport;			// Source port
	u_short dport;			// Destination port
	u_short len;			// Datagram length
	u_short checksum;			// Checksum
};

struct icmp_header
{
	u_char type;
	u_char code;
	u_short checksum;
	u_short id;
	u_short sequence;
};

int main()
{
	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum;
	int i = 0;
	int pktnum;
	pcap_t *adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];
	u_int netmask = 0xffffff;;
	struct bpf_program fcode;

	if (pcap_findalldevs(&alldevs, errbuf) == -1)
	{
		fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
		exit(1);
	}


	for (d = alldevs; d; d = d->next)
	{
		cout << ++i << "." << d->name;
		if (d->description)
			cout << d->description << endl;
		else
			cout << " (No description available)" << endl;
	}

	if (i == 0)
	{
		cout << "\nNo interfaces found! Make sure WinPcap is installed." << endl;
		return -1;
	}

	cout << "Enter the interface number (1-" << i << "): ";
	cin >> inum;

	if (inum < 1 || inum > i)
	{
		cout << "\nInterface number out of range." << endl;
		pcap_freealldevs(alldevs);
		return -1;
	}


	for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

	if ((adhandle = pcap_open_live(d->name,	// name of the device
												65536,			// portion of the packet to capture. 
																// 65536 grants that the whole packet will be captured on all the MACs.
												1,				// promiscuous mode (nonzero means promiscuous)
												1000,			// read timeout
												errbuf			// error buffer
												)) == NULL)
	{
		fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		pcap_freealldevs(alldevs);
		return -1;
	}

	cout << "listening on " << d->description << "...." << endl;

	pcap_freealldevs(alldevs);

	if (pcap_compile(adhandle, &fcode, "ip or arp", 1, netmask) < 0)
	{
		fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
		pcap_close(adhandle);
		return -1;
	}

	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
		fprintf(stderr, "\nError setting the filter.\n");
		pcap_close(adhandle);
		return -1;
	}

	cout << "please input the num of packets you want to catch(0 for keeping catching): ";
	cin >> pktnum;
	cout << endl;
	pcap_loop(adhandle, pktnum, packet_handler, NULL);
	pcap_close(adhandle);

	getchar();
	return 0;
}


/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	struct tm *ltime;
	char timestr[16];
	time_t local_tv_sec;

	/* convert the timestamp to readable format */
	local_tv_sec = header->ts.tv_sec;
	ltime = localtime(&local_tv_sec);
	strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);
	cout << B_DIVISION << "time:" << timestr << ","
		<< header->ts.tv_usec << "  len:" << header->len << B_DIVISION<<endl;
	ethernet_package_handler(param, header, pkt_data);
}

void ethernet_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	ethernet_header* eh = (ethernet_header*)pkt_data;
	cout << DIVISION << "以太網協議分析結構" << DIVISION << endl;
	u_short type = ntohs(eh->type);
	cout << "類型:0x" <<  hex << type;
	cout << setbase(10);
	switch (type)
	{
	case 0x0800:
		cout << " (IPv4)" << endl;
		break;
	case 0x86DD:
		cout << "(IPv6)" << endl;
		break;
	case 0x0806:
		cout << " (ARP)" << endl;
		break;
	case 0x0835:
		cout << " (RARP)" << endl;
	default:
		break;
	}
	cout << "目的地址:" << int(eh->des_mac_addr.byte1) << ":"
		<< int(eh->des_mac_addr.byte2) << ":"
		<< int(eh->des_mac_addr.byte3) << ":"
		<< int(eh->des_mac_addr.byte4) << ":"
		<< int(eh->des_mac_addr.byte5) << ":"
		<< int(eh->des_mac_addr.byte6) << endl;
	cout << "源地址:" << int(eh->src_mac_addr.byte1) << ":"
		<< int(eh->src_mac_addr.byte2) << ":"
		<< int(eh->src_mac_addr.byte3) << ":"
		<< int(eh->src_mac_addr.byte4) << ":"
		<< int(eh->src_mac_addr.byte5) << ":"
		<< int(eh->src_mac_addr.byte6) << endl;
	switch (type)
	{
	case 0x0800:
		ip_v4_package_handler(param, header, pkt_data);
		break;
	case 0x0806:
		arp_package_handler(param, header, pkt_data);
		break;
	case 0x86DD:
		ip_v6_package_handler(param, header, pkt_data);
		break;
	default:
		break;
	}
	cout << endl << endl;
}

void arp_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	arp_header* ah;
	ah = (arp_header*)(pkt_data + 14);
	cout << DIVISION << "ARP協議分析結構" << DIVISION << endl;
	u_short operation_code = ntohs(ah->operation_code);
	cout << "硬件類型:" << ntohs(ah->hardware_type) << endl;
	cout << "協議類型:0x" << hex << ntohs(ah->protocol_type) << endl;
	cout << setbase(10);
	cout << "硬件地址長度:" << int(ah->hardware_length) << endl;
	cout << "協議地址長度:" << int(ah->protocol_length) << endl;
	switch (operation_code)
	{
	case 1:
		cout << "ARP請求協議" << endl;
		break;
	case 2:
		cout << "ARP應答協議" << endl;
		break;
	case 3:
		cout << "ARP請求協議" << endl;
		break;
	case 4:
		cout << "RARP應答協議" << endl;
		break;
	default:
		break;
	}
	cout << "源IP地址:"
		<< int(ah->source_ip_addr.byte1) << "."
		<< int(ah->source_ip_addr.byte2) << "."
		<< int(ah->source_ip_addr.byte3) << "."
		<< int(ah->source_ip_addr.byte4) << endl;

	cout << "目的IP地址:"
		<< int(ah->des_ip_addr.byte1) << "."
		<< int(ah->des_ip_addr.byte2) << "."
		<< int(ah->des_ip_addr.byte3) << "."
		<< int(ah->des_ip_addr.byte4) << endl;

	add_to_map(counter, ah->source_ip_addr);
	print_map(counter);
}

void ip_v4_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	ip_v4_header *ih;
	ih = (ip_v4_header *)(pkt_data + 14); //14 measn the length of ethernet header
	cout << DIVISION << "IPv4協議分析結構" << DIVISION << endl;
	cout << "版本號:" << ((ih->ver_ihl & 0xf0) >> 4) << endl;
	cout << "首部長度:" << (ih->ver_ihl & 0xf) << "("
		<< ((ih->ver_ihl & 0xf)<<2) << "B)" << endl;
	cout << "區別服務:" << int(ih->tos) << endl;
	cout << "總長度:" << ntohs(ih->tlen) << endl;
	cout << "標識:" << ntohs(ih->identification) << endl;
	cout << "標誌:" << ((ih->flags_fo & 0xE000) >> 12) << endl;
	cout << "片偏移:" <<  (ih->flags_fo & 0x1FFF) << "("
		<< ((ih->flags_fo & 0x1FFF) << 3) << "B)" <<endl;
	cout << "生命週期:" << int(ih->ttl) << endl;
	cout << "協議:";
	switch (ih->proto)
	{
		case 6:
			cout << "TCP" << endl;
			break;
		case 17:
			cout << "UDP" << endl;
			break;
		case 1:
			cout << "ICMP" << endl;
			break;
		default:
			cout <<  endl;
			break;
	}
	cout << "校驗和:" << ntohs(ih->checksum) << endl;
	cout << "源IP地址:" 
		<< int(ih->src_ip_addr.byte1) << "."
		<< int(ih->src_ip_addr.byte2) << "."
		<< int(ih->src_ip_addr.byte3) << "."
		<< int(ih->src_ip_addr.byte4) <<  endl;

	cout << "目的IP地址:" 
		<< int(ih->des_ip_addr.byte1) << "."
		<< int(ih->des_ip_addr.byte2) << "."
		<< int(ih->des_ip_addr.byte3) << "."
		<< int(ih->des_ip_addr.byte4) << endl;
	switch (ih->proto)
	{
		case 6:
			tcp_package_handler(param, header, pkt_data);
			break;
		case 17:
			udp_package_handler(param, header, pkt_data);
			break;
		case 1:
			icmp_package_handler(param, header, pkt_data);
			break;
		default:
			break;
	}
	add_to_map(counter, ih->src_ip_addr);
	print_map(counter);
}

void ip_v6_package_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	ip_v6_header *ih;
	ih = (ip_v6_header *)(pkt_data + 14); //14 measn the length of ethernet header
	int version = (ih->ver_trafficclass_flowlabel & 0xf0000000) >> 28;
	int traffic_class = ntohs((ih->ver_trafficclass_flowlabel & 0x0ff00000) >> 20);
	int flow_label = ih->ver_trafficclass_flowlabel & 0x000fffff;
	cout << "版本號:" << version << endl;
	cout << "通信量類:" << traffic_class << endl;
	cout << "流標號:" << flow_label << endl;
	cout << "有效載荷:" << ntohs(ih->payload_len) << endl;
	cout << "下一個首部:" << int(ih->next_head) << endl;
	cout << "跳數限制:" << int(ih->ttl) << endl;
	cout << "源IP地址:"
		<< int(ih->src_ip_addr.part1) << ":"
		<< int(ih->src_ip_addr.part2) << ":"
		<< int(ih->src_ip_addr.part3) << ":"
		<< int(ih->src_ip_addr.part4) << ":"
		<< int(ih->src_ip_addr.part5) << ":"
		<< int(ih->src_ip_addr.part6) << ":"
		<< int(ih->src_ip_addr.part7) << ":"
		<< int(ih->src_ip_addr.part8) << endl;
	cout << "目的IP地址:"
		<< int(ih->dst_ip_addr.part1) << ":"
		<< int(ih->dst_ip_addr.part2) << ":"
		<< int(ih->dst_ip_addr.part3) << ":"
		<< int(ih->dst_ip_addr.part4) << ":"
		<< int(ih->dst_ip_addr.part5) << ":"
		<< int(ih->dst_ip_addr.part6) << ":"
		<< int(ih->dst_ip_addr.part7) << ":"
		<< int(ih->dst_ip_addr.part8) << endl;
	switch (ih->next_head)
	{
	case 6:
		tcp_package_handler(param, header, pkt_data);
		break;
	case 17:
		udp_package_handler(param, header, pkt_data);
		break;
	case 58:
		icmp_package_handler(param, header, pkt_data);
		break;
	default:
		break;
	}
	add_to_map(counter, ih->src_ip_addr);
	print_map(counter);
}


void udp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	udp_header *uh;
	uh = (udp_header *)(pkt_data + 20 + 14);
	cout << DIVISION << "UDP協議分析結構" << DIVISION << endl;
	cout << "源端口:" << ntohs(uh->sport) << endl;
	cout << "目的端口:" << ntohs(uh->dport) << endl;
	cout << "長度:" << ntohs(uh->len) << endl;
	cout << "檢驗和:" << ntohs(uh->checksum) << endl;
}


void tcp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	tcp_header* th;
	th = (tcp_header*)(pkt_data + 14 + 20);
	cout << DIVISION << "TCP協議分析結構" << DIVISION << endl;
	cout << "源端口:" <<  ntohs(th->sport) << endl;
	cout << "目的端口:" << ntohs(th->dport) << endl;
	cout << "序號:" << ntohl(th->sequence) << endl;
	cout << "確認號:" << ntohl(th->acknowledgement) << endl;
	cout << "數據偏移:" << ((th->offset & 0xf0) >> 4) << "("
		<< ((th->offset & 0xf0) >> 2) << "B)"<< endl;
	cout << "標誌:" ;
	if (th->flags & 0x01) 
	{
		cout << "FIN ";
	}
	if (th->flags & 0x02) 
	{
		cout << "SYN ";
	}
	if (th->flags & 0x04)
	{
		cout << "RST ";
	}
	if (th->flags & 0x08)
	{
		cout << "PSH ";
	}
	if (th->flags & 0x10)
	{
		cout << "ACK ";
	}
	if (th->flags & 0x20)
	{
		cout << "URG ";
	}
	cout << endl;
	cout << "窗口:" << ntohs(th->windows) << endl;
	cout << "檢驗和:" << ntohs(th->checksum) << endl;
	cout << "緊急指針:" << ntohs(th->urgent_pointer) << endl;
}


void icmp_package_handler(u_char* param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	icmp_header* ih;
	ih = (icmp_header*)(pkt_data + 14 + 20);
	cout << DIVISION << "ICMP協議分析結構" << DIVISION << endl;
	cout << "ICMP類型:" << ih->type;
	switch (ih->type)
	{
	case 8:
		cout << "ICMP回顯請求協議" << endl;
		break;
	case 0:
		cout << "ICMP回顯應答協議" << endl;
		break;
	default:
		break;
	}
	cout << "ICMP代碼:" << ih->code << endl;
	cout << "標識符:" << ih->id << endl;
	cout << "序列碼:" << ih->sequence << endl;
	cout << "ICMP校驗和:" << ntohs(ih->checksum) << endl;
}

void add_to_map(map<string, int> &counter, ip_v4_address ip) 
{
	string ip_string;
	int amount = 0;
	map<string,int>::iterator iter;
	ip_string = to_string(ip.byte1) + "."
					+ to_string(ip.byte2) + "."
					+ to_string(ip.byte3) + "."
					+ to_string(ip.byte4);
	iter = counter.find(ip_string);
	if (iter != counter.end())
	{
		amount = iter->second;
	}
	counter.insert_or_assign(ip_string, ++amount);
}

void add_to_map(map<string, int> &counter, ip_v6_address ip)
{
	string ip_string;
	int amount = 0;
	map<string, int>::iterator iter;
	ip_string = to_string(ip.part1) + ":"
		+ to_string(ip.part2) + ":"
		+ to_string(ip.part3) + ":"
		+ to_string(ip.part4) + ":"
		+ to_string(ip.part5) + ":"
		+ to_string(ip.part6) + ":"
		+ to_string(ip.part7) + ":"
		+ to_string(ip.part8);
	iter = counter.find(ip_string);
	if (iter != counter.end())
	{
		amount = iter->second;
	}
	counter.insert_or_assign(ip_string, ++amount);
}

void print_map(map<string, int> counter)
{
	map<string, int>::iterator iter;
	cout << DIVISION << "流量統計" << DIVISION << endl;
	cout << "IP" << setfill(' ')<<setw(45) << "流量" << endl;
	for (iter = counter.begin(); iter != counter.end(); iter++)
	{
		cout << iter->first  << setfill('.') << setw(45-iter->first.length()) << iter->second<<endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章