推荐两个UDP可靠传输库

C/C++代码 blackfeather

 

可靠传输必然优先考虑TCP,但是不同人遇到不同问题,可能会有变态的情况,用UDP也要达到TCP那样保持发送顺序和可靠性,还要类似未连接的状态。

偶然间看到这两个库,测试了一下效果确实不错,分享一下。

 

1.ENet

ENet 是一个易用的、可移植的 UDP 网络开发包,主要功能包括连接管理、可靠的按顺序的多通道包传输机制、包分解和重新打包、避免堵塞机制等。

经测试,此库用于局域网没多大问题,但是在公网环境里,速度很差,伪连接还经常断开。100000字节的数据发了十几秒,还经常有几次发了一半连接就断开了。测试为公司网络--本VPS(香港机房)

官方链接: http://enet.bespin.org/Tutorial.html 

示例:

建立server

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 = 1234;  
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 */);   
 
if (server == NULL) { 
     fprintf (stderr, 
               "An error occurred while trying to create an ENet server host.\n";); 
     exit (EXIT_FAILURE);  
} 
...  
enet_host_destroy(server);

 

 

事件处理

ENetEvent event; 
/* Wait up to 1000 milliseconds for an event. */ 
while (enet_host_service (client, & event, 1000) > 0) 
{ 
    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"; 
        break; 
    case ENET_EVENT_TYPE_RECEIVE: 
        printf ("A packet of length %u containing %s was received from %s on channel %u.\n", 
                event.packet -> dataLength, 
                event.packet -> data, 
                event.peer -> data, 
                event.channelID); 
        /* 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", event.peer -> data); 
        /* Reset the peer's client information. */ 
        event.peer -> data = NULL; 
    } 
} 
... 
... 
...


在使用上,还有channelID和限速等功能,使用起来很方便,static release大概40Kb,整个工程文件数不错,提供了VS工程可以直接编译测试。

 

2.UDT

UDT 是一个可靠的基于UDP的数据传输协议,主要为应用程序间提供高效高速的广域网数据传输功能。UDT 使用 UDP 协议来传输大数据块,通过它的可靠性控制和拥塞控制机制。该协议比 TCP 的传输速度要快,具有高可配置性和各种不同的拥塞控制算法。

这个库宣传最大的特点就是快!在可靠传输的基础上,是TCP数据传输的4倍多!不过博主测试,这个库提供的函数并不像ENet那样高度封装使用方便,比较像TCP socket那一套函数调用流程,对于UDP独立包的概念也没有了,转成了数据流的方式。

官方链接: http://udt.sourceforge.net/udt4/index.htm

示例:

建立server

#include <arpa/inet.h> 
#include <udt.h> 
#include <iostream.h> 
 
using namespace std; 
 
int main() 
{ 
UDTSOCKET serv = UDT::socket(AF_INET, SOCK_STREAM, 0); 
 
sockaddr_in my_addr; 
my_addr.sin_family = AF_INET; 
my_addr.sin_port = htons(9000); 
my_addr.sin_addr.s_addr = INADDR_ANY; 
memset(&(my_addr.sin_zero), '\0', 8); 
 
if (UDT::ERROR == UDT::bind(serv, (sockaddr*)&my_addr, sizeof(my_addr))) 
{ 
  cout << "bind: " << UDT::getlasterror().getErrorMessage(); 
  return 0; 
} 
 
UDT::listen(serv, 10); 
 
int namelen; 
sockaddr_in their_addr; 
 
UDTSOCKET recver = UDT::accept(serv, (sockaddr*)&their_addr, &namelen); 
 
char ip[16]; 
cout << "new connection: " << inet_ntoa(their_addr.sin_addr) << ":" << ntohs(their_addr.sin_port) << endl; 
 
char data[100]; 
 
if (UDT::ERROR == UDT::recv(recver, data, 100, 0)) 
{ 
  cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl; 
  return 0; 
} 
 
cout << data << endl; 
 
UDT::close(recver); 
UDT::close(serv); 
 
return 1; 
}


客户端示例

#include <iostream> 
#include <udt.h> 
#include <arpa/inet.h> 
 
using namespace std; 
using namespace UDT; 
 
int main() 
{ 
UDTSOCKET client = UDT::socket(AF_INET, SOCK_STREAM, 0); 
 
sockaddr_in serv_addr; 
serv_addr.sin_family = AF_INET; 
serv_addr.sin_port = htons(9000); 
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr); 
 
memset(&(serv_addr.sin_zero), '\0', 8); 
 
// connect to the server, implict bind 
if (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr))) 
{ 
  cout << "connect: " << UDT::getlasterror().getErrorMessage(); 
  return 0; 
} 
 
char* hello = "hello world!\n"; 
if (UDT::ERROR == UDT::send(client, hello, strlen(hello) + 1, 0)) 
{ 
  cout << "send: " << UDT::getlasterror().getErrorMessage(); 
  return 0; 
} 
 
UDT::close(client); 
 
return 1; 
}


各位看情况来使用吧。

 

评论列表:

发表评论: