在“OpenHarmony源码评注|Coap协议的定义”中,我们具体讲了coap协议的定义方法并补充了Coap协议的特点。本篇我们主要讲如何传输Coap数据包。
背景知识
套接字(Socket),是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端。数据传输时,会有服务器端和客户端双向通信,通过两端的套接字进行数据收发工作。
源码分析
在communication_softbus_lite-master\discovery\coap?中定义了coap_socket.h头文件,提供了COAP包的发送和接收服务函数
#define COAP_DEFAULT_PORT 5684 //在此端口提供监听COAP包的服务
typedef struct {
int cliendFd;
struct sockaddr_in dstAddr; //目的地址
} SocketInfo;
int GetCoapServerSocket(void); //获取服务器套接字
int GetCoapClientSocket(void); //获取客户端套接字
int CoapInitSocket(void); //初始化端口的功能
int CoapCreateUdpClient(const struct sockaddr_in *sockAddr); //创建一个服务器
int CoapCreateUdpServer(const struct sockaddr_in *sockAddr); //创建一个客户端
int CoapSocketRecv(int socketFd, uint8_t *buffer, size_t length);//发送数据
int CoapSocketSend(const SocketInfo *socket, const uint8_t *buffer, size_t length); //接收数据
#define COAP_MAX_PDU_SIZE 1024 //协议数据单元大小
实现传输的过程大致分为三步:创建服务器和客户端,两端连接,数据传输
在coap_socket.c中具体实现了所有功能
创建服务器
int CoapCreateUdpServer(const struct sockaddr_in *sockAddr) //创建服务器,传输协议为UDP
{
if (sockAddr == NULL) {
return NSTACKX_EINVAL; //判断传入地址是否为空,若空返回无效
}
struct sockaddr_in localAddr; //创建本地地址结构体
socklen_t len = sizeof(localAddr); //获得地址大小
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //建立一个socket用于连接
if (sockfd < 0) {
return NSTACKX_OVERFLOW; //判断创建是否成功
}
(void)memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr));
localAddr.sin_family = AF_INET; //保存地址族
localAddr.sin_port = sockAddr->sin_port; //存放sock地址
if (sockAddr->sin_addr.s_addr != 0) { //如果sock地址部位0
localAddr.sin_addr.s_addr = sockAddr->sin_addr.s_addr; //将sock地址存入本地
} else {
localAddr.sin_addr.s_addr = htonl(INADDR_ANY); //s_addr为0,则localAttr对应的s_addr为htonl获取的地址
}
if (bind(sockfd, (struct sockaddr *)&localAddr, len) == -1) { //将sockfd和localAddr相关联的端口设备进行绑定
CloseSocket(&sockfd); //失败则关闭套接字,返回失败信息
return NSTACKX_EFAILED;
}
if (getsockname(sockfd, (struct sockaddr *)&localAddr, &len) == -1) { //获取套接字的名字
CloseSocket(&sockfd); //失败则关闭套接字
return NSTACKX_EFAILED;
}
return sockfd; //返回套接字
}
创建客户端
int CoapCreateUdpClient(const struct sockaddr_in *sockAddr) //创建客户端,协议为UDP
{
if (sockAddr == NULL) {
return NSTACKX_EFAILED; //判断传入地址是否为空
}
struct sockaddr_in tmpAddr; //创建临时套接字地址
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建一个新的套接字
if (sockfd < 0) { //检验创建是否成功
return NSTACKX_EFAILED;
}
int ret = connect(sockfd, (struct sockaddr *)sockAddr, sizeof(struct sockaddr)); //将服务端与客户端连接
if (ret != 0) { //判断连接是否成功
CloseSocket(&sockfd); //失败则关闭,返回错误信息
return NSTACKX_EFAILED;
}
socklen_t srcAddrLen = sizeof(struct sockaddr_in);
(void)memset_s(&tmpAddr, sizeof(tmpAddr), 0, sizeof(tmpAddr)); //初始化临时的套接字地址
ret = getsockname(sockfd, (struct sockaddr *)&tmpAddr, &srcAddrLen); //获取临时套接字名字
if (ret != 0) {
CloseSocket(&sockfd); //判断初始化是否成功
return NSTACKX_EFAILED; //失败则关闭,返回错误信息
}
CloseSocket(&g_clientFd); //关闭临时客户端
g_clientFd = sockfd; //重置套接字
return NSTACKX_EOK; //返回创建成功
}
其中connect函数负责将服务器与客户端连接
int ret = connect(sockfd, (struct sockaddr *)sockAddr, sizeof(struct sockaddr)); //将服务端与客户端连接
if (ret != 0) { //判断连接是否成功
CloseSocket(&sockfd); //失败则关闭,返回错误信息
return NSTACKX_EFAILED;
}
?初始化
int CoapInitSocket(void) //初始化套接字
{
if (g_serverFd >= 0) { //检查是否已经建立连接
return NSTACKX_EOK;
}
struct sockaddr_in sockAddr; //创建一个网络通信地址sockAddr
(void)memset_s(&sockAddr, sizeof(sockAddr), 0, sizeof(sockAddr)); //对sockAddr初始化
sockAddr.sin_port = htons(COAP_DEFAULT_PORT); //将多字节整数类型的数据,从主机的字节顺序转化为网络字节顺序
g_serverFd = CoapCreateUdpServer(&sockAddr); //创建服务器,获取套接字
if (g_serverFd < 0) {
return NSTACKX_OVERFLOW; //判断套接字是否有效
}
COAP_SoftBusInitMsgId(); //COAP_SoftBusInitMsgId函数把全局变量g_msgId初始化为常量RAND_DIVISOR,做为COAP协议中的MessageID
return NSTACKX_EOK; //返回成功
初始化连接的逻辑就是先判断服务器是否已经存在,若存在说明已经建立连接了,直接返回;
若没有建立连接,则创建通信地址,创建服务器
数据收发
int CoapSocketSend(const SocketInfo *socket, const uint8_t *buffer, size_t length)
{
if (buffer == NULL || socket == NULL) { //判断参数是否合法
return NSTACKX_EFAILED;
}
socklen_t dstAddrLen = sizeof(struct sockaddr_in);
int ret = sendto(socket->cliendFd, buffer, length, 0, (struct sockaddr *)&socket->dstAddr, dstAddrLen);
//sendto用来发送udp数据包,这里发送到ocket->dstAddr目的地缓冲区
return ret;
}
CoapSocketSend函数用来将数据发送到udp连接的另一端
int CoapSocketRecv(int socketFd, uint8_t *buffer, size_t length)
{
if (buffer == NULL || socketFd < 0) { //判断参数是否合法
return NSTACKX_EFAILED;
}
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
(void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
int ret = recvfrom(socketFd, buffer, length, 0, (struct sockaddr *)&addr, &len);
//recvfrom函数用来接收指定socket传来的数据
return ret;
}
CoapSocketRecv函数则负责数据接收
|