文章目录

一、通信协议的详细方案

前文说到,我们设计了这么一个BTP(BWB Transport Protocol)通信协议:

序号BTP字段名占用空间说明
 1协议标识1字节 0x42(大写的'B')
 2协议版本1字节 0x01(1.0版本)
 3包类型1字节 握手请求包:0x01
 握手响应包:0x02
 心跳请求包:0x03
 心跳响应包:0x04
 数据包:0x05
 断开请求包:0x06
 断开响应包:0x07
 4包序号1字节 0x00~0xFF循环使用
 5数据长度2字节 0x0000~0xFFFF
 6数据0~65535字节 要传输的数据,可以为空 
 7校验和 4字节 采用经典的CRC32

(1)包序号

每次开始发送数据时,总是从0x00到0xFF随机挑选一个初始数字,发包和回包必须携带相同的数字,并且下一个包为这一个包序号+1,如果超过0xFF,则从0x00重新开始。

(2)握手请求包0x01与握手响应包0x02

a. 规定数据长度为0,数据为空。

b. 发送者发送握手请求包,接收者接收握手请求包后立即响应握手应答包。

c. 只发送一次,1秒超时则认为握手失败。

d. 握手必须确认协议标识和版本号,否则认为不是BTP包或者版本不相同进行丢弃(一般的协议都是返回一个带错误码的包,这里直接进行简单丢弃)。

(3)心跳请求包0x03与心跳响应包0x04

a. 规定数据长度为0,数据为空。

b. 在成功握手之后,每隔5秒发送一次心跳请求包,接收者必须立即回应心跳响应包,用来维护链路。

c. 1秒超时则认为链路断开。

(4)数据包0x05

携带着要发送的数据。

(5)断开请求包0x06和断开响应包0x07

a. 规定数据长度为0,数据为空。  

b. 当接收者接收到发送者的断开请求包后,立即回应断开响应包,双方各自进行清理工作,结束通信。

OK,方案设计完成。

二、winpcap的发送者和接收者的实现

1、实现思路

前面已经实现了基本的发包和收包,要想实现发送者和接收者,核心是要让程序能够同时进行发包和收包。为了方便说明,仍然使用PA进行作为通信发起者,PB作为通信接收者。

方案一

对于PB来说,用一个全局变量维护当前状态,然后在回调函数里面收到包后进行发包操作即可。

对于PA来说,也使用全局变量维护当前状态,在发送第一个握手包之后,就陷入监听,然后在回调函数里实现类似的收包发包操作。

但这种方案维护起来很麻烦,比较差。

方案二(采用)

多线程,用全局变量来进行同步,使用互斥锁进行写操作。

【PB】

只需要进行反射发包,所以仅仅需要收到特定包后回送特定包,根据状态机前进,两个线程,一个主线程发射发包,一个计时线程计时心跳。

如果心跳超时,强行中断所有线程。

(这和直接接受心跳包然后计算时间差来判断有什么区别呢?区别在于,如果对方断开了连接,那么永远也接受不到心跳包,也就计算不了时间差了;而线程自己计时则能够自己控制)

【PA】

线程1为发包线程,在特定条件满足后,开始发送特定包(数据包)或进行特定操作(开启心跳线程)。

线程2为接收线程,在接收到特定包后(比如接收到握手包),进行唤醒操作(比如每隔1秒唤醒发包线程,每隔5秒同时唤醒心跳线程)。

线程3为心跳线程,在特定条件满足后,开始发送心跳包。

线程4为计时线程,如果超时,强行中断所有线程。  

哎,就很舒服~但是实现起来比较麻烦,一步一步来。

2、实现过程

(1)配置pthread2.9.1 下载地址(密码:vyqi)

和前面配置winpcap类似,首先把pthread源代码文件解压到英文目录,比如E:\pthreads2.9.1

然后添加lib支持,告诉编译器链接的时候先去找这个库。调试→属性→配置属性→链接器→输入,添加:

pthreadVC2.lib;pthreadVCE2.lib;pthreadVSE2.lib;

最后添加包含目录和库目录,调试→属性→配置属性→VC++目录,添加:

包含目录添加     E:\pthreads2.9.1\Pre-built.2\include;
库目录添加        E:\pthreads2.9.1\Pre-built.2\lib\x86;

这里很可能还是没配置好,运行时还会报错:

这是因为之前配置的winpcap在很多软件安装的时候都会自动安装,比如安装wireshark都会提示你安装winpcap否则wireshark不会正常工作,他们把dll文件都拷贝进了C:\Windows\System32里面。所以之前能够找到winpcap的动态链接库dll,这里却找不到了。pthread很可能我们只用一次而已,拷贝进系统dll不可取,拷贝进项目dll不方便,因此我们依然采用设置的方法:

调试→属性→配置属性→调试→环境,添加:

path=E:\pthreads2.9.1\Pre-built.2\dll\x86

编译环境就OK了。但是我们这个exe是要放到windows xp下运行的,由于官方的lib是动态的,依然会提示错误,有兴趣可以看看 《pthread-win32静态库的编译和使用方法》 , 这时候我们采用复制dll的方法,把需要的dll赋值到EXE所在目录,反正都是虚拟机,无所谓,这样就能成功运行啦:

(2)编写接收者

BTPrecver.h

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <winsock.h>
#include <pthread.h>

#define SEND_BUFSIZE                    1024
#define TIMER_SLEEPTIME                 2000
#define ERROR_GENERAL                   -1
#define ERROR_FINDALLDEVS_FAILURE       -2
#define ERROR_INTERFACES_NOT_FOUND      -3
#define ERROR_BAD_INPUT                 -4
#define ERROR_OPEN_ADAPTER_FAILURE      -5
#define ERROR_SENDING_FAILURE           -6
#define ERROR_INVALID_DATALINK_TYPE     -7
#define ERROR_COMPILE_FILTER_FALIURE    -8
#define ERROR_SET_FILTER_FALIURE        -9
#define ERROR_CREATE_THREAD             -10
#define ERROR_BTP_HELLO_FAILED          -11
#define ERROR_BTP_HEARTBEAT_FAILED      -12
#define ERROR_BTP_BYE_FAILED            -13
#define ERROR_BTP_TIMEOUT               -14
#define ERROR_BAD_VERSION               -15


