发现关于coturn源码分析的文章没有,由于只接触其中一部分功能,在此粗略解析一下。
当你想了解coturn源码时,说明你已经具备一点p2p通信知识,以及STUN/TURN相关协议,以及查看config文件,知晓coturn的具体功能。
本人还在coturn上增添一些打印STUN协议解析的日志代码,以便清晰明了的直到客户端接收和发送了什么数据。
coturn 通过libevent/bufferevent进行高并发处理。turnserver是其主程序,它 的main函数入口在src/apps/relay/mainrelay.c
中。除此之外,还同时编译出多个工具程序。
bin/turnadmin bin/turnutils_peer
bin/turnserver bin/turnutils_rfc5769check
bin/turnutils_natdiscovery bin/turnutils_stunclient
bin/turnutils_oauth bin/turnutils_uclient
而且在src/apps/relay/dbdrivers/
路径下,支持多个数据库的接口:mongodb/mysql/pgsql/redis/sqlite,里面有个文件dbdriver.c
下自动检测系统中有哪个数据库。在turndb/
文件夹下,还有对应数据库的SQL初始化命令。
—总之,服务周全,良心产品。
程序初始运行流程如下。
函数main运行setup_server默认根据4核CPU建立9个服务,见setup_server 函数。
/* relay threads plus auth threads plus main listener thread */
/* plus admin thread */
/* udp address listener thread(s) will start later */
4个relay(4个CPU) + 3 个auth + 1个main listener + 1 个admin thread
服务相关数据都由结构体turn_params 连接衍生下去,整个运行过程基本都是对turn_params的各个成员初始化,当然还有几个服务,及其对应的event_base。
static struct auth_server authserver[256];
static struct relay_server *general_relay_servers[1+((turnserver_id)-1)];
static struct relay_server *udp_relay_servers[1+((turnserver_id)-1)];
函数run_auth_server_thread处理auth_server结构体
函数setup_admin_thread处理admin_server结构体
服务setup_relay_server处理message_to_relay结构体,客户端访问的数据进行处理....
read_client_connection才是我们最为关心的客户端访问入口函数!见 1.3 客户端访问入口函数
一般main函数步骤都是:初始化参数->载入配置文件->根据配置运行加载,coturn也不例外。src/apps/relay/mainrelay.c
中定义的结构体turn_params
是整个程序数据结构的根部,main函数主要对其进行初始化。
int main(int argc, char **argv){
...参数初始化,加载配置..省略...
setup_server(); //建立服务器监听主函数
drop_privileges();
run_listener_server(&(turn_params.listener));
return 0;
}
建立服务引擎主函数,在src/apps/relay/netengine.c
文件中定义,建立的监听服务好多啊!而且运行过程都不一样。
void setup_server(void)
{
evthread_use_pthreads();
pthread_mutex_init(&mutex_bps, NULL);
authserver_number = 1 + (authserver_id)(turn_params.cpus / 2);
if(authserver_number < MIN_AUTHSERVER_NUMBER) authserver_number = MIN_AUTHSERVER_NUMBER;
#if !defined(TURN_NO_THREAD_BARRIERS)
/* relay threads plus auth threads plus main listener thread */
/* plus admin thread */
/* udp address listener thread(s) will start later */
barrier_count = turn_params.general_relay_servers_number+authserver_number+1+1;
//默认4+3+1+1个服务,下面一个一个建立
#endif
setup_listener();
allocate_relay_addrs_ports();
setup_barriers();
setup_general_relay_servers();
if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_THREAD)
setup_socket_per_thread_udp_listener_servers(); //默认这个UDP监听
else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_ENDPOINT)
setup_socket_per_endpoint_udp_listener_servers();
else if(turn_params.net_engine_version == NEV_UDP_SOCKET_PER_SESSION)
setup_socket_per_session_udp_listener_servers();
if(turn_params.net_engine_version != NEV_UDP_SOCKET_PER_THREAD) {
setup_tcp_listener_servers(turn_params.listener.ioa_eng, NULL);
}
{
int tot = 0;
if(udp_relay_servers[0]) {
tot = get_real_udp_relay_servers_number();
}
if(tot) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total UDP servers: %d\n",(int)tot);
}
}
{
int tot = get_real_general_relay_servers_number(); //默认4个general_relay_servers
if(tot) {
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"Total General servers: %d\n",(int)tot);
int i;
for(i = 0;i
文件src/server/ns_turn_server.c
函数read_client_connection
,处理访问的数据并输出结果。
结构体ioa_net_data
保存着访问者所有的数据。
typedef struct _ioa_net_data {
ioa_addr src_addr;
ioa_network_buffer_handle nbh; //客户请求的数据
int recv_ttl;
int recv_tos;
} ioa_net_data;
static int read_client_connection(turn_turnserver *server,
ts_ur_super_session *ss, ioa_net_data *in_buffer,
int can_resume, int count_usage) {
....
int ret = (int)ioa_network_buffer_get_size(in_buffer->nbh); //获取请求数据的大小
....
unsigned char *test_dd = ioa_network_buffer_data(in_buffer->nbh); //请求数据转化为unsigned char *
//解析协议时,注意高低位nswap16/nswap32等等
...
handle_turn_command(server, ss, in_buffer, nbh, &resp_constructed, can_resume); //处理STUN协议主方法
...
int ret = write_client_connection(server, ss, nbh, TTL_IGNORE, TOS_IGNORE); //处理完后回复数据给客户端
....
//当然还支持 旧的STUN协议
handle_old_stun_command(server, ss, in_buffer, nbh, &resp_constructed, old_stun_cookie);
...
//对HTTP访问简单回复:
handle_http_echo(ss->client_socket);//见write_http_echo
}
为了清晰明了的知道接收和发送的UDP数据,都是什么内容,为此添加打印日志代码,更好的学习STUN/TURN协议
stun_attr_ref sar = stun_attr_get_first_str(ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh));
if (!sar)
{
printf("stun_attr_get_first_str: NULL\n");
return 0;
}
while (sar)
{
int sarlen = stun_attr_get_len(sar);
u16bits stun_attr_type = stun_attr_get_type(sar);
printf("ATTR[0x%04X][%s] LENGTH[%d] ", stun_attr_type, get_attr_type_string(stun_attr_type), sarlen);
if (stun_attr_type == 0)
break;
if(sarlen >4096) break;
if (sarlen > 0)
{
u08bits *o = (u08bits *)turn_malloc(sarlen + 1);
ns_bcopy(stun_attr_get_value(sar), o, sarlen);
o[sarlen] = 0;
switch (stun_attr_type)
{
case STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS:
case STUN_ATTRIBUTE_XOR_PEER_ADDRESS:
case STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS:
case STUN_ATTRIBUTE_MAPPED_ADDRESS:
case STUN_ATTRIBUTE_ALTERNATE_SERVER:
case OLD_STUN_ATTRIBUTE_RESPONSE_ADDRESS:
case OLD_STUN_ATTRIBUTE_SOURCE_ADDRESS:
case OLD_STUN_ATTRIBUTE_CHANGED_ADDRESS:
case OLD_STUN_ATTRIBUTE_REFLECTED_FROM:
case STUN_ATTRIBUTE_RESPONSE_ORIGIN:
case STUN_ATTRIBUTE_OTHER_ADDRESS:
case STUN_ATTRIBUTE_USER_INFO_MY_REALY_ADDR:
case STUN_ATTRIBUTE_RES_USER_INFO_REAL_ADDR:
case STUN_ATTRIBUTE_RES_USER_INFO_MAPPED_ADDR:
case STUN_ATTRIBUTE_RES_USER_INFO_RELAYED_ADDR:
addr_set_any(peer_addr);
stun_attr_get_addr_str(ioa_network_buffer_data(nbh),
ioa_network_buffer_get_size(nbh),
sar, peer_addr,
NULL);
if(ss && stun_attr_type == STUN_ATTRIBUTE_USER_INFO_MY_REALY_ADDR && recv0_send1 == 0 && user) {
if(user->real_addr->addr == NULL){
user->real_addr->addr = turn_malloc(sizeof(ioa_addr));
}
user->real_addr->addr->s4.sin_port = ((const u16bits*)stun_attr_get_value(sar))[1];
user->real_addr->addr->s4.sin_addr.s_addr=((const u32bits*)stun_attr_get_value(sar))[1];
user->real_addr->addr->ss.sa_family = AF_INET;
addr_cpy(peer_addr,user->real_addr->addr);
}
addr_debug_print(1, peer_addr, "");
break;
case STUN_ATTRIBUTE_CHANNEL_NUMBER:
chn = stun_attr_get_channel_number(sar);
printf("channel = [%d] ", chn);
break;
case STUN_ATTRIBUTE_LIFETIME:
printf("lifetime [%d]", nswap32(*((const u32bits*)o)));
break;
case STUN_ATTRIBUTE_EVEN_PORT:
printf("port [%d]", stun_attr_get_even_port(sar));
break;
case STUN_ATTRIBUTE_RESERVATION_TOKEN:
printf("token [%ld]", stun_attr_get_reservation_token_value(sar));
break;
case STUN_ATTRIBUTE_FINGERPRINT:
case STUN_ATTRIBUTE_SOFTWARE:
case STUN_ATTRIBUTE_REQUESTED_ADDRESS_FAMILY:
printf("Hex:[0x");
for(ti=0;ti
增加后,打印代码如下:
======================================================UDP SEND >>>>>>>>>>>>>>>
len[68] 010300302112a442ce6672a1043f958f3c97b929001600080001c22871c3cfd80020000800019904c0a801c8000d000400000e10802200044e6f6e6580280004da135792
SOCKET TYPE :
MESSAGE INFO :
RESPONSE OK [0x03][STUN_METHOD_ALLOCATE]
ATTR[0x0016][STUN_ATTRIBUTE_XOR_RELAYED_ADDRESS] LENGTH[8] IPv4. addr is : 113.195.207.216:49704
ATTR[0x0020][STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS] LENGTH[8] IPv4. addr is : 192.168.1.200:39172
ATTR[0x000D][STUN_ATTRIBUTE_LIFETIME] LENGTH[4] lifetime [3600]
ATTR[0x8022][STUN_ATTRIBUTE_SOFTWARE] LENGTH[4] Hex:[0x4E6F6E65]
ATTR[0x8028][STUN_ATTRIBUTE_FINGERPRINT] LENGTH[4] Hex:[0xDA135792]
IPv4. =====udp_send===: 192.168.1.200:39172