文章目录
前言
最近研究了下实现coap协议,几个纯C开源库看下来感觉还是libnyoci移植起来靠谱点,其他库要不然对POSIX的API强耦合,要不反正就是不知道怎么搞。
折腾好久起码现在能够实现一个正常的Coap服务器了,发上来供需要的人参考。
因为这个库依赖了大量相对高级版本的标准库函数以及(如果要完整移植的话)要求实现很多应用层网络功能,如DNS、TSL什么的,所以着实折腾了好久才成功编译。
这里只讲到要实现一个基于UDP的Coap服务器所需要移植的部分。
工程环境配置
首先,先去全球最大的同性交友网站下载好libnyoci的源文件,完不成的话点击下右上角的×
然后libnyoci\src\libnyoci中的.c和.h文件全扔进工程中,基本都会用到。
libnyoci\src\libnyociextra中的文件则根据需要放进工程中,取决于你使用这个库的方法。
然后我们需要加入配置文件,在这之前先打开编译器选项,加入宏定义HAVE_CONFIG_H=1
在我的CodeWarrior上是这样子实现的:
不同的IDE找各自的方法。
配置文件
这样子nyoci就知道了你会包含一个叫做config.h的配置文件;以及默认会有的nyoci-config.h配置文件。这两个文件如下:
config.h
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* rl_completion_entry_function has the wrong return type */
#define HAS_LIBEDIT_COMPLETION_ENTRY_BUG 1
/* Define to 1 if you have the `alloca' function. */
/* #undef HAVE_ALLOCA */
/* Define to 1 if you have the <alloca.h> header file. */
#define HAVE_ALLOCA_H 0
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 0
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
/* Define to 1 if you have the `fprintf' function. */
#define HAVE_FPRINTF 0
/* Define to 1 if the system has the `deprecated' function attribute */
#define HAVE_FUNC_ATTRIBUTE_DEPRECATED 0
/* Define to 1 if the system has the `pure' function attribute */
#define HAVE_FUNC_ATTRIBUTE_PURE 0
/* Define to 1 if you have the `getline' function. */
#define HAVE_GETLINE 0
/* Define to 1 if you have the `getloadavg' function. */
#define HAVE_GETLOADAVG 0
/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 0
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 0
/* Define to 1 if you have the `readline' library (-lreadline). */
#define HAVE_LIBREADLINE 0
/* Define to 1 if you have the `malloc' function. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the `memcmp' function. */
#define HAVE_MEMCMP 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 0
/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1
/* Set if OpenSSL is present */
/* #undef HAVE_OPENSSL */
/* Set if OpenSSL has DTLSv1_method() */
/* #undef HAVE_OPENSSL_DTLSV1_METHOD */
/* Set if OpenSSL has DTLS_method() */
/* #undef HAVE_OPENSSL_DTLS_METHOD */
/* Set if OpenSSL has SSL_CONF_CTX_new() */
/* #undef HAVE_OPENSSL_SSL_CONF_CTX_NEW */
/* Set if OpenSSL has SSL_CONF_finish() */
/* #undef HAVE_OPENSSL_SSL_CONF_FINISH */
/* Define to 1 if you have the `printf' function. */
#define HAVE_PRINTF 1
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 0
/* Have PTHREAD_PRIO_INHERIT. */
#define HAVE_PTHREAD_PRIO_INHERIT 0
/* Define to 1 if you have the `rl_set_prompt' function. */
#define HAVE_RL_SET_PROMPT 0
/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 0
/* Define to 1 if you have the `snprintf' function. */
#define HAVE_SNPRINTF 1
/* Define to 1 if you have the `sprintf' function. */
#define HAVE_SPRINTF 1
/* Define to 1 if you have the <stdarg.h> header file. */
#define HAVE_STDARG_H 1
/* Define to 1 if you have the <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1
/* Define to 1 if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `stpncpy' function. */
#define HAVE_STPNCPY 1
/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1
/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 0
#define HAVE_STRSEP 0
/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 0
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 0
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strlcat' function. */
#define HAVE_STRLCAT 1
/* Define to 1 if you have the `strlcpy' function. */
#define HAVE_STRLCPY 0
/* Define to 1 if you have the `strndup' function. */
#define HAVE_STRNDUP 0
/* Define to 1 if you have the `strstr' function. */
#define HAVE_STRSTR 1
/* Define to 1 if you have the `strtol' function. */
#define HAVE_STRTOL 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 0
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 0
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 0
/* Define to 1 if you have the `vsnprintf' function. */
#define HAVE_VSNPRINTF 0
/* Define to 1 if you have the `vsprintf' function. */
#define HAVE_VSPRINTF 0
#define HAVE_C99_VLA 0
/* Define to the sub-directory where libtool stores uninstalled libraries. */
//#define LT_OBJDIR ".libs/"
/* . */
//#define NYOCI_API_EXTERN extern
/* . */
/* #undef NYOCI_AVOID_MALLOC */
/* . */
/* #undef NYOCI_AVOID_PRINTF */
/* . */
/* #undef NYOCI_CONF_NODE_ROUTER */
/* . */
/* #undef NYOCI_CONF_TRANS_ENABLE_BLOCK2 */
/* . */
/* #undef NYOCI_CONF_TRANS_ENABLE_OBSERVING */
/* . */
/* #undef NYOCI_EMBEDDED */
/* . */
//#define NYOCI_INTERNAL_EXTERN extern
/* . */
/* #undef NYOCI_MAX_OBSERVERS */
/* . */
/* #undef NYOCI_MAX_VHOSTS */
/* LibNyoci network abstraction */
//#define NYOCI_PLAT_NET posix
/* . */
/* #undef NYOCI_PLAT_NET_POSIX_FAMILY */
/* LibNyoci TLS abstraction */
/* #undef NYOCI_PLAT_TLS */
/* . */
/* #undef NYOCI_SINGLETON */
/* . */
/* #undef NYOCI_USE_CASCADE_COUNT */
/* Name of package */
#define PACKAGE "libnyoci"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "https://github.com/darconeous/libnyoci/"
/* Define to the full name of this package. */
#define PACKAGE_NAME "LibNyoci"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "LibNyoci 0.07.00rc1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libnyoci"
/* Define to the home page for this package. */
#define PACKAGE_URL "http://libnyoci.org/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.07.00rc1"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* Source version */
#define SOURCE_VERSION "0.07.00rc1-6-g5ad1f3d"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 0
/* Enable extensions on AIX 3, Interix. */
//#ifndef _ALL_SOURCE
// #define _ALL_SOURCE 1
//#endif
/* Enable GNU extensions on systems that have them. */
//#ifndef _GNU_SOURCE
//# define _GNU_SOURCE 1
//#endif
/* Enable threading extensions on Solaris. */
//#ifndef _POSIX_PTHREAD_SEMANTICS
//# define _POSIX_PTHREAD_SEMANTICS 1
//#endif
/* Enable extensions on HP NonStop. */
//#ifndef _TANDEM_SOURCE
//# define _TANDEM_SOURCE 1
//#endif
/* Enable general extensions on Solaris. */
//#ifndef __EXTENSIONS__
//# define __EXTENSIONS__ 1
//#endif
#define DEBUG 1
#define VERBOSE_DEBUG 1
#define __ORDER_BIG_ENDIAN__ 1
#define __BYTE_ORDER__ 0
/* Version number of package */
#define VERSION "0.07.00rc1"
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef ssize_t */
/* Define to empty if the keyword `volatile' does not work. Warning: valid
code using `volatile' can become incorrect without. Disable with care. */
/* #undef volatile */
这个文件主要是用于告知nyoci你的开发环境有没有包含某个函数以配置代码,上面是我目前的配置。请参照自己的开发环境按注释配置好其中未注释掉的部分,以减少编译链接时一堆报警。。
要注意的是
#define __ORDER_BIG_ENDIAN__ 1
#define __BYTE_ORDER__ 0
这里两个其实并不是让你回答机器是大端还是小端的。它的功能就是帮助nyoci确定位序以确定:
struct coap_header_s {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t
version:2,
tt:2,
token_len:4;
#else
uint8_t
token_len:4,
tt:2,
version:2;
#endif
……
};
这个结构体中到底应该按什么顺序排这三个字段。这个可能和单片机与编译器都有关,不见得大端字节序的就一定大端位序。反正就是两个值相等和两个值不等的都试一下,用成功通讯的那个就好。
nyoci-config.h
/*! @file nyoci-config.h
** @author Robert Quattlebaum <darco@deepdarc.com>
** @brief LibNyoci Build Options
**
** Copyright (C) 2017 Robert Quattlebaum
**
** Permission is hereby granted, free of charge, to any person
** obtaining a copy of this software and associated
** documentation files (the "Software"), to deal in the
** Software without restriction, including without limitation
** the rights to use, copy, modify, merge, publish, distribute,
** sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so,
** subject to the following conditions:
**
** The above copyright notice and this permission notice shall
** be included in all copies or substantial portions of the
** Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
** KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
** WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
** PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
** OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
** OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
** OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// 1/0
#define NYOCI_EMBEDDED 1
// 1/0
#define NYOCI_SINGLETON 0
#undef NYOCI_PLAT_NET_POSIX_FAMILY
#undef NYOCI_PLAT_NET
// define/undef
#undef NYOCI_PLAT_TLS
#undef NYOCI_AVOID_MALLOC
#undef NYOCI_AVOID_PRINTF
#undef NYOCI_DEFAULT_PORT
#undef NYOCI_CONF_DUPE_BUFFER_SIZE
#undef NYOCI_CONF_ENABLE_VHOSTS
#define NYOCI_CONF_MAX_ALLOCED_NODES 2
#undef NYOCI_CONF_MAX_GROUPS
#undef NYOCI_CONF_MAX_OBSERVERS
#undef NYOCI_CONF_MAX_PAIRINGS
#undef NYOCI_CONF_MAX_TIMEOUT
#define NYOCI_CONF_NODE_ROUTER
#undef NYOCI_CONF_TRANS_ENABLE_BLOCK2
#undef NYOCI_CONF_TRANS_ENABLE_OBSERVING
// 0/1
#define NYOCI_CONF_USE_DNS 0
#undef NYOCI_ADD_NEWLINES_TO_LIST_OUTPUT
#undef NYOCI_ASYNC_RESPONSE_MAX_LENGTH 50
#undef NYOCI_DEBUG_INBOUND_DROP_PERCENT
#undef NYOCI_DEBUG_OUTBOUND_DROP_PERCENT
#undef NYOCI_MAX_CASCADE_COUNT
#define NYOCI_MAX_CONTENT_LENGTH 50
#undef NYOCI_MAX_OBSERVERS
#undef NYOCI_MAX_PACKET_LENGTH
#undef NYOCI_MAX_PATH_LENGTH
#undef NYOCI_MAX_URI_LENGTH
#undef NYOCI_MAX_VHOSTS
#undef NYOCI_TRANSACTION_BURST_COUNT
#undef NYOCI_TRANSACTION_BURST_TIMEOUT_MAX
#undef NYOCI_TRANSACTION_BURST_TIMEOUT_MIN
#undef NYOCI_THREAD_SAFE
#undef NYOCI_NODE_ROUTER_USE_BTREE
#undef NYOCI_OBSERVATION_DEFAULT_MAX_AGE
#undef NYOCI_OBSERVATION_KEEPALIVE_INTERVAL
#undef NYOCI_OBSERVER_CON_EVENT_EXPIRATION
#undef NYOCI_OBSERVER_NON_EVENT_EXPIRATION
#undef NYOCI_TRANSACTIONS_USE_BTREE
#undef NYOCI_TRANSACTION_BURST_COUNT
#undef NYOCI_TRANSACTION_POOL_SIZE
#undef NYOCI_USE_CASCADE_COUNT
#undef NYOCI_VARIABLE_MAX_KEY_LENGTH
#undef NYOCI_VARIABLE_MAX_VALUE_LENGTH
#undef NYOCI_INTERNAL_EXTERN
#undef NYOCI_API_EXTERN
#undef NYOCI_DEPRECATED
这个配置文件主要用于配置NYOCI本身。反正看名字大概也能猜出大概每个配置变量是干嘛用的,自己瞅一眼大概设设就是。要设置的话就把#undef改成#define然后设置值,不设置的话基本都会在nyoci-defaults.h文件里有默认值。上面也是我目前根据我的环境设的值。
把这两个配置文件创建好后放在自己的工程目录下,然后扔进工程里就好。
平台移植
libnyoci和(网络栈)平台相关的代码被组织在libnyoci\src\plat-net目录下。目前,只支持posix和uip。
我们要为wiz的io库做一个移植,然后因为我用的RTOS是uCOS-II,所以还会有些uCOS-II的相关代码。首先,把uip那个文件夹复制然后黏贴,改个名字叫wiz。就像这样:
然后我直接给出我目前移植好的文件:
nyoci-plat-net.h
#ifndef NYOCI_nyoci_plat_wiz_h
#define NYOCI_nyoci_plat_wiz_h
#if !defined(NYOCI_INCLUDED_FROM_LIBNYOCI_H) && !defined(BUILDING_LIBNYOCI)
#error "Do not include this header directly, include <libnyoci/libnyoci.h> instead"
#endif
NYOCI_BEGIN_C_DECLS
typedef uint8_t nyoci_addr_t[4];
typedef struct {
nyoci_addr_t nyoci_addr;
uint16_t nyoci_port;
} nyoci_sockaddr_t;
NYOCI_API_EXTERN uint8_t nyoci_plat_get_socketnumber(nyoci_t self);
NYOCI_API_EXTERN void nyoci_plat_set_socketnumber(nyoci_t self, uint8_t sn);
NYOCI_BEGIN_C_DECLS
#include "nyoci-plat-net-func.h"
#endif
nyoci-plat-net-internal.h
#ifndef NYOCI_nyoci_plat_wiz_internal_h
#define NYOCI_nyoci_plat_wiz_internal_h
#ifndef wiz_is_addr_mcast
#define wiz_is_addr_mcast(addrptr) (*(const uint8_t*)(addrptr)==224)
#endif
#ifndef wiz_is_addr_unspecified
#define wiz_is_addr_unspecified(addrptr) (*(addrptr)==0)
#endif
#ifndef wiz_is_addr_loopback
#define wiz_is_addr_loopback(addrptr) (*(const uint8_t*)(addrptr)==127)
#endif
#define NYOCI_IS_ADDR_MULTICAST wiz_is_addr_mcast
#define NYOCI_IS_ADDR_UNSPECIFIED wiz_is_addr_unspecified
#define NYOCI_IS_ADDR_LOOPBACK wiz_is_addr_loopback
NYOCI_BEGIN_C_DECLS
struct nyoci_plat_s {
nyoci_sockaddr_t sockaddr_local;
nyoci_sockaddr_t sockaddr_remote;
nyoci_session_type_t session_type;
uint8_t socketNum;
uint8_t outbound_packet_bytes[NYOCI_MAX_PACKET_LENGTH+1];
uint8_t inbound_packet_bytes[NYOCI_MAX_PACKET_LENGTH+1];
};
NYOCI_END_C_DECLS
#endif
nyoci-plat-net.c
#if HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef VERBOSE_DEBUG
#define VERBOSE_DEBUG 0
#endif
#ifndef DEBUG
#define DEBUG VERBOSE_DEBUG
#endif
#define NYOCI_COAP_MULTICAST_ALLDEVICES_ADDR COAP_MULTICAST_STR_ALLDEVICES
#include <stdio.h>
#include "assert-macros.h"
#include "libnyoci.h"
#include "nyoci-internal.h"
#include "nyoci-logging.h"
#include "lib_lite.h"
#include "Network.h"
#include "ucos_ii.h"
#include "os_cfg.h"
#include "socket.h"
uint8_t nyoci_plat_get_socketnumber(nyoci_t self) {
return self->plat.socketNum;
};
void nyoci_plat_set_socketnumber(nyoci_t self, uint8_t sn){
self->plat.socketNum = sn;
}
nyoci_status_t
nyoci_plat_join_standard_groups(nyoci_t self, int interface)
{
NYOCI_SINGLETON_SELF_HOOK;
// TODO: Implement me!
return NYOCI_STATUS_NOT_IMPLEMENTED;
}
nyoci_t
nyoci_plat_init(nyoci_t self) {
NYOCI_SINGLETON_SELF_HOOK;
self->plat.socketNum = -1;
return self;
}
nyoci_status_t
nyoci_plat_bind_to_port(
nyoci_t self,
nyoci_session_type_t type,
uint16_t port
) {
nyoci_status_t ret = NYOCI_STATUS_FAILURE;
NYOCI_SINGLETON_SELF_HOOK;
switch(type) {
case NYOCI_SESSION_TYPE_UDP:
//#if NYOCI_DTLS
// case NYOCI_SESSION_TYPE_DTLS:
//#endif
//#if NYOCI_TCP
// case NYOCI_SESSION_TYPE_TCP:
//#endif
//#if NYOCI_TLS
// case NYOCI_SESSION_TYPE_TLS:
//#endif
break;
default:
ret = NYOCI_STATUS_NOT_IMPLEMENTED;
// Unsupported session type.
goto bail;
}
self->plat.session_type = type;
self->plat.sockaddr_local.nyoci_port = port;
ret = NYOCI_STATUS_OK;
bail:
return ret;
}
void
nyoci_plat_finalize(nyoci_t self) {
NYOCI_SINGLETON_SELF_HOOK;
close(self->plat.socketNum);
}
nyoci_status_t
nyoci_plat_set_remote_hostname_and_port(const char* hostname, uint16_t port)
{
nyoci_status_t ret;
NYOCI_NON_RECURSIVE nyoci_sockaddr_t saddr;
DEBUG_PRINTF("Outbound: Dest host [%s]:%d",hostname,port);
#if NYOCI_DTLS
nyoci_plat_tls_set_remote_hostname(hostname);
#endif
// Check to see if this host is a group we know about.
if (strcasecmp(hostname, COAP_MULTICAST_STR_ALLDEVICES) == 0) {
hostname = NYOCI_COAP_MULTICAST_ALLDEVICES_ADDR;
}
ret = nyoci_plat_lookup_hostname(hostname, &saddr, NYOCI_LOOKUP_HOSTNAME_FLAG_DEFAULT);
require_noerr(ret, bail);
saddr.nyoci_port = port;
nyoci_plat_set_remote_sockaddr(&saddr);
bail:
return ret;
}
void
nyoci_plat_set_remote_sockaddr(const nyoci_sockaddr_t* addr)
{
nyoci_t const self = nyoci_get_current_instance();
if (addr) {
self->plat.sockaddr_remote = *addr;
} else {
memset(&self->plat.sockaddr_remote,0,sizeof(self->plat.sockaddr_remote));
}
}
void
nyoci_plat_set_local_sockaddr(const nyoci_sockaddr_t* addr)
{
nyoci_t const self = nyoci_get_current_instance();
if (addr) {
self->plat.sockaddr_local = *addr;
} else {
memset(&self->plat.sockaddr_local,0,sizeof(self->plat.sockaddr_local));
}
}
void
nyoci_plat_set_session_type(nyoci_session_type_t type)
{
nyoci_t const self = nyoci_get_current_instance();
self->plat.session_type = type;
}
const nyoci_sockaddr_t*
nyoci_plat_get_remote_sockaddr(void)
{
return &nyoci_get_current_instance()->plat.sockaddr_remote;
}
const nyoci_sockaddr_t*
nyoci_plat_get_local_sockaddr(void)
{
return &nyoci_get_current_instance()->plat.sockaddr_local;
}
nyoci_session_type_t
nyoci_plat_get_session_type(void)
{
return nyoci_get_current_instance()->plat.session_type;
}
uint16_t
nyoci_plat_get_port(nyoci_t self) {
NYOCI_SINGLETON_SELF_HOOK;
return self->plat.sockaddr_local.nyoci_port;
}
nyoci_status_t
nyoci_plat_outbound_start(nyoci_t self, uint8_t** data_ptr, coap_size_t *data_len)
{
NYOCI_SINGLETON_SELF_HOOK;
if (data_ptr) {
*data_ptr = (uint8_t*)self->plat.outbound_packet_bytes;
}
if (data_len) {
*data_len = sizeof(self->plat.outbound_packet_bytes);
}
self->outbound.packet = (struct coap_header_s*)self->plat.outbound_packet_bytes;
return NYOCI_STATUS_OK;
}
//
nyoci_status_t
nyoci_plat_outbound_finish(nyoci_t self,const uint8_t* data_ptr, coap_size_t data_len, int flags)
{
nyoci_status_t ret = NYOCI_STATUS_FAILURE;
int32_t sent_bytes = -1;
int sn;
NYOCI_SINGLETON_SELF_HOOK;
#if NYOCI_DTLS
if (nyoci_plat_get_session_type() == NYOCI_SESSION_TYPE_DTLS) {
ret = nyoci_plat_tls_outbound_packet_process(self, data_ptr, data_len);
} else
#endif
if (nyoci_plat_get_session_type() == NYOCI_SESSION_TYPE_UDP) {
sn = nyoci_get_current_instance()->plat.socketNum;
assert(sn < 8);
require(data_len > 0, bail);
#if VERBOSE_DEBUG
{
char addr_str[30] = "???";
uint16_t port = nyoci_plat_get_remote_sockaddr()->nyoci_port;
IPToStr(nyoci_plat_get_remote_sockaddr()->nyoci_addr,addr_str);
DEBUG_PRINTF("nyoci(%p): Outbound packet to [%s]:%u", self,addr_str,port);
coap_dump_header(
NYOCI_DEBUG_OUT_FILE,
"Outbound:\t",
(struct coap_header_s*)data_ptr,
(coap_size_t)data_len
);
}
#endif
sent_bytes = sendto(
sn,
data_ptr,
data_len,
self->plat.sockaddr_remote.nyoci_addr,
self->plat.sockaddr_remote.nyoci_port
);
require_action_string(
(sent_bytes >= 0),
bail, ret = NYOCI_STATUS_ERRNO, "sendto() fail"
);
require_action_string(
(sent_bytes == data_len),
bail, ret = NYOCI_STATUS_FAILURE, "sendto() returned less than len"
);
ret = NYOCI_STATUS_OK;
} else {
ret = NYOCI_STATUS_NOT_IMPLEMENTED;
}
bail:
return ret;
}
// MARK: -
nyoci_status_t
nyoci_plat_wait(
nyoci_t self, nyoci_cms_t cms
) {
#ifdef OS_uCOS_II_H
OSTimeDly(cms * OS_TICKS_PER_SEC / MSEC_PER_SEC);
return NYOCI_STATUS_OK;
#else
return NYOCI_STATUS_NOT_IMPLEMENTED;
#endif
}
nyoci_status_t
nyoci_plat_process(nyoci_t self) {
uint8_t sn;
int32_t bytesCnt;
nyoci_status_t ret = NYOCI_STATUS_FAILURE;
NYOCI_SINGLETON_SELF_HOOK;
require_action(self != NULL, bail, ret = NYOCI_STATUS_INVALID_ARGUMENT);
sn = self->plat.socketNum;
if(!Network_hasReady()){
#ifdef OS_uCOS_II_H
OSTimeDlyHMSM(0, 0, 3, 0);
#endif
goto bail;
}
require(sn < 8,bail);
if(getSn_SR(sn) != SOCK_UDP){
if(socket(sn,Sn_MR_UDP,self->plat.sockaddr_local.nyoci_port,0x00) != sn){
DEBUG_PRINTF("nyoci init fail");
#ifdef OS_uCOS_II_H
OSTimeDlyHMSM(0, 0, 3, 0);
#endif
goto bail;
}else{
DEBUG_PRINTF("nyoci process on socket %d", (int)sn);
}
}
if(getSn_RX_RSR(sn)>0){
bytesCnt = recvfrom(sn, self->plat.inbound_packet_bytes, sizeof(self->plat.inbound_packet_bytes),
self->plat.sockaddr_remote.nyoci_addr, &self->plat.sockaddr_remote.nyoci_port);
#if VERBOSE_DEBUG
{
char addr_str[30] = "???";
uint16_t port = self->plat.sockaddr_remote.nyoci_port;
IPToStr(self->plat.sockaddr_remote.nyoci_addr,addr_str);
DEBUG_PRINTF("nyoci received data from %s:%u len:%d", addr_str, port, (int)bytesCnt);
}
#endif
require(bytesCnt >= 0, bail);
nyoci_plat_set_session_type(NYOCI_SESSION_TYPE_UDP);
nyoci_inbound_packet_process(self, self->plat.inbound_packet_bytes, bytesCnt, 0);
}
nyoci_handle_timers(self);
ret = NYOCI_STATUS_OK;
bail:
nyoci_set_current_instance(NULL);
self->is_responding = false;
return ret;
}
nyoci_status_t
nyoci_plat_lookup_hostname(const char* hostname, nyoci_sockaddr_t* saddr, int flags)
{
/* nyoci_status_t ret;
memset(saddr, 0, sizeof(*saddr));
ret = StrToIP(hostname, (uint8_t *)&saddr->nyoci_addr) ? NYOCI_STATUS_OK : NYOCI_STATUS_HOST_LOOKUP_FAILURE;
#if NYOCI_CONF_USE_DNS
#if CONTIKI
if(ret) {
NYOCI_NON_RECURSIVE uip_ipaddr_t *temp = NULL;
switch(resolv_lookup(hostname,&temp)) {
case RESOLV_STATUS_CACHED:
memcpy(&saddr->nyoci_addr, temp, sizeof(uip_ipaddr_t));
ret = NYOCI_STATUS_OK;
break;
case RESOLV_STATUS_UNCACHED:
case RESOLV_STATUS_EXPIRED:
resolv_query(hostname);
case RESOLV_STATUS_RESOLVING:
ret = NYOCI_STATUS_WAIT_FOR_DNS;
break;
default:
case RESOLV_STATUS_ERROR:
case RESOLV_STATUS_NOT_FOUND:
ret = NYOCI_STATUS_HOST_LOOKUP_FAILURE;
break;
}
}
#else // CONTIKI
#warning NYOCI_CONF_USE_DNS was set, but no DNS lookup mechamism is known!
#endif
#endif // NYOCI_CONF_USE_DNS
require_noerr(ret,bail);
bail:
return ret; //*/
// TODO: Implement me!
return NYOCI_STATUS_NOT_IMPLEMENTED;
}
#if defined(CONTIKI)
nyoci_timestamp_t
nyoci_plat_cms_to_timestamp(
nyoci_cms_t cms
) {
return clock_time() + cms*CLOCK_SECOND/MSEC_PER_SEC;
}
nyoci_cms_t
nyoci_plat_timestamp_diff(nyoci_timestamp_t lhs, nyoci_timestamp_t rhs) {
return (lhs - rhs)*MSEC_PER_SEC/CLOCK_SECOND;
}
nyoci_cms_t
nyoci_plat_timestamp_to_cms(nyoci_timestamp_t ts) {
return nyoci_plat_timestamp_diff(ts, clock_time());
}
#elif defined(OS_uCOS_II_H)
nyoci_timestamp_t
nyoci_plat_cms_to_timestamp(
nyoci_cms_t cms
) {
return OSTimeGet() + cms * ((double)OS_TICKS_PER_SEC / MSEC_PER_SEC);
}
nyoci_cms_t
nyoci_plat_timestamp_diff(nyoci_timestamp_t lhs, nyoci_timestamp_t rhs) {
return (lhs - rhs) * ((double)MSEC_PER_SEC / OS_TICKS_PER_SEC);
}
nyoci_cms_t
nyoci_plat_timestamp_to_cms(nyoci_timestamp_t ts) {
return nyoci_plat_timestamp_diff(ts, OSTimeGet());
}
#endif
简要说明
这里大概说一下一些移植会出问题的地方。
首先看一下include
#include “lib_lite.h”
这个文件是我自己加的,用到的里头的函数就一个IPtoStr,在打印调试信息时用到的,不是很重要,自己可以根据需要实现一个或直接不管。
#include “Network.h”
这个也是我自己写的基于W5500的网络管理模块的头文件,主要就是提供nyoci_plat_process这个函数里头的Network_hasReady这一个判断,自己根据需要进行修改即可。
#include “os_cfg.h”
这个文件是uCOS-II的配置文件名,用于引入uCOS-II接口的。
nyoci_plat_set_remote_hostname_and_port和nyoci_plat_lookup_hostname顾名思义,用于通过字符串设置和DNS查询对方主机。这个不是必要的接口,我也没有实现它,主要是io库也没有提供好用的DNS接口,但我保留了实现的框架,如有需要可以照着实现它。
这里头最容易让人困惑的是时间戳里的cms到底是什么鬼。经过我辛苦的钻研,得出结论:这个变量代表的意思是相对时间,单位ms。
nyoci中同时使用平台特定的时间戳和单位为ms的相对时间戳。
nyoci_plat_cms_to_timestamp
就是要你返回相对当前时间cms毫秒(正代表未来,负代表过去)对应的平台时间戳。
nyoci_plat_timestamp_diff
返回两个平台时间戳之间差多少ms。
nyoci_plat_timestamp_to_cms
返回给定平台时间戳与当前时间差多少ms。
nyoci_plat_wait
也就是sleep多少毫秒的意思。
上面我加进去了在uCOS-II下的实现,如果你用的是其他操作系统的话则根据具体OS进行修改。
使用示例
这里只简单示例下这个移植后的代码的其中一种用法,其他用法请参照examples里的几个示例。
示例代码
以下是Coap服务器任务/线程的示例
……
#include <libnyoci/libnyoci.h>
#include <libnyociextra/nyoci-node-router.h>
……
#define OBSERVABLE_KEY 35
static nyoci_t CoapInstance = NULL;
static struct nyoci_observable_s observable = { 0 };
static char sensordata[] = "30.32Cel";
……
static nyoci_status_t
request_handler(void* context)
{
nyoci_observable_t observable = context;
const uint8_t* opt;
coap_size_t len;
if (!nyoci_inbound_is_fake()) {
printf("Got a request!\n");
}
// Only handle GET requests for now.
if (nyoci_inbound_get_code() != COAP_METHOD_GET) {
return NYOCI_STATUS_NOT_IMPLEMENTED;
}
// Begin describing the response.
nyoci_outbound_begin_response(COAP_RESULT_205_CONTENT);
// This is the key line to making a resource observable.
// It must be placed after nyoci_outbound_begin_response()
// and before nyoci_outbound_send(). When this resource changes,
// a simple call to nyoci_observable_trigger() with the given
// nyoci_observable object and observable key will trigger the
// observers to be updated. Really --- that's it...!
nyoci_observable_update(observable, OBSERVABLE_KEY);
nyoci_outbound_add_option_uint(
COAP_OPTION_CONTENT_TYPE,
COAP_CONTENT_TYPE_TEXT_PLAIN
);
nyoci_outbound_append_content(sensordata, sizeof(sensordata) - 1);
return nyoci_outbound_send();
}
static void CoapTask(void *p_arg){
nyoci_node_t root_node, hello_node;
NYOCI_LIBRARY_VERSION_CHECK();
while(!CoapInstance){
CoapInstance = nyoci_create();
if (!CoapInstance) {
printf("Unable to create LibNyoci instance");
OSTimeDlyHMSM(0, 0, 5, 0);
}
}
nyoci_plat_set_socketnumber(CoapInstance, COAP_SOCKET);
nyoci_plat_bind_to_port(CoapInstance, NYOCI_SESSION_TYPE_UDP, COAP_DEFAULT_PORT);
root_node = nyoci_node_init(NULL, NULL, NULL);
nyoci_set_default_request_handler(
CoapInstance,
&nyoci_node_router_handler,
(void*)root_node
);
hello_node = nyoci_node_init(NULL,root_node,"sData");
hello_node->request_handler = &request_handler;
hello_node->context = &observable;
printf("Listening on port %d\n", nyoci_plat_get_port(CoapInstance));
while (1) {
MyOS_DlyHMSM(0, 0, 0, 30);
//nyoci_plat_wait(CoapInstance, INTERVAL_PER_TICK);
nyoci_plat_process(CoapInstance);
}
nyoci_release(CoapInstance);
}
如果要触发publish的话,则在某处调用nyoci_observable_trigger:
if(++sensordata[1] > '9')
sensordata[1] = '0';
(void)printf("%d observers registered\n", nyoci_observable_observer_count(&observable, OBSERVABLE_KEY));
nyoci_observable_trigger(&observable, OBSERVABLE_KEY ,0);
简要说明
由于我这个示例使用的是多实例,所以一开始要create一个nyoci实例。
然后通过nyoci_plat_set_socketnumber设置其socket号,对应W5500的socket号。
然后用nyoci_plat_bind_to_port绑定这个Coap服务的端口为默认端口号,使用UDP,这个移植也只实现了UDP。
这个代码里头使用了router,这种方式使用链表(可选B树)来管理资源,收到请求时会在所有注册的资源中寻找匹配的资源,如果存在,则调用对应的request_handler,否则404。这里我们添加了一个叫sData的资源,又给他设置了一个observable使其可以被订阅。
然后就开始不断地用nyoci_plat_process驱动服务器实例的运行了,这个方法的含义是进行一次无阻塞的处理。
还有些其他方式,请自行探索,或者等我研究透了再出篇博文来讲。
测试
这里我通过Chrome浏览器插件Copper来进行测试。
http://element-ui.cn/news_show_33612.shtml
我目前配置的设备主机名为SIA5300.local,所以资源的地址为:
SIA5300.local/sData
然后我们使用Get方法获取资源:
获取资源成功。
然后来试一下Observe即发布订阅方式:
主动推送消息成功。
总结
这篇文章讲了移植libnyoci到wiz io库以实现一个简单的UDP Coap服务器所需要的主要事项,但是实际移植的时候由于开发环境不同等原因估摸还是会遇到很多问题,语法不支持、缺少库什么的一堆问题。如果实在搞不定,下面留言。