#define BTP_HELLO_REQUEST       0x01
#define BTP_HELLO_RESPONSE      0x02
#define BTP_HEARTBEAT_REQUEST   0x03
#define BTP_HEARTBEAT_RESPONSE  0x04
#define BTP_DATA                0x05
#define BTP_BYE_REQUEST         0x06
#define BTP_BYE_RESPONSE        0x07
#define BTP_PROTOCOL            0x42
#define BTP_VERSION             0x01

typedef struct ETH_HEADER
{
    u_char dest_mac[6];
    u_char src_mac[6];
    u_short etype;
}ETH_HEADER;

typedef struct BTP_HEADER{
    u_char protocol;
    u_char version;
    u_char type;
    u_char pid;
    u_short data_length;
}BTP_HEADER;

typedef struct BTP_STATE{
    int state; /* 状态:0=初始(可握手) 1=握手后(可心跳、可数据、可断开)*/
    pthread_t pids[1]; /* 只有一个心跳线程 */
    int timeout_flag; /* 心跳超时标记,每次收包都进行+1,如果计时线程发现两次的flag都相同,说明断开了 */
    pcap_t *adapter; /* 网卡句柄 */
}BTP_STATE;

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);/* 抓包回调函数 */
void format_mac(LPSTR lpHWAddrStr, const unsigned char *HWAddr);/* mac地址格式化函数 */

int btp_send(ETH_HEADER* eth_header, BTP_HEADER *btp_header, u_char type);
void *timer(void *arg);
void kill_all(char *msg, int error_code);

void copy_mac(u_char *mac1, u_char *mac2);
char* rec_type(u_char type);

BTPrecver.c

#include "BTPrecver.h"


BTP_STATE btp_state;

int main()
{
    char errbuf[PCAP_ERRBUF_SIZE]; /* 错误信息buffer */
    u_int netmask; /* 掩码信息 */
    char packet_filter[] = "ether src 00:50:56:C0:00:08 and ether dst 00:0C:29:86:B8:C8"; /* 过滤规则 */
    struct bpf_program fcode; /* 存储编译好的过滤码 */

    pcap_if_t *alldevs; /* 全部网卡列表 */
    pcap_if_t *d; /* 一个网卡 */
    int did; /* 选择的网卡ID */

    int i = 0; /* 迭代 */

    /*查找网卡*/
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
        fprintf(stderr, "[ERROR] pcap_findalldevs error: %s\n", errbuf);
        return ERROR_FINDALLDEVS_FAILURE;
    }

    /* 选择网卡d */
    for (d = alldevs, i = 0; d; d = d->next) {
        if (d->description)
            printf("NO.%d: %s\n", ++i, d->description);
        else
            printf("[WARN] No description available\n");
    }

    if (i == 0) {
        printf("[ERROR] No interfaces found! Make sure WinPcap is installed.\n");
        return ERROR_INTERFACES_NOT_FOUND;
    }

    printf("[INFO] Enter the interface number (1-%d):", i);
    scanf("%d", &did);

    if (did < 1 || did > i) {
        printf("[ERROR] Interface number out of range.\n");
        pcap_freealldevs(alldevs);
        return ERROR_BAD_INPUT;
    }

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

    /* 打开网卡 */
    if ((btp_state.adapter = pcap_open_live(d->name, /* 设备名 */
        65536, /* 捕获数据包的长度(65536捕获所有数据包) */
        1, /* 混杂模式(非0表示使用混杂模式) */
        1000, /* 超时时间(0表示没有超时限制) */
        errbuf /* 错误缓存(存储错误信息) */
        )) == NULL) {
        fprintf(stderr, "[ERROR] Unable to open the adapter. %s is not supported by WinPcap\n");
        pcap_freealldevs(alldevs);
        return ERROR_OPEN_ADAPTER_FAILURE;
    }

    /* 检查链路层类型 */
    if (pcap_datalink(btp_state.adapter) != DLT_EN10MB) /* DLT_EN10MB指10Mb以太网 */
    {
        fprintf(stderr, "[ERROR] This program works only on Ethernet networks.\n");
        pcap_freealldevs(alldevs);
        return ERROR_INVALID_DATALINK_TYPE;
    }

    /* 检查地址类型 */
    if (d->addresses != NULL) /* 如果有IP地址 */
        netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; /* 使用第一个掩码 */
    else /* 如果没有IP地址,说明是C类网络(局域网) */
        netmask = 0xffffff; /* 掩码设置为255.255.255.0 */


    /* 编译过滤器 */
    if (pcap_compile(btp_state.adapter, &fcode, packet_filter, 1, netmask) < 0) /* 1表示自动进行优化 */
    {
        fprintf(stderr, "[ERROR] Unable to compile the packet filter. Check the syntax.\n");
        pcap_freealldevs(alldevs);
        return ERROR_COMPILE_FILTER_FALIURE;
    }

    /* 应用过滤器 */
    if (pcap_setfilter(btp_state.adapter, &fcode) < 0)
    {
        fprintf(stderr, "[ERROR] Error setting the filter.\n");
        pcap_freealldevs(alldevs);
        return ERROR_SET_FILTER_FALIURE;
    }

    /* 开始抓包 */
    btp_state.state = 0;
    btp_state.timeout_flag = 0;
    printf("listening on %s...\n", d->description);
    pcap_freealldevs(alldevs);
    pcap_loop(btp_state.adapter, 0, packet_handler, NULL);

    system("pause");
    return 0;
}

