Wakaama 是lwm2m协议的一种实现,其源码路径为wakaama。lwm2m的实现分为server和client。
编译server的方法为:
cmake [wakaama directory]/examples/server
make
./lwm2mserver [Options]
我们看看server的makerfile是怎么写的
#要求cmake的版本必须是2.8
cmake_minimum_required (VERSION 2.8)
project (lwm2mserver C)
#不支持dtls特性
if(DTLS) message(FATAL_ERROR "DTLS option is not supported." ) endif()
include(${CMAKE_CURRENT_LIST_DIR}/../../core/wakaama.cmake) include(${CMAKE_CURRENT_LIST_DIR}/../shared/shared.cmake)
#定义宏
add_definitions(-DLWM2M_SERVER_MODE) add_definitions(${SHARED_DEFINITIONS} ${WAKAAMA_DEFINITIONS})
include_directories (${WAKAAMA_SOURCES_DIR} ${SHARED_INCLUDE_DIRS})
#原来server就一个源文件
SET(SOURCES ${CMAKE_CURRENT_LIST_DIR}/lwm2mserver.c
)
#编译成可执行文件
add_executable(${PROJECT_NAME} ${SOURCES} ${WAKAAMA_SOURCES} ${SHARED_SOURCES})
# Add WITH_LOGS to debug variant set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:WITH_LOGS>)
SOURCE_GROUP(wakaama FILES ${WAKAAMA_SOURCES})
我们看看这个唯一的源文件的main函数
int main(int argc, char *argv[])
{
int sock;
fd_set readfds;
struct timeval tv;
int result;
lwm2m_context_t * lwm2mH = NULL;
int i;
connection_t * connList = NULL;
int addressFamily = AF_INET6;
int opt;
const char * localPort = LWM2M_STANDARD_PORT_STR;
#main函数支持的子命令
command_desc_t commands[] =
{
{"list", "List registered clients.", NULL, prv_output_clients, NULL},
{"read", "Read from a client.", " read CLIENT# URI\r\n"
" CLIENT#: client number as returned by command 'list'\r\n"
" URI: uri to read such as /3, /3/0/2, /1024/11, /1024/0/1\r\n"
"Result will be displayed asynchronously.", prv_read_client, NULL},
{"disc", "Discover resources of a client.", " disc CLIENT# URI\r\n"
" CLIENT#: client number as returned by command 'list'\r\n"
" URI: uri to discover such as /3, /3/0/2, /1024/11, /1024/0/1\r\n"
"Result will be displayed asynchronously.", prv_discover_client, NULL},
{"write", "Write to a client.", " write CLIENT# URI DATA\r\n"
" CLIENT#: client number as returned by command 'list'\r\n"
" URI: uri to write to such as /3, /3/0/2, /1024/11, /1024/0/1\r\n"
" DATA: data to write\r\n"
"Result will be displayed asynchronously.", prv_write_client, NULL},
};
opt = 1;
{
if (argv[opt] == NULL
|| argv[opt][0] != '-'
|| argv[opt][2] != 0)
{
print_usage();
return 0;
}
switch (argv[opt][1])
{
case '4':
addressFamily = AF_INET;
break;
case 'l':
opt++;
if (opt >= argc)
{
print_usage();
return 0;
}
localPort = argv[opt];
break;
default:
print_usage();
return 0;
}
opt += 1;
}
#建立一个socket,默认是用AF_INET即netlink
sock = create_socket(localPort, addressFamily);
if (sock < 0)
{
fprintf(stderr, "Error opening socket: %d\r\n", errno);
return -1;
}
#初始化lwm2m
lwm2mH = lwm2m_init(NULL);
if (NULL == lwm2mH)
{
fprintf(stderr, "lwm2m_init() failed\r\n");
return -1;
}
#设置信号量SIGNIT的回调函数
signal(SIGINT, handle_sigint);
for (i = 0 ; commands[i].name != NULL ; i++)
{
commands[i].userData = (void *)lwm2mH;
}
fprintf(stdout, "> "); fflush(stdout);
lwm2m_set_monitoring_callback(lwm2mH, prv_monitor_callback, lwm2mH);
#看见main函数就是一个死循环,直到收到SIGINT信号后将g_quit修改成0 才退出.
while (0 == g_quit)
{
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
FD_SET(STDIN_FILENO, &readfds);
tv.tv_sec = 60;
tv.tv_usec = 0;
result = lwm2m_step(lwm2mH, &(tv.tv_sec));
if (result != 0)
{
fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result);
return -1;
}
#最终要是这里开始调用select来监听client的通信
result = select(FD_SETSIZE, &readfds, 0, 0, &tv);
#select发生错误的情况
if ( result < 0 )
{
if (errno != EINTR)
{
fprintf(stderr, "Error in select(): %d\r\n", errno);
}
}
#select正常返回的情况
else if (result > 0)
{
uint8_t buffer[MAX_PACKET_SIZE];
int numBytes;
#检查是否sock这个句柄被设置了
if (FD_ISSET(sock, &readfds))
{
struct sockaddr_storage addr;
socklen_t addrLen;
addrLen = sizeof(addr);
#读取sock返回的信息
numBytes = recvfrom(sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen);
if (numBytes == -1)
{
fprintf(stderr, "Error in recvfrom(): %d\r\n", errno);
}
else
{
char s[INET6_ADDRSTRLEN];
in_port_t port;
connection_t * connP;
s[0] = 0;
#判断netlink是ipv4还是ipv6,这里假定是ipv4
if (AF_INET == addr.ss_family)
{
struct sockaddr_in *saddr = (struct sockaddr_in *)&addr;
inet_ntop(saddr->sin_family, &saddr->sin_addr, s, INET6_ADDRSTRLEN);
port = saddr->sin_port;
}
else if (AF_INET6 == addr.ss_family)
{
struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&addr;
inet_ntop(saddr->sin6_family, &saddr->sin6_addr, s, INET6_ADDRSTRLEN);
port = saddr->sin6_port;
}
fprintf(stderr, "%d bytes received from [%s]:%hu\r\n", numBytes, s, ntohs(port));
#将数据读到buffer中
output_buffer(stderr, buffer, numBytes, 0);
#判断这个是新连接还是就连接
connP = connection_find(connList, &addr, addrLen);
if (connP == NULL)
{
#如果是新连接则建立连接后,将新连接保存到connlist中
connP = connection_new_incoming(connList, sock, (struct sockaddr *)&addr, addrLen);
if (connP != NULL)
{
connList = connP;
}
}
if (connP != NULL)
{
#只要前面的连接有效,则开始处理client发送过来的包
lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP);
}
}
}
#如果是STDIN_FILENO 这个句柄被设置,则出来这个句柄中读到的command
else if (FD_ISSET(STDIN_FILENO, &readfds))
{
numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE - 1);
if (numBytes > 1)
{
buffer[numBytes] = 0;
handle_command(commands, (char*)buffer);
fprintf(stdout, "\r\n");
}
if (g_quit == 0)
{
fprintf(stdout, "> ");
fflush(stdout);
}
else
{
fprintf(stdout, "\r\n");
}
}
}
}
lwm2m_close(lwm2mH);
close(sock);
connection_free(connList);
#ifdef MEMORY_TRACE
if (g_quit == 1)
{
trace_print(0, 1);
}
#endif
return 0;
}