1. 程式人生 > >talk is cheap;Later equals never;

talk is cheap;Later equals never;

前言

當讀取離線檔案並分析時,用回撥方式有點不好.

在一個java-cm中看到人家的處理 :只要要分析的資料不夠了,直接去網路上收包(即使就讀1位元組,如果緩衝區內沒有了,也去收一個完整包下來),然後快取起來給呼叫者使用。這就沒有挎包的繁瑣處理了。

因為包資料的處理是在回撥中完成的, 如果在回撥中還想要處理更多的後續包,那是不可能的(只有出了回撥,才會有新的回撥來)。

看了libpcap介面,有pcap_next或pcap_next_ex, 對這個介面進行了封裝,使程式處理流程合理了。當想處理更多資料時,直接去pcap流中去拿,然後緩衝起來。當要處理的資料長度在緩衝區裡有時,就從緩衝區裡拿。如果呼叫方需要的資料長度在緩衝中不夠了,再去pcap流中去拿,然後拼在原有緩衝後面。再將請求的資料返回給呼叫者。

這個類還要再封裝下,要應對從一堆有序的pcap檔案裡順序讀取資料的情況。因為小概率情況,會在不同pcap檔案介面的地方,分析錯誤(如果資料沒有無縫讀取的話)。

demo下載

稍後上傳

demo預覽

// @file main.cpp
// @brief 無縫從pcap流中連續讀取資料

#include <iostream>
#include "IF_pcap_byte_stream.h"

// #define WORK_ON_COMPANY
#ifdef WORK_ON_COMPANY
#else // #ifdef WORK_ON_COMPANY
#define OFFLINE_PCAP_FILE_TO_PARSE "D:\\pcap_files\\test.pcap"
#define MY_CAPTRUE_FILTER "host 192.168.11.132 and port 6666"
#define DB_IP_TO_PARSE "192.168.11.132"
#define DB_PORT_TO_PARSE 1521
#endif // #ifdef WORK_ON_COMPANY

#ifndef MYLOG_I
#define MYLOG_I printf
#endif // #ifndef MYLOG_I

#ifndef LINE_80
#define LINE_80 "--------------------------------------------------------------------------------"
#endif // #ifndef LINE_80

IF_pcap_byte_stream g_my_pcap_stream;

BOOL WINAPI phandler_routine(DWORD CtrlType);

int main()
{
	bool rc = false;
	int i = 0;
	int err = 0;
	u_char uc_tmp = '\0';
	int i_cb_pos_index = 0;

	do {
		if (!fn_dy_load_wpcap()) {
			break;
		}

		if (!SetConsoleCtrlHandler(phandler_routine, TRUE)) {
			break;
		}

		if (!g_my_pcap_stream.open_pcap_file(OFFLINE_PCAP_FILE_TO_PARSE, MY_CAPTRUE_FILTER)) {
			printf("open pcap file error\n");
			break;
		}

		while (g_my_pcap_stream.read_n_bytes(&uc_tmp, sizeof(uc_tmp))) {
			printf("bytes[%d] = 0x%X\n", i_cb_pos_index, uc_tmp);
			i_cb_pos_index++;
		}

		printf("task over\n");
		rc = true;
	} while (0);

	if (!rc) {
		printf("error happen\n");
	}

	g_my_pcap_stream.close_pcap_file();
	system("pause");
	return 0;
}

BOOL WINAPI phandler_routine(DWORD CtrlType)
{
	BOOL rc = FALSE;
	switch (CtrlType) {
	case CTRL_C_EVENT:
	{
		g_my_pcap_stream.close_pcap_file();
	}
	rc = TRUE;
	break;
	default:
		break;
	}

	return rc;
}

// @file \read_from_pcap_files\IF_pcap_byte_stream.h
// @brief 構造一個邏輯, 讓'從pcap包中讀取位元組'像從一個連續的流中讀取一樣
// 不用再考慮挎包問題

#ifndef __IF_PCAP_BYTE_STREAM_H__
#define __IF_PCAP_BYTE_STREAM_H__

#include "wpcap_dll_ex.h"

class IF_pcap_byte_stream
{
public:
	IF_pcap_byte_stream();
	virtual ~IF_pcap_byte_stream();

	bool open_pcap_file(const char* psz_pcap_flie_path_name, const char* psz_captrue_filter);
	void close_pcap_file();

	// 讀失敗的可能性
	// * 離線檔案開啟失敗
	// * 離線檔案讀完了
	bool read_n_bytes(/*OUT*/ u_char* puc_buf, /*IN*/ int cb_to_read);

private:
	int get_packet_buf_left_len(); // 得到緩衝區內包內容生於長度
	int get_packet_buf_free_size(); // 得到緩衝區還能放多少位元組
	void move_left_data_to_buf_head(); // 如果要附加新的包資料進來, 先將剩下的資料挪到緩衝區的開始處
	bool append_data_to_buf(const u_char* pkt_data, int i_len); //向當前緩衝區內附加包資料

private:
	pcap_t* m_h_lib_pcap;
	struct bpf_program m_bpf_program;
	bool m_b_valid_bpf;

	u_char m_packet_buf[1024 * 16]; // 能裝下2個包的緩衝區

	int m_i_packet_buf_pos_begin; // 包緩衝區內有效內容的開始位置
	int m_i_packet_buf_pos_end; // 包緩衝區內有效內容的結束位置
	// 如果 (m_i_packet_buf_pos_begin == m_i_packet_buf_pos_end), 說明緩衝區內是空的
};

#endif // #ifndef __IF_PCAP_BYTE_STREAM_H__
// @file read_from_pcap_files\IF_pcap_byte_stream.cpp

#include "IF_pcap_byte_stream.h"

IF_pcap_byte_stream::IF_pcap_byte_stream()
{
	m_h_lib_pcap = NULL;
	m_b_valid_bpf = false;
	memset(&m_bpf_program, 0, sizeof(struct bpf_program));

	memset(m_packet_buf, 0, sizeof(m_packet_buf));
	m_i_packet_buf_pos_begin = 0;
	m_i_packet_buf_pos_end = 0;
}

IF_pcap_byte_stream::~IF_pcap_byte_stream()
{
	close_pcap_file();
}

bool IF_pcap_byte_stream::open_pcap_file(const char* psz_pcap_flie_path_name, const char* psz_captrue_filter)
{
	bool b_rc = false;
	char errbuf[PCAP_ERRBUF_SIZE] = { '\0' };
	
	do {
		if (NULL == psz_pcap_flie_path_name) {
			break;
		}

		close_pcap_file();

		m_h_lib_pcap = pcap_open_offline(psz_pcap_flie_path_name, errbuf);
		if (NULL == m_h_lib_pcap) {
			break;
		}

		pcap_set_promisc(m_h_lib_pcap, 1); // interface_opts->promisc_mode is 1
		pcap_set_timeout(m_h_lib_pcap, -1); // timeout is 250

		pcap_set_buffer_size(m_h_lib_pcap, 10 * 1024 * 1024);

		// capture_loop_init_filter
		int snaplen = pcap_snapshot(m_h_lib_pcap);
		// printf("snaplen = %d\n", snaplen);

		// compile BPF
		if (pcap_compile(m_h_lib_pcap, &m_bpf_program, psz_captrue_filter, 1, 0) == -1) {
			fprintf(stderr, "error\n");
			break;
		}

		m_b_valid_bpf = true;

		// set BPF filter
		if (pcap_setfilter(m_h_lib_pcap, &m_bpf_program) == -1) {
			fprintf(stderr, "error\n");
			break;
		}

		b_rc = true;
	} while (0);

	return b_rc;
}

void IF_pcap_byte_stream::close_pcap_file()
{
	if (m_b_valid_bpf) {
		m_b_valid_bpf = false;
		pcap_freecode(&m_bpf_program);
	}

	if (NULL != m_h_lib_pcap) {
		pcap_close(m_h_lib_pcap);
		m_h_lib_pcap = NULL;
	}
}

bool IF_pcap_byte_stream::read_n_bytes(/*OUT*/ u_char* puc_buf, /*IN*/ int cb_to_read)
{
	bool b_rc = false;
	pcap_pkthdr* p_pkt_hdr = NULL;
	const u_char* p_pkt_data = NULL;
	int i_rc = 0;

	do {
		if (NULL == m_h_lib_pcap) {
			break; // pcap file not open, or open failed
		}

		if (get_packet_buf_left_len() < cb_to_read) {
			i_rc = pcap_next_ex(m_h_lib_pcap, &p_pkt_hdr, &p_pkt_data);
			if (1 != i_rc) {
				// 0 是超時
				// -2 是離線檔案讀完了
				break;
			}

			move_left_data_to_buf_head();
			if (get_packet_buf_free_size() < (int)p_pkt_hdr->caplen) {
				break; // 緩衝區開小了
			}

			if (!append_data_to_buf(p_pkt_data, p_pkt_hdr->caplen)) {
				break; // 生於的緩衝區空間不夠大
			}
		}

		if (get_packet_buf_left_len() >= cb_to_read) {
			memcpy(puc_buf, m_packet_buf + m_i_packet_buf_pos_begin, cb_to_read);
			m_i_packet_buf_pos_begin += cb_to_read;
			b_rc = true;
		}
	} while (0);

	return b_rc;
}

bool IF_pcap_byte_stream::append_data_to_buf(const u_char* pkt_data, int i_len)
{
	bool b_rc = false;

	do {
		if (get_packet_buf_free_size() < i_len) {
			break;
		}

		memcpy(m_packet_buf, pkt_data, i_len);
		m_i_packet_buf_pos_end += i_len;
		b_rc = true;
	} while (0);

	return b_rc;
}

int IF_pcap_byte_stream::get_packet_buf_left_len()
{
	if (m_i_packet_buf_pos_end > m_i_packet_buf_pos_begin) {
		return (m_i_packet_buf_pos_end - m_i_packet_buf_pos_begin);
	}

	return 0;
}

void IF_pcap_byte_stream::move_left_data_to_buf_head()
{
	int i_left_data_len = 0;
	int i = 0;
	u_char* pdst = NULL;
	u_char* psrc = NULL;

	if (m_i_packet_buf_pos_begin != 0) {
		pdst = m_packet_buf;
		psrc = m_packet_buf + m_i_packet_buf_pos_begin;
		i_left_data_len = get_packet_buf_left_len();
		if ((m_i_packet_buf_pos_begin + 1) > i_left_data_len) {
			// 直接拷貝過去
			memcpy(pdst, psrc, i_left_data_len);
		} else {
			// 資料有重疊, 逐位元組拷貝過去
			for (i = 0; i < i_left_data_len; i++) {
				pdst[i] = psrc[i];
			}
		}

		m_i_packet_buf_pos_begin = 0;
		m_i_packet_buf_pos_end = i_left_data_len;
	}
}

int IF_pcap_byte_stream::get_packet_buf_free_size()
{
	return (sizeof(m_packet_buf) - m_i_packet_buf_pos_end);
}