/* 抓包回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    time_t local_tv_sec; /* 时间戳 */
    struct tm *ltime; /* 本地时间 */
    char timestr[16]; /* 格式化后的本地时间 */
    ETH_HEADER *eth_header; /* 以太网帧包头 */
    char str_mac[50]; /* 源MAC地址 */
    BTP_HEADER *btp_header;
    char *data;
    int i;

    /* 没有使用param */
    (VOID)(param);

    /* 格式化当前时间 */
    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

    /* 解析以太网帧 */
    eth_header = (ETH_HEADER *)pkt_data;
    format_mac(str_mac, eth_header->src_mac);
    printf("[ %s.%.6d ] receive package from %s:\n",
        timestr, header->ts.tv_usec, str_mac);

    /* 解析数据域 */
    btp_header = (BTP_HEADER *)(pkt_data + sizeof(ETH_HEADER));
    if (btp_header->protocol == BTP_PROTOCOL){ /* 确认是btp包 */
        printf("protocol=\tBTP \nversion=\t%d \ntype=\t%s \npid=\t%d \ndata_length=\t%d \n",
            btp_header->version, rec_type(btp_header->type), btp_header->pid, btp_header->data_length);

        if (btp_header->version > BTP_VERSION){
            kill_all("[ERROR] version mismatch.", ERROR_BAD_VERSION);
        }

        switch (btp_header->type){
        case BTP_HELLO_REQUEST:
            if (btp_state.state == 0){/* 初始,可握手 */
                if (!btp_send(eth_header, btp_header, BTP_HELLO_RESPONSE)){
                    if (pthread_create(&btp_state.pids[0], NULL, timer, NULL) == -1){/* 开启心跳线程 */
                        kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
                    }
                    btp_state.state = 1;/*握手后,可心跳,可数据,可断开*/
                }
                else{
                    kill_all("[ERROR] btp hello failed.", ERROR_BTP_HELLO_FAILED);
                }
            }
            break;
        case BTP_HEARTBEAT_REQUEST:
            if (btp_state.state == 1){
                btp_state.timeout_flag = (btp_state.timeout_flag + 1) % 1000;
                if (btp_send(eth_header, btp_header, BTP_HEARTBEAT_RESPONSE) != 0)
                    kill_all("[ERROR] btp heartbeat failed.", ERROR_BTP_HEARTBEAT_FAILED);
            }
            break;
        case BTP_DATA:
            if (btp_state.state == 1){
                data = (char*)(pkt_data + sizeof(ETH_HEADER)+sizeof(BTP_HEADER));
                printf("recv data: ");
                for (i = 0; i < btp_header->data_length; i++)
                    printf("%c", data[i]);
                printf("\n");
            }
            break;
        case BTP_BYE_REQUEST:
            if (btp_state.state == 1){
                if (btp_send(eth_header, btp_header, BTP_BYE_RESPONSE) != 0)
                    kill_all("[ERROR] btp bye failed.", ERROR_BTP_BYE_FAILED);
                else
                    kill_all("[INFO] btp finished.", 0);
            }
            break;
        }
    }

}

int btp_send(ETH_HEADER* eth_header, BTP_HEADER *btp_header, u_char type){
    int index;
    u_char sendbuf[SEND_BUFSIZE]; /* 发送buffer */
    u_char temp_mac[6];

    /* 制作新的eth_header */
    copy_mac(temp_mac, eth_header->src_mac);
    copy_mac(eth_header->src_mac, eth_header->dest_mac);
    copy_mac(eth_header->dest_mac, temp_mac);

    /* 制作新的btp_header */
    btp_header->type = type;
    btp_header->data_length = 0;

    /* 发包 */
    memcpy(sendbuf, eth_header, sizeof(ETH_HEADER));
    index = sizeof(ETH_HEADER);
    memcpy(&sendbuf[index], btp_header, sizeof(BTP_HEADER));
    index += sizeof(BTP_HEADER);

    if (pcap_sendpacket(btp_state.adapter, /* 网卡句柄 */
        sendbuf, /* 要发送的帧 */
        index /* 帧的大小 */
        ) != 0) {
        fprintf(stderr, "[ERROR] Error sending the packet: %s\n", pcap_geterr(btp_state.adapter));
        return ERROR_SENDING_FAILURE;
    }
    printf("response a %s package.\n", rec_type(type));
    return 0;
}


void *timer(void *arg){
    int timeout_flag;
    printf("timer start.\n");
    while (1){
        timeout_flag = btp_state.timeout_flag;
        Sleep(3000);
        if (timeout_flag == btp_state.timeout_flag){ /* 没有改变 */
            kill_all("[ERROR] time out.", ERROR_BTP_TIMEOUT);
            break;
        }
        printf("timer ok\n");
    }
    return NULL;
}

void kill_all(char *msg, int error_code){
    printf("%s\n", msg);
    pcap_breakloop(btp_state.adapter);
    system("pause");
    exit(error_code);
}

char* rec_type(u_char type){
    switch (type){
    case BTP_HELLO_REQUEST:
        return "BTP_HELLO_REQUEST";
    case BTP_HELLO_RESPONSE:
        return "BTP_HELLO_RESPONSE";
    case BTP_HEARTBEAT_REQUEST:
        return "BTP_HEARTBEAT_REQUEST";
    case BTP_HEARTBEAT_RESPONSE:
        return "BTP_HEARTBEAT_RESPONSE";
    case BTP_DATA:
        return "BTP_DATA";
    case BTP_BYE_REQUEST:
        return "BTP_BYE_REQUEST";
    case BTP_BYE_RESPONSE:
        return "BTP_BYE_RESPONSE";
    default:
        return "BAD_TYPE";
    }
}
void copy_mac(u_char *mac1, u_char *mac2){
    mac1[0] = mac2[0];
    mac1[1] = mac2[1];
    mac1[2] = mac2[2];
    mac1[3] = mac2[3];
    mac1[4] = mac2[4];
    mac1[5] = mac2[5];
}

void format_mac(char* lpHWAddrStr, const unsigned char *HWAddr)
{
    int i;
    short temp;
    char szStr[3];

    strcpy(lpHWAddrStr, "");
    for (i = 0; i < 6; ++i)
    {
        temp = (short)(*(HWAddr + i));
        _itoa(temp, szStr, 16);
        if (strlen(szStr) == 1)
            strcat(lpHWAddrStr, "0");
        strcat(lpHWAddrStr, szStr);
        if (i < 5)
            strcat(lpHWAddrStr, ":");
    }
}

注意这里的kill_all函数,你可以自己去编写中止线程的方法,比如cancel或者kill,并且pcap_breakloop(btp_state.adapter);这一句不是必须的,是因为我使用了system("pause");想查看一下输出结果,造成exit(error_code);没有执行,所以使用这一句来结束winpcap抓包。正常的来说,你可以使用打印输出到文件而不是到屏幕,然后注释掉这句,让exit执行,就能够中止所有线程了。

整体思路很容易看懂,state就是状态机;pids保存着线程的结构体,用来结束线程;每次收到心跳包后就更新timeout_flag,timer根据这个值是否更新来判断是否心跳超时了,从而判断是否断开连接了,注意接收者是不需要发包计时线程的,因为它只管接受新包和回包,不需要期待对面回包;adapter是网卡句柄。

(3)测试接收者

把sender稍微改造一下。

BTPsender.h

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <winsock.h>
#include <pthread.h>

#define SEND_BUFSIZE                    1024
#define TIMER_SLEEPTIME                 2000
#define ERROR_GENERAL                   -1
#define ERROR_FINDALLDEVS_FAILURE       -2
#define ERROR_INTERFACES_NOT_FOUND      -3
#define ERROR_BAD_INPUT                 -4
#define ERROR_OPEN_ADAPTER_FAILURE      -5
#define ERROR_SENDING_FAILURE           -6
#define ERROR_INVALID_DATALINK_TYPE     -7
#define ERROR_COMPILE_FILTER_FALIURE    -8
#define ERROR_SET_FILTER_FALIURE        -9
#define ERROR_CREATE_THREAD             -10
#define ERROR_BTP_HELLO_FAILED          -11
#define ERROR_BTP_HEARTBEAT_FAILED      -12
#define ERROR_BTP_BYE_FAILED            -13
#define ERROR_BTP_TIMEOUT               -14
#define ERROR_BAD_VERSION               -15


#define BTP_HELLO_REQUEST       0x01
#define BTP_HELLO_RESPONSE      0x02
#define BTP_HEARTBEAT_REQUEST   0x03
#define BTP_HEARTBEAT_RESPONSE  0x04
#define BTP_DATA                0x05
#define BTP_BYE_REQUEST         0x06
#define BTP_BYE_RESPONSE        0x07
#define BTP_PROTOCOL            0x42
#define BTP_VERSION             0x01

typedef struct ETH_HEADER
{
    u_char dest_mac[6];
    u_char src_mac[6];
    u_short etype;
}ETH_HEADER;

typedef struct BTP_HEADER{
    u_char protocol;
    u_char version;
    u_char type;
    u_char pid;
    u_short data_length;
}BTP_HEADER;

typedef struct BTP_STATE{
    int state; /* 状态:0=初始(可握手) 1=握手后(可心跳、可数据、可断开)*/
    pthread_t pids[1]; /* 只有一个心跳线程 */
    int timeout_flag; /* 心跳超时标记,每次收包都进行+1,如果计时线程发现两次的flag都相同,说明断开了 */
    pcap_t *adapter; /* 网卡句柄 */
}BTP_STATE;

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);/* 抓包回调函数 */
void format_mac(LPSTR lpHWAddrStr, const unsigned char *HWAddr);/* mac地址格式化函数 */

int btp_send(ETH_HEADER* eth_header, BTP_HEADER *btp_header, u_char type);
void *timer(void *arg);
void kill_all(char *msg, int error_code);

void copy_mac(u_char *mac1, u_char *mac2);
char* rec_type(u_char type);

BTPsender.c

#include "BTPrecver.h"


BTP_STATE btp_state;

int main()
{
    char errbuf[PCAP_ERRBUF_SIZE]; /* 错误信息buffer */
    u_int netmask; /* 掩码信息 */
    char packet_filter[] = "ether src 00:50:56:C0:00:08 and ether dst 00:0C:29:86:B8:C8"; /* 过滤规则 */
    struct bpf_program fcode; /* 存储编译好的过滤码 */

    pcap_if_t *alldevs; /* 全部网卡列表 */
    pcap_if_t *d; /* 一个网卡 */
    int did; /* 选择的网卡ID */

    int i = 0; /* 迭代 */

    /*查找网卡*/
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
        fprintf(stderr, "[ERROR] pcap_findalldevs error: %s\n", errbuf);
        return ERROR_FINDALLDEVS_FAILURE;
    }

    /* 选择网卡d */
    for (d = alldevs, i = 0; d; d = d->next) {
        if (d->description)
            printf("NO.%d: %s\n", ++i, d->description);
        else
            printf("[WARN] No description available\n");
    }

    if (i == 0) {
        printf("[ERROR] No interfaces found! Make sure WinPcap is installed.\n");
        return ERROR_INTERFACES_NOT_FOUND;
    }

    printf("[INFO] Enter the interface number (1-%d):", i);
    scanf("%d", &did);

    if (did < 1 || did > i) {
        printf("[ERROR] Interface number out of range.\n");
        pcap_freealldevs(alldevs);
        return ERROR_BAD_INPUT;
    }

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

    /* 打开网卡 */
    if ((btp_state.adapter = pcap_open_live(d->name, /* 设备名 */
        65536, /* 捕获数据包的长度(65536捕获所有数据包) */
        1, /* 混杂模式(非0表示使用混杂模式) */
        1000, /* 超时时间(0表示没有超时限制) */
        errbuf /* 错误缓存(存储错误信息) */
        )) == NULL) {
        fprintf(stderr, "[ERROR] Unable to open the adapter. %s is not supported by WinPcap\n");
        pcap_freealldevs(alldevs);
        return ERROR_OPEN_ADAPTER_FAILURE;
    }

    /* 检查链路层类型 */
    if (pcap_datalink(btp_state.adapter) != DLT_EN10MB) /* DLT_EN10MB指10Mb以太网 */
    {
        fprintf(stderr, "[ERROR] This program works only on Ethernet networks.\n");
        pcap_freealldevs(alldevs);
        return ERROR_INVALID_DATALINK_TYPE;
    }

    /* 检查地址类型 */
    if (d->addresses != NULL) /* 如果有IP地址 */
        netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; /* 使用第一个掩码 */
    else /* 如果没有IP地址,说明是C类网络(局域网) */
        netmask = 0xffffff; /* 掩码设置为255.255.255.0 */


    /* 编译过滤器 */
    if (pcap_compile(btp_state.adapter, &fcode, packet_filter, 1, netmask) < 0) /* 1表示自动进行优化 */
    {
        fprintf(stderr, "[ERROR] Unable to compile the packet filter. Check the syntax.\n");
        pcap_freealldevs(alldevs);
        return ERROR_COMPILE_FILTER_FALIURE;
    }

    /* 应用过滤器 */
    if (pcap_setfilter(btp_state.adapter, &fcode) < 0)
    {
        fprintf(stderr, "[ERROR] Error setting the filter.\n");
        pcap_freealldevs(alldevs);
        return ERROR_SET_FILTER_FALIURE;
    }

    /* 开始抓包 */
    btp_state.state = 0;
    btp_state.timeout_flag = 0;
    printf("listening on %s...\n", d->description);
    pcap_freealldevs(alldevs);
    pcap_loop(btp_state.adapter, 0, packet_handler, NULL);

    system("pause");
    return 0;
}

