可在网络不好的环境下运行的ENet示例程序

  本文是ENet的示例程序。因为仅是作为示例所以程序并没有做支持多请求处理。

  一开始按照教程写程序,在网络好的情况下(本机和局域网),可以成功完成传输,但是网络差时就不行了。

  用top监视时,发现程序消耗内存增长极快,但使用valgrind检查发现并无内存泄漏。于是怀疑ENet的可靠传输机制是当一个packet没发出时,就将这个packet保存在内存中,成功发出后才会被释放掉。当client被destroy时,这些包如果还没发出,就会一起被释放掉,所以文件没传完就结束了。于是考虑是不是可以让一个packet发完后再发下一个。(因为程序里几乎没有其它的处理过程,如果有的话,或许执行一遍后packet早就成功发出了--未测试,猜测而已)。

查看ENetPacket的定义

 

typedef struct _ENetPacket

{

   size_t                               referenceCount;  

   enet_uint32                     flags;           

   enet_uint8 *                    data;            

   size_t                               dataLength;      

   ENetPacketFreeCallback  freeCallback;    

} ENetPacket;

  发现有一个当packet被free时的回调函数:

ENetPacketFreeCallback  freeCallback;

  于是用这个回调函数来检测。

 

 

  采用这个方法后,内存果然不再增长了,但是传输时间长后,client端(发送端)会报超时错误。发送失败。

  于是想到两种方法,断点续传或将超时设为无限。这里为方便采用第二种。

  于是查看enet的超时检查函数在protocol.c里发现

 

static int

enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)

{

    ENetOutgoingCommand * outgoingCommand;

    ENetListIterator currentCommand, insertPosition;



    currentCommand = enet_list_begin (& peer -> sentReliableCommands);

    insertPosition = enet_list_begin (& peer -> outgoingReliableCommands);



    while (currentCommand != enet_list_end (& peer -> sentReliableCommands))

    {

       outgoingCommand = (ENetOutgoingCommand *) currentCommand;



       currentCommand = enet_list_next (currentCommand);



       if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)

         continue;



       if (peer -> earliestTimeout == 0 ||

           ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout))

         peer -> earliestTimeout = outgoingCommand -> sentTime;



       if (peer -> earliestTimeout != 0 &&

             (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= ENET_PEER_TIMEOUT_MAXIMUM ||

               (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit &&

                 ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= ENET_PEER_TIMEOUT_MINIMUM)))

       {

          enet_protocol_notify_disconnect (host, peer, event);



          return 1;

       }



       if (outgoingCommand -> packet != NULL)

         peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;

          

       ++ peer -> packetsLost;



       outgoingCommand -> roundTripTimeout *= 2;



       enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));



       if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&

           ! enet_list_empty (& peer -> sentReliableCommands))

       {

          outgoingCommand = (ENetOutgoingCommand *) currentCommand;



          peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;

       }

    }

    

    return 0;

}

  其中有一句 

outgoingCommand -> roundTripTimeout *= 2;

 

  把这句注释掉后,重新编译安装enet。

  之后再运行就可正常传输了。

  下面是程序说明:

安装enet的方法很简单按照源码安装三步曲即可 

 

./configure --prefix=安装路径

make

make install

  现在从ENet官网上下载的源码有两个版本enet-1.2.2和enet-1.3.0。1.2.2和1.3.0的说明文档和教程可以分别在源码包的docs目录下找到。

  两者的接口有些许变化。例如enet_host_create()和enet_host_connect()。

  程序里对通过判断版本号,对两者都支持。

  下面代码在gcc4.4.3下可以编译通过。编译时要加-lenet。

  程序里用了不少全局变量,这个不是好的编程习惯,这里仅作示例,为了方便而已。

  单文件编译命令示例(假设enet装在/usr/local/enetlib1.3.0/下)

 

$ gcc -o ENetDemo ENetDemo.c -I /usr/local/enetlib1.3.0/include/ -L /usr/local/enetlib1.3.0/lib/ -lenet

  以下为程序源码。程序的使用说明参看main函数前的注释。

/**

 * @file ENetDemo.c

 * @brief A demo of ENet. Clinet just sends one file. And server just handles one file.

 * @author allen

 * @date 2010-12-9

 * @other For convenient I used many global variables. And I know it's not good.

 */



#define _LARGE_FILES

#undef  _FILE_OFFSET_BITS   

#define _FILE_OFFSET_BITS   64  

#include <enet/enet.h>

#include <stdio.h>

#include<string.h>

#include<stdlib.h>



char * file_name;

int packet_size;

int per_wait_time;

int my_flag = 1;

/**

 * @brief this is a callback function. I want to use it check whether the packet was sent.

 */

ENetPacketFreeCallback packetCallback(ENetPacket * pac) {

    my_flag = 0;

};

/**

 * @brief the enetServer main funtion

 * @param port the port enet server bind to.

 */

void enetServer(int port) {

    ENetAddress address;

    ENetHost * server;



    /* Bind the server to the default localhost.     */

    /* A specific host address can be specified by   */

    /* enet_address_set_host (& address, "x.x.x.x"); */



    address.host = ENET_HOST_ANY;

    /* Bind the server to port 1234. */

    address.port = port;

#if ENET_VERSION ==  ENET_VERSION_CREATE(1,2,2)

    server = enet_host_create(& address /* the address to bind the server host to */,

            32 /* allow up to 32 clients and/or outgoing connections */,

            0 /* assume any amount of incoming bandwidth */,

            0 /* assume any amount of outgoing bandwidth */);

#elif ENET_VERSION ==  ENET_VERSION_CREATE(1,3,0)

    server = enet_host_create(&address/* the address to bind the server host to */,

            32 /* allow up to 32 clients and/or outgoing connections*/,

            2 /*allow up to 2 channels to be used, 0 and 1 */,

            0 /*assume any amount of incoming bandwidth*/,

            0 /* assume any amount of outgoing bandwidth */);

#endif

    if (server == NULL) {

        fprintf(stderr,

                "An error occurred while trying to create an ENet server host.\n");

        exit(EXIT_FAILURE);

    }



    ENetEvent event;

    char * filename = file_name;

    FILE *fp;

    puts(file_name);

    unsigned int sumsize = 0;

    /* Wait up to 5000 milliseconds for an event. */

    while (enet_host_service(server, & event, 5000) >= 0) {



        /*  printf("Type %d\n", event.type);*/

        switch (event.type) {

            case ENET_EVENT_TYPE_CONNECT:

                printf("A new client connected from %x:%u.\n",

                        event.peer -> address.host,

                        event.peer -> address.port);



                /* Store any relevant client information here. */

                /*  event.peer -> data = "Client information";*/

                static unsigned int num = 0;

                ENetAddress remote = event.peer->address; /*remote address*/

                char ip[256];

                static char peerInfo[256];

                enet_address_get_host_ip(&remote, ip, 256);

                num++;

                printf("ip:%s has connected. No.%u\n", ip, num);

                sprintf(peerInfo, "ip:%s No.%u connection.", ip, num);

                event.peer->data = (void*) peerInfo;

                break;



            case ENET_EVENT_TYPE_RECEIVE:

                printf("%s\n", (char*) (event.peer->data));

                if ((fp = fopen(filename, "ab")) == NULL) {

                    fprintf(stderr, "enetServer: file open error");

                    enet_packet_destroy(event.packet);

                    enet_host_destroy(server);

                    return;

                }



                size_t writeBytes = fwrite(event.packet -> data, sizeof (char), event.packet -> dataLength, fp);

                if (writeBytes != event.packet -> dataLength) {

                    fprintf(stderr, "enetServer: write to file error");

                }

                sumsize += writeBytes;

                printf("%d\n", writeBytes);

                fclose(fp);

                /* Clean up the packet now that we're done using it. */

                enet_packet_destroy(event.packet);

                break;

            case ENET_EVENT_TYPE_DISCONNECT:

                printf("%s disconected.\n", (char*) (event.peer -> data));

                /* Reset the peer's client information. */

                event.peer -> data = NULL;

                printf("get%d\n", sumsize);

                enet_host_destroy(server);

                return;

        }

    }

    enet_host_destroy(server);

}



/**

 * @brief this function used to send one file by enet.

 * @param fileName the filename

 * @param client the enethost

 * @param peer the target peer to send

 * @return 0 means success. 1 means failure.

 */



int sendFile(char * fileName, ENetHost * client, ENetPeer *peer) {

    const int failureCode = 1;

    const int successCode = 0;

    ENetEvent event;

    char * datas;

    int dataSize = packet_size;

    unsigned int sum = 0;

    FILE *fp;

    if ((fp = fopen(fileName, "rb")) == NULL) {

        fprintf(stderr, "sendFile: file read error");

        return failureCode;

    }



    datas = (char*) malloc(dataSize);

    if (datas == NULL) {

        fprintf(stderr, "sendFile: memory malloc error");

        fclose(fp);

        return failureCode;

    }



    size_t readBytes = 0;

    ENetPacket *packet;

    while (!feof(fp)) {

        readBytes = fread(datas, sizeof (char), dataSize, fp);

        packet = enet_packet_create(datas, readBytes, ENET_PACKET_FLAG_RELIABLE); /*create reliable packet*/

        if (packet == NULL) {

            fprintf(stderr, "sendFile:: packet error");

            fclose(fp);

            free(datas);

            return failureCode;

        }

        printf("sendFile: packet:%d\n", packet -> dataLength);



        int errorcode = 0;



        packet->freeCallback = (ENetPacketFreeCallback) packetCallback;

        if ((errorcode = enet_peer_send(peer, 0, packet)) != 0) {

            fprintf(stderr, "sendFile: enet_peer_send errorcode %d\n", errorcode);

            fclose(fp);

            free(datas);

            fprintf(stderr, "sendFile: sumsize%u\n", sum);

            return failureCode;

        }

        printf("sendFile: enet_peer_send errorcode %d\n", errorcode);



        do {

            errorcode = enet_host_service(client, &event, per_wait_time);

            if (errorcode > 0) {

                fprintf(stderr, "enetClient: eventtype:%d\n", event.type);

            }

        } while (my_flag != 0);

        my_flag = 1;

        printf(" service errorcode %d\n", errorcode);

        sum += readBytes;

        printf("sumSize%u\n", sum);

    }

    fclose(fp);

    free(datas);

    printf("total file sumsize%d\n", sum);

    return successCode;

}

/**

 * @brief the enetClient main funtion

 * @param hostname target IP or hostname

 * @param port target port

 */

void enetClient(char * hostname, int port) {

    ENetHost * client;

#if ENET_VERSION ==  ENET_VERSION_CREATE(1,2,2)

    client = enet_host_create(NULL /* create a client host */,

            1 /* only allow 1 outgoing connection */,

            57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,

            14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);

#elif ENET_VERSION ==  ENET_VERSION_CREATE(1,3,0)

    client = enet_host_create(NULL /* create a client host */,

            1 /*only allow 1 outgoing connection */,

            2 /*allow up to 2 channels to be used, 0 and 1  */,

            57600 / 8 /*56K modem with 56 Kbps downstream bandwidth */,

            14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);

#endif



    if (client == NULL) {

        fprintf(stderr,

                "enetClient: An error occurred while trying to create an ENet client host.\n");

        /*exit(EXIT_FAILURE);*/

        return;

    }



    ENetAddress address;

    ENetEvent event;

    ENetPeer *peer;



    /* Connect to host. */

    enet_address_set_host(& address, hostname);

    address.port = port;





#if ENET_VERSION ==  ENET_VERSION_CREATE(1,2,2)

    /* Initiate the connection, allocating the two channels 0 and 1. */

    peer = enet_host_connect(client, & address, 2);

#elif ENET_VERSION ==  ENET_VERSION_CREATE(1,3,0)

    /* Initiate the connection, allocating the two channels 0 and 1. */

    peer = enet_host_connect(client, & address, 2, 0);

#endif

    if (peer == NULL) {

        fprintf(stderr,

                "enetClient: No available peers for initiating an ENet connection.\n");

        /*exit(EXIT_FAILURE);*/

        return;

    }



    /* Wait up to 10 seconds for the connection attempt to succeed. */

    if (enet_host_service(client, &event, 10000) > 0 &&

            event.type == ENET_EVENT_TYPE_CONNECT) {

        printf("enetClient: Connection %s:%d succeeded.", hostname, port);

        /*send a file*/

        int errCode = sendFile(file_name, client, peer);

        if (errCode != 0) {

            puts("send error\n");

        }

        /*send file end*/

    } else {

        /* Either the 10 seconds are up or a disconnect event was */

        /* received. Reset the peer in the event the 5 seconds   */

        /* had run out without any significant event.            */

        enet_peer_reset(peer);

        printf("enetClient: Connection %s:%d failed.", hostname, port);

    }



    enet_peer_disconnect(peer, 0);

    /* Allow up to 3 seconds for the disconnect to succeed and drop any packets received packets. */

    while (enet_host_service(client, & event, 3000) > 0) {

        switch (event.type) {

            case ENET_EVENT_TYPE_RECEIVE:

                enet_packet_destroy(event.packet);

                break;

            case ENET_EVENT_TYPE_DISCONNECT:

                puts("Disconnection succeeded.");

                enet_host_destroy(client);

                return;

        }

    }

    /* We've arrived here, so the disconnect attempt didn't */

    /* succeed yet.  Force the connection down.             */

    enet_peer_reset(peer);

    puts("Disconnection force succeeded.");

    enet_host_destroy(client);

}

/**

 * @brief as server : ENetDemo s port filename

 *                    port means the port server to bind.

 *                    filename is the file server recieved save as.

 *        as client : ENetDemo c IP port filename packetsize perwaittime(ms)

 *                    IP is the target server IP(hostname)

 *                    port the target server listened.

 *                    filename the file client send

 *                    packetsize the client send per time.

 *                    perwaittime (please see the enet_host_service() )

 */

int main(int argc, char ** argv) {

#if ENET_VERSION ==  ENET_VERSION_CREATE(1,2,2)

    /* Initiate the connection, allocating the two channels 0 and 1. */

    printf("Enet Version 1.2.2\n");

#elif ENET_VERSION ==  ENET_VERSION_CREATE(1,3,0)

    /* Initiate the connection, allocating the two channels 0 and 1. */

    printf("Enet Version 1.3.0\n");

#endif

    if (argc < 4) {

        fprintf(stderr, "Wrong Format. ENetDemo [c IP port filename packetsize perwaittime(ms)] [ s port filename] \n");

        return EXIT_FAILURE;

    }



    int err = 0;

    if ((err = enet_initialize()) != 0) {

        fprintf(stderr, "An error occurred while initializing ENet %d.\n", err);

        return EXIT_FAILURE;

    }

    atexit(enet_deinitialize);

    if (strcmp(argv[1], "s") == 0) {

        puts("SERVER");

        file_name = argv[3];

        puts(file_name);

        enetServer(atoi(argv[2]));

    }

    if (strcmp(argv[1], "c") == 0) {

        puts("CLIENT");

        if (argc < 7) {

            fprintf(stderr, "Wrong Format. ENetDemo [c IP port filename packetsize perwaittime(ms)] [ s port filename] \n");

            return EXIT_FAILURE;

        }

        file_name = argv[4];

        printf("send file %s\n", file_name);

        packet_size = atoi(argv[5]);

        per_wait_time = atoi(argv[6]);

        enetClient(argv[2], atoi(argv[3]));

    }

    int endPause;

    puts("enter any key to end\n");

    scanf("%d", &endPause);

    return 0;



}

你可能感兴趣的:(net)