• 可在网络不好的环境下运行的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;
    
    }
    
  • 相关阅读:
    Java 快速入门-06-JDK 目录文件说明
    Java快速入门-05-数组循环条件 实例《延禧攻略》
    腾讯云服务器 选购+远程控制 图文教程
    无法获得锁 /var/lib/dpkg/lock
    Ubuntu 安装 PhpMyAdmin 图文教程
    基于Redis的BloomFilter算法去重
    CAP理论
    Linux常用命令回顾
    基于Solr实现HBase的二级索引
    Solr搜索服务架构图
  • 原文地址:https://www.cnblogs.com/allen8807/p/1900452.html
Copyright © 2020-2023  润新知