/* 抓包回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    time_t local_tv_sec; /* 时间戳 */
    struct tm *ltime; /* 本地时间 */
    char timestr[16]; /* 格式化后的本地时间 */
    ETH_HEADER *eth_header; /* 以太网帧包头 */
    char str_mac[50]; /* 源MAC地址 */
    BTP_HEADER *btp_header;
    char *data;
    int i;

    /* 没有使用param */
    (VOID)(param);

    /* 格式化当前时间 */
    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

    /* 解析以太网帧 */
    eth_header = (ETH_HEADER *)pkt_data;
    format_mac(str_mac, eth_header->src_mac);
    printf("[ %s.%.6d ] receive package from %s:\n",
        timestr, header->ts.tv_usec, str_mac);

    /* 解析数据域 */
    btp_header = (BTP_HEADER *)(pkt_data + sizeof(ETH_HEADER));
    if (btp_header->protocol == BTP_PROTOCOL){ /* 确认是btp包 */
        printf("protocol=\tBTP \nversion=\t%d \ntype=\t%s \npid=\t%d \ndata_length=\t%d \n",
            btp_header->version, rec_type(btp_header->type), btp_header->pid, btp_header->data_length);

        if (btp_header->version > BTP_VERSION){
            kill_all("[ERROR] version mismatch.", ERROR_BAD_VERSION);
        }

        switch (btp_header->type){
        case BTP_HELLO_REQUEST:
            if (btp_state.state == 0){/* 初始,可握手 */
                if (!btp_send(eth_header, btp_header, BTP_HELLO_RESPONSE)){
                    if (pthread_create(&btp_state.pids[0], NULL, timer, NULL) == -1){/* 开启心跳线程 */
                        kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
                    }
                    btp_state.state = 1;/*握手后,可心跳,可数据,可断开*/
                }
                else{
                    kill_all("[ERROR] btp hello failed.", ERROR_BTP_HELLO_FAILED);
                }
            }
            break;
        case BTP_HEARTBEAT_REQUEST:
            if (btp_state.state == 1){
                btp_state.timeout_flag = (btp_state.timeout_flag + 1) % 1000;
                if (btp_send(eth_header, btp_header, BTP_HEARTBEAT_RESPONSE) != 0)
                    kill_all("[ERROR] btp heartbeat failed.", ERROR_BTP_HEARTBEAT_FAILED);
            }
            break;
        case BTP_DATA:
            if (btp_state.state == 1){
                data = (char*)(pkt_data + sizeof(ETH_HEADER)+sizeof(BTP_HEADER));
                printf("recv data: ");
                for (i = 0; i < btp_header->data_length; i++)
                    printf("%c", data[i]);
                printf("\n");
            }
            break;
        case BTP_BYE_REQUEST:
            if (btp_state.state == 1){
                if (btp_send(eth_header, btp_header, BTP_BYE_RESPONSE) != 0)
                    kill_all("[ERROR] btp bye failed.", ERROR_BTP_BYE_FAILED);
                else
                    kill_all("[INFO] btp finished.", 0);
            }
            break;
        }
    }

}

int btp_send(ETH_HEADER* eth_header, BTP_HEADER *btp_header, u_char type){
    int index;
    u_char sendbuf[SEND_BUFSIZE]; /* 发送buffer */
    u_char temp_mac[6];

    /* 制作新的eth_header */
    copy_mac(temp_mac, eth_header->src_mac);
    copy_mac(eth_header->src_mac, eth_header->dest_mac);
    copy_mac(eth_header->dest_mac, temp_mac);

    /* 制作新的btp_header */
    btp_header->type = type;
    btp_header->data_length = 0;

    /* 发包 */
    memcpy(sendbuf, ð_header, sizeof(eth_header));
    index = sizeof(eth_header);
    if (pcap_sendpacket(btp_state.adapter, /* 网卡句柄 */
        sendbuf, /* 要发送的帧 */
        index /* 帧的大小 */
        ) != 0) {
        fprintf(stderr, "[ERROR] Error sending the packet: %s\n", pcap_geterr(btp_state.adapter));
        return ERROR_SENDING_FAILURE;
    }
    printf("response a %s package.\n", rec_type(type));
    return 0;
}


void *timer(void *arg){
    int timeout_flag;
    printf("timer start.\n");
    while (1){
        timeout_flag = btp_state.timeout_flag;
        Sleep(3000);
        if (timeout_flag == btp_state.timeout_flag){ /* 没有改变 */
            kill_all("[ERROR] time out.", ERROR_BTP_TIMEOUT);
            break;
        }
        printf("timer ok\n");
    }
    return NULL;
}

void kill_all(char *msg, int error_code){
    printf("%s\n", msg);
    pcap_breakloop(btp_state.adapter);
    system("pause");
    exit(error_code);
}

char* rec_type(u_char type){
    switch (type){
    case BTP_HELLO_REQUEST:
        return "BTP_HELLO_REQUEST";
    case BTP_HELLO_RESPONSE:
        return "BTP_HELLO_RESPONSE";
    case BTP_HEARTBEAT_REQUEST:
        return "BTP_HEARTBEAT_REQUEST";
    case BTP_HEARTBEAT_RESPONSE:
        return "BTP_HEARTBEAT_RESPONSE";
    case BTP_DATA:
        return "BTP_DATA";
    case BTP_BYE_REQUEST:
        return "BTP_BYE_REQUEST";
    case BTP_BYE_RESPONSE:
        return "BTP_BYE_RESPONSE";
    default:
        return "BAD_TYPE";
    }
}
void copy_mac(u_char *mac1, u_char *mac2){
    mac1[0] = mac2[0];
    mac1[1] = mac2[1];
    mac1[2] = mac2[2];
    mac1[3] = mac2[3];
    mac1[4] = mac2[4];
    mac1[5] = mac2[5];
}

void format_mac(char* lpHWAddrStr, const unsigned char *HWAddr)
{
    int i;
    short temp;
    char szStr[3];

    strcpy(lpHWAddrStr, "");
    for (i = 0; i < 6; ++i)
    {
        temp = (short)(*(HWAddr + i));
        _itoa(temp, szStr, 16);
        if (strlen(szStr) == 1)
            strcat(lpHWAddrStr, "0");
        strcat(lpHWAddrStr, szStr);
        if (i < 5)
            strcat(lpHWAddrStr, ":");
    }
}

看看实验结果:

后面time out是因为那个心跳线程没有直接结束,原因在前面已经说明了。

(4)编写发送者

sender.h

#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <winsock.h>
#include <pthread.h>

#define ETHERTYPE_IP                    0x0800 /* IP */
#define ERROR_GENERAL                   -1
#define ERROR_FINDALLDEVS_FAILURE       -2
#define ERROR_INTERFACES_NOT_FOUND      -3
#define ERROR_BAD_INPUT                 -4
#define ERROR_OPEN_ADAPTER_FAILURE      -5
#define ERROR_SENDING_FAILURE           -6
#define ERROR_INVALID_DATALINK_TYPE     -7
#define ERROR_COMPILE_FILTER_FALIURE    -8
#define ERROR_SET_FILTER_FALIURE        -9
#define ERROR_CREATE_THREAD             -10
#define ERROR_BTP_HELLO_FAILED          -11
#define ERROR_BTP_HEARTBEAT_FAILED      -12
#define ERROR_BTP_BYE_FAILED            -13
#define ERROR_BTP_TIMEOUT               -14
#define ERROR_BAD_VERSION               -15
#define ERROR_BTP_DATA_FAILED            -16
#define SEND_BUFSIZE                    1024
#define SEND_TIMES                      10000
#define SEND_INTVAL                     1000
#define TIMER_SLEEPTIME                 2000
#define TIMER_EXPECT_SLEEPTIME          1000

#define BTP_HELLO_REQUEST       0x01
#define BTP_HELLO_RESPONSE      0x02
#define BTP_HEARTBEAT_REQUEST   0x03
#define BTP_HEARTBEAT_RESPONSE  0x04
#define BTP_DATA                0x05
#define BTP_BYE_REQUEST         0x06
#define BTP_BYE_RESPONSE        0x07
#define BTP_PROTOCOL            0x42
#define BTP_VERSION             0x01

typedef struct ETH_HEADER
{
    u_char dest_mac[6];
    u_char src_mac[6];
    u_short etype;
}ETH_HEADER;

typedef struct BTP_HEADER{
    u_char protocol;
    u_char version;
    u_char type;
    u_char pid;
    u_short data_length;
}BTP_HEADER;

typedef struct BTP_STATE{
    int state; /* 状态:0=初始(可发送握手) 1=握手后(可心跳、可数据、可断开)*/
    pthread_t pids[3]; /* 0=心跳线程 1=接收线程 2=计时线程 */
    int timeout_flag; /* 心跳超时标记,每次收包都进行+1,如果计时线程发现两次的flag都相同,说明断开了 */
    pcap_t *adapter; /* 网卡句柄 */
    int pid;/* 这个是package id,包序号*/
    u_char sendbuf[SEND_BUFSIZE]; /* 发送buffer */
    int index; /* 发送buffer偏移 */
}BTP_STATE;


int send_btp_package(pcap_t *adapter, u_char *sendbuf, int index, u_char type, char* data, int len);
char* rec_type(u_char type);
void *timer(void *arg);
void *timer_expect(void *arg);
void *bye(void *arg);
void kill_all(char *msg, int error_code);
void btp_listen();
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
int send_btp_package(pcap_t *adapter, u_char *sendbuf, int index, int *pid, u_char type, char* data, int len, int te);
void format_mac(LPSTR lpHWAddrStr, const unsigned char *HWAddr);/* mac地址格式化函数 */

sender.c

#include "BTPsender.h"
BTP_STATE btp_state;

int main()
{
    pcap_t *adapter; /* 网卡句柄 */
    char errbuf[PCAP_ERRBUF_SIZE]; /* 错误信息buffer */
    u_int netmask; /* 掩码信息 */
    char packet_filter[] = "ether src 00:0C:29:86:B8:C8 and ether dst 00:50:56:C0:00:08"; /* 过滤规则 */
    struct bpf_program fcode; /* 存储编译好的过滤码 */
    ETH_HEADER eth_header; /* 以太网包头 */

    pcap_if_t *alldevs; /* 全部网卡列表 */
    pcap_if_t *d; /* 一个网卡 */
    int did; /* 选择的网卡ID */

    int i; /* 迭代 */

    /* 查找网卡 */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
        fprintf(stderr, "[ERROR] pcap_findalldevs error: %s\n", errbuf);
        return ERROR_FINDALLDEVS_FAILURE;
    }

    /* 选择网卡d */
    for (d = alldevs, i = 0; d; d = d->next) {
        if (d->description)
            printf("NO.%d: %s\n", ++i, d->description);
        else
            printf("[WARN] No description available\n");
    }

    if (i == 0) {
        printf("[ERROR] No interfaces found! Make sure WinPcap is installed.\n");
        return ERROR_INTERFACES_NOT_FOUND;
    }

    printf("[INFO] Enter the interface number (1-%d):", i);
    scanf("%d", &did);

    if (did < 1 || did > i) {
        printf("[ERROR] Interface number out of range.\n");
        pcap_freealldevs(alldevs);
        return ERROR_BAD_INPUT;
    }

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

    /* 打开网卡 */
    if ((btp_state.adapter = pcap_open_live(d->name, /* 设备名 */
        65536, /* 捕获数据包的长度(65536捕获所有数据包) */
        1, /* 混杂模式(非0表示使用混杂模式) */
        1000, /* 超时时间(0表示没有超时限制) */
        errbuf /* 错误缓存(存储错误信息) */
        )) == NULL) {
        fprintf(stderr, "[ERROR] Unable to open the adapter. %s is not supported by WinPcap\n");
        pcap_freealldevs(alldevs);
        return ERROR_OPEN_ADAPTER_FAILURE;
    }

    /* 检查链路层类型 */
    if (pcap_datalink(btp_state.adapter) != DLT_EN10MB) /* DLT_EN10MB指10Mb以太网 */
    {
        fprintf(stderr, "[ERROR] This program works only on Ethernet networks.\n");
        pcap_freealldevs(alldevs);
        return ERROR_INVALID_DATALINK_TYPE;
    }

    /* 检查地址类型 */
    if (d->addresses != NULL) /* 如果有IP地址 */
        netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; /* 使用第一个掩码 */
    else /* 如果没有IP地址,说明是C类网络(局域网) */
        netmask = 0xffffff; /* 掩码设置为255.255.255.0 */


    /* 编译过滤器 */
    if (pcap_compile(btp_state.adapter, &fcode, packet_filter, 1, netmask) < 0) /* 1表示自动进行优化 */
    {
        fprintf(stderr, "[ERROR] Unable to compile the packet filter. Check the syntax.\n");
        pcap_freealldevs(alldevs);
        return ERROR_COMPILE_FILTER_FALIURE;
    }

    /* 应用过滤器 */
    if (pcap_setfilter(btp_state.adapter, &fcode) < 0)
    {
        fprintf(stderr, "[ERROR] Error setting the filter.\n");
        pcap_freealldevs(alldevs);
        return ERROR_SET_FILTER_FALIURE;
    }

    /*目的PB的mac地址*/
    eth_header.dest_mac[0] = 0x00;
    eth_header.dest_mac[1] = 0x0C;
    eth_header.dest_mac[2] = 0x29;
    eth_header.dest_mac[3] = 0x86;
    eth_header.dest_mac[4] = 0xB8;
    eth_header.dest_mac[5] = 0xC8;

    /*源PA的mac地址*/
    eth_header.src_mac[0] = 0x00;
    eth_header.src_mac[1] = 0x50;
    eth_header.src_mac[2] = 0x56;
    eth_header.src_mac[3] = 0xC0;
    eth_header.src_mac[4] = 0x00;
    eth_header.src_mac[5] = 0x08;

    eth_header.etype = htons(ETHERTYPE_IP);

    memcpy(btp_state.sendbuf, ð_header, sizeof(eth_header));
    btp_state.index = sizeof(eth_header);

    /* 开启抓包线程 */
    btp_state.state = 0;
    btp_state.timeout_flag = 0;
    printf("listening on %s...\n", d->description);
    pcap_freealldevs(alldevs);
    if (pthread_create(&btp_state.pids[1], NULL, btp_listen, NULL) == -1){
        kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
    }


    /* 发送第一个包 */
    btp_state.pid = 1;
    if (send_btp_package(btp_state.adapter, btp_state.sendbuf, btp_state.index, &btp_state.pid, BTP_HELLO_REQUEST, NULL, 0, 1) != 0){
        kill_all("[ERROR] send hello failed.", ERROR_BTP_HELLO_FAILED);
    }
    /* 发包 

    Sleep(SEND_INTVAL);
    send_btp_package(adapter, sendbuf, index, &pid, BTP_HEARTBEAT_REQUEST, NULL, 0);
    send_btp_package(adapter, sendbuf, index, &pid, BTP_DATA, data, strlen(data));
    Sleep(SEND_INTVAL);
    send_btp_package(adapter, sendbuf, index, &pid, BTP_BYE_REQUEST, NULL, 0);
    */

    pthread_join(btp_state.pids[1], NULL);

    pcap_close(btp_state.adapter);
    system("pause");
    return 0;
}

void *timer(void *arg){
    int timeout_flag;
    printf("timer start.\n");
    while (1){
        timeout_flag = btp_state.timeout_flag;
        Sleep(3000);
        if (timeout_flag == btp_state.timeout_flag){ /* 没有改变 */
            kill_all("[ERROR] time out.", ERROR_BTP_TIMEOUT);
            break;
        }
        printf("timer ok\n");
    }
    return NULL;
}

void *timer_expect(void *arg){
    Sleep(TIMER_EXPECT_SLEEPTIME);
    pthread_testcancel();
    kill_all("[ERROR] timer_expect time out.", ERROR_BTP_TIMEOUT);
}

void *bye(void *arg){
    Sleep(10000);
    if (send_btp_package(btp_state.adapter, btp_state.sendbuf, btp_state.index, &btp_state.pid,
        BTP_BYE_REQUEST, NULL, 0, 1)
        != 0){
        kill_all("[ERROR] send bye failed.", ERROR_BTP_BYE_FAILED);
    }
}

void kill_all(char *msg, int error_code){
    printf("%s\n", msg);
    pcap_breakloop(btp_state.adapter);
    system("pause");
    exit(error_code);
}

void btp_listen(){
    pcap_loop(btp_state.adapter, 0, packet_handler, NULL);
}

/* 抓包回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    time_t local_tv_sec; /* 时间戳 */
    struct tm *ltime; /* 本地时间 */
    char timestr[16]; /* 格式化后的本地时间 */
    ETH_HEADER *eth_header; /* 以太网帧包头 */
    char str_mac[50]; /* 源MAC地址 */
    BTP_HEADER *btp_header;
    char *data;
    int i;

    /* 没有使用param */
    (VOID)(param);

    /* 格式化当前时间 */
    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

    /* 解析以太网帧 */
    eth_header = (ETH_HEADER *)pkt_data;
    format_mac(str_mac, eth_header->src_mac);
    printf("[ %s.%.6d ] receive package from %s:\n",
        timestr, header->ts.tv_usec, str_mac);

    /* 解析数据域 */
    btp_header = (BTP_HEADER *)(pkt_data + sizeof(ETH_HEADER));
    if (btp_header->protocol == BTP_PROTOCOL){ /* 确认是btp包 */
        printf("protocol=\tBTP \nversion=\t%d \ntype=\t%s \npid=\t%d \ndata_length=\t%d \n",
            btp_header->version, rec_type(btp_header->type), btp_header->pid, btp_header->data_length);

        if (btp_header->version > BTP_VERSION){
            kill_all("[ERROR] version mismatch.", ERROR_BAD_VERSION);
        }

        switch (btp_header->type){
        case BTP_HELLO_RESPONSE:
            if (btp_state.state == 0){/* 初始,可握手 */
                if (pthread_create(&btp_state.pids[0], NULL, timer, NULL) == -1){/* 开启心跳线程 */
                    kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
                }
                btp_state.state = 1;/*握手后,可心跳,可数据,可断开*/
                pthread_cancel(btp_state.pids[2]);/* 中止hello包的计时器 */
                if (send_btp_package(btp_state.adapter, btp_state.sendbuf, btp_state.index, &btp_state.pid, 
                                     BTP_HEARTBEAT_REQUEST, NULL, 0, 0) 
                                     != 0){
                    kill_all("[ERROR] send heartbeat failed.", ERROR_BTP_HEARTBEAT_FAILED);
                }
                if (send_btp_package(btp_state.adapter, btp_state.sendbuf, btp_state.index, &btp_state.pid,
                                     BTP_DATA, "test btp data.", strlen("test btp data."), 0) 
                                     != 0){
                    kill_all("[ERROR] send data failed.", ERROR_BTP_DATA_FAILED);
                }
                if (pthread_create(&btp_state.pids[0], NULL, bye, NULL) == -1){/* 开启bye线程,等待10秒发包 */
                    kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
                }
            }
            break;
        case BTP_HEARTBEAT_RESPONSE:
            if (btp_state.state == 1){
                btp_state.timeout_flag = (btp_state.timeout_flag + 1) % 1000;
                if (send_btp_package(btp_state.adapter, btp_state.sendbuf, btp_state.index, &btp_state.pid,
                                     BTP_HEARTBEAT_REQUEST, NULL, 0, 0)
                                     != 0){
                    kill_all("[ERROR] btp heartbeat failed.", ERROR_BTP_HEARTBEAT_FAILED);
                }
            }
            break;
        case BTP_BYE_RESPONSE:
            if (btp_state.state == 1){
                pthread_cancel(btp_state.pids[2]);/* 中止bye包的计时器 */
                kill_all("[INFO] btp finished.", 0);
            }
            break;
        }
    }

}

int send_btp_package(pcap_t *adapter, u_char *sendbuf, int index, int *pid, u_char type, char* data, int len, int te){
    BTP_HEADER package;

    package.protocol = BTP_PROTOCOL;
    package.pid = (*pid)++;
    package.version = BTP_VERSION;
    package.data_length = type == BTP_DATA ? len : 0;
    package.type = type;

    memcpy(&sendbuf[index], &package, sizeof(package));
    index += sizeof(package);

    if (type == BTP_DATA){
        memcpy(&sendbuf[index], data, len);
        index += len;
    }

    if (pcap_sendpacket(adapter, /* 网卡句柄 */
        sendbuf, /* 要发送的帧 */
        index /* 帧的大小 */
        ) != 0) {
        fprintf(stderr, "[ERROR] Error sending the packet: %s\n", pcap_geterr(adapter));
        return ERROR_SENDING_FAILURE;
    }
    printf("send a %s btp packet success.\n", rec_type(type));

    /* 启动一个计时器,期待回复 */
    if (te){
        if (pthread_create(&btp_state.pids[2], NULL, timer, NULL) == -1){
            kill_all("[ERROR] create thread failed.", ERROR_CREATE_THREAD);
        }
    }
    return 0;
}

char* rec_type(u_char type){
    switch (type){
    case BTP_HELLO_REQUEST:
        return "BTP_HELLO_REQUEST";
    case BTP_HELLO_RESPONSE:
        return "BTP_HELLO_RESPONSE";
    case BTP_HEARTBEAT_REQUEST:
        return "BTP_HEARTBEAT_REQUEST";
    case BTP_HEARTBEAT_RESPONSE:
        return "BTP_HEARTBEAT_RESPONSE";
    case BTP_DATA:
        return "BTP_DATA";
    case BTP_BYE_REQUEST:
        return "BTP_BYE_REQUEST";
    case BTP_BYE_RESPONSE:
        return "BTP_BYE_RESPONSE";
    default:
        return "BAD_TYPE";
    }
}

void format_mac(char* lpHWAddrStr, const unsigned char *HWAddr)
{
    int i;
    short temp;
    char szStr[3];

    strcpy(lpHWAddrStr, "");
    for (i = 0; i < 6; ++i)
    {
        temp = (short)(*(HWAddr + i));
        _itoa(temp, szStr, 16);
        if (strlen(szStr) == 1)
            strcat(lpHWAddrStr, "0");
        strcat(lpHWAddrStr, szStr);
        if (i < 5)
            strcat(lpHWAddrStr, ":");
    }
}

这里的实现上,没有单独地开一个发送线程,只简单地开了一个等待10秒后发送BTP断开包的线程。

注意发送者的监听需要调换源MAC和目的MAC,因为是虚拟机PB端发过来的。

每次发送期待回复的包后,都会开一个定时器,来判断是否超时。

(5)整体测试

正常实验结果的sender / PA 端:

上来就发了一个hello包,收到回复后开始不断发送和接受心跳包,显示timer start和timer ok,然后10秒后发送了一个bye包:

完成了整个过程。我们再试试中途关闭PB端的程序:

可以看到心跳包超时了。我们再试试一开始就不开PB端的程序:

可以看到PA发送的hello包没有人回复,造成计时器超时了。

三、总结

整个实验过程非常好玩,只不过时间仓促没有测试完所有的分支,如果你也有兴趣,可以在这里(密码:fqqy)下载代码工程文件,原理上或者程序中有什么错误,欢迎邮件或评论指正。

参考资料

1、《VS2015中配置Pthread》

2、《Linux中pthread线程使用详解》

3、《终止正在运行的子线程(一、几种方式的介绍)》

4、《linux下pthread_cancel无法取消线程的原因》

5、《基于VS2013配置pthread》

6、《VS2013中引用dll目录的配置方法》

7、《pthread-win32静态库的编译和使用方法》

8、《使用pthread-win32工程编译静态库》


转载请注明出处http://www.bewindoweb.com/212.html | 三颗豆子
分享许可方式知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
重大发现:转载注明原文网址的同学刚买了彩票就中奖,刚写完代码就跑通,刚转身就遇到了真爱。
你可能还会喜欢
具体问题具体杠