本文是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 */);
#endifif (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 */);
#endifif (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);
#endifif (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");
#endifif (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;}

转载于:https://www.cnblogs.com/allen8807/archive/2010/12/10/1900452.html

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

  1. Windows下运行Fast DDS示例程序(包含.idl文件的使用方法)

    本文默认以二进制方式安装好了 Fast DDS(安装包可以从官网获取,建议使用下载工具提速).运行环境是Windows. 本文介绍了两种方法,一种是直接编译运行,另一种是从.idl文件编译运行. 准备 ...

  2. win10环境下 运行debug程序

    百度网盘:链接:https://pan.baidu.com/s/1y6omgW6fI-gT3Dp-0hutOg    提取码:iw4l CSDN0积分下载:https://download.csdn. ...

  3. sublime text3 怎么配置、运行python_【IT专家】Sublime Text3配置在可交互环境下运行python快捷键...

    本文由我司收集整编,推荐下载,如有疑问,请与我司联系 Sublime Text3 配置在可交互环境下运行 python 快捷键 2015/06/04 19131 安装插件 在 Sublime Text ...

  4. 成功解决Windows10环境下运行Linux系统下的.sh文件

    成功解决Windows10环境下运行Linux系统下的.sh文件 目录 解决问题 解决方法 解决问题 Windows10环境下运行Linux系统下的.sh文件 解决方法 .sh是shell scrip ...

  5. KDE应用如何在GNOME环境下运行?

    KDE应用如何在GNOME环境下运行? 2014/03/19 | 分类: IT技术 | 0 条评论 | 标签: GNOME, KDE 分享到:1 本文由 伯乐在线 - honpey 翻译自 howto ...

  6. 关于Fiori MyAccount无法在standalone环境下运行的问题

    Sent: Tuesday, 8 September, 2015 7:35 PM 首先说昨天遇到的offlineInterface.js在standalone下运行无法加载的问题.我们在本地run a ...

  7. 关于MyAccount无法在standalone环境下运行的问题

    Sent: Tuesday, 8 September, 2015 7:35 PM 首先说昨天遇到的offlineInterface.js在standalone下运行无法加载的问题.我们在本地run a ...

  8. MySQL在Docker环境下运行基础

    墨墨导读:MySQL在Docker环境下运行的基础操作,本文从Docker介绍.安装Docker.MySQL安装部署几个方面展开介绍. 另:墨天轮邀请到原厂专家直播解析MySQL的性能管理,欢迎大家报 ...

  9. FD.io VPP环境下运行用户应用程序教程

    FD.io VPP环境下运行用户应用程序教程 RToax 2020年9月 相关文章:<FD.io VPP利用iperf3进行UDP灌包测试-英特尔X520万兆网卡> 1. VPP简介 VP ...

最新文章

  1. 转 Android自动测试之monkeyrunner工具(二)
  2. PHP微信开发框架LaneWeChat框架简介
  3. C语言90道试题资料
  4. java web 分页技术_javaweb分页的后端实现
  5. MySQL架构设计相关的方式方法和软件介绍
  6. 如何在 Mac 上的照片中创建幻灯片?
  7. 向下兼容性格什么意思_向下兼容是什么意思
  8. ext3转化为ext4
  9. 【Pix4d精品教程】垂直摄影空三加密生成DOM和DSM,并按10m间距提取高程点,生成等高线
  10. 2.4 数值分析: Doolittle直接三角分解法
  11. Sublime 高亮汇编asm文件
  12. 标识符的命名规则和规范
  13. 怎样屏蔽(去掉)csdn博客侧栏广告
  14. Excel 怎么把多个excel工作表合并在一起
  15. umi路由懒加载和权限验证(基于React)
  16. 算法梳理(三)决策树
  17. 基于FPGA的以太网UDP协议实现过程记录
  18. Cryptozoic隐生宙使用指南
  19. php2020春节倒计时,春节倒计时_2019_01_21
  20. JPG图片压缩成PNG图片怎么压缩,压缩的方法

热门文章

  1. 0501 0503 模块区别_金川区西门子控制器模块CPU313C哪里有
  2. rocketmq mysql_聊聊rocketmq-mysql的BinlogPositionManager
  3. SQLite Tutorial 1 在ubuntu上安装SQLite 3.8.2
  4. indigo中用roslaunch启动RViz观察机器人模型
  5. android获取网络视频缩略图,Android 获取视频(本地和网络)缩略图的解决方案
  6. Flask 和 Google App Engine 部署模型服务
  7. matlab神经网络(二)-bp神经网络,MATLAB神经网络(2) BP神经网络的非线性系统建模——非线性函数拟合...
  8. python json的中文读取与中文写入
  9. linux如何压缩磁盘,Linux初级运维(十二)——磁盘及文件系统管理
  10. 《C++游戏开发》笔记十一 平滑动画:不再颤抖的小雪花