0%

OpenHarmony|CoAP协议解读之socket

本篇博客简单的来看一下CoAP_socket部分。

还是从头文件开始看起,首先是两个宏定义:

1
2
#define COAP_DEFAULT_PORT      5684
#define COAP_MAX_PDU_SIZE 1024
  • 从命名可以看出,我们的 CoAP 协议默认是运行在UDP的 5684 端口上
  • 另一个宏定义限制了 CoAP 协议每个数据包的最大长度

另外还定义了一个结构体用于记录 socket 的信息:

1
2
3
4
typedef struct {
int cliendFd;
struct sockaddr_in dstAddr;
} SocketInfo;

最后还定义了 7 个函数对外提供相关的服务:

1
2
3
4
5
6
7
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);
  • GetCoapServerSocket
    • 获取服务端 socket 的描述符 fd
  • GetCoapClientSocket
    • 获取客户端 socket 的描述符 fd
  • CoapInitSocket
    • 调用CoapCreateUdpServer初始化服务端
  • CoapCreateUdpClient
    • 客户端创建 socket 连接服务端
  • CoapCreateUdpServer
    • 服务端创建 socket 并绑定端口,等待客户端连接
  • CoapSocketRecv
    • 数据接收
  • CoapSocketSend
    • 数据发送

其中GetCoapServerSocketGetCoapClientSocket返回的是一个全局的套接字描述符:

1
2
3
4
5
6
7
8
9
10
11
12
int g_serverFd = -1;
int g_clientFd = -1;

int GetCoapServerSocket(void)
{
return g_serverFd;
}

int GetCoapClientSocket(void)
{
return g_clientFd;
}

下面是服务端的CoapInitSocket调用CoapCreateUdpServer创建 socket 并绑定端口,等待客户端连接:

CoapInitSocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int CoapInitSocket(void)
{
if (g_serverFd >= 0) {
return NSTACKX_EOK;
}
struct sockaddr_in sockAddr;
(void)memset_s(&sockAddr, sizeof(sockAddr), 0, sizeof(sockAddr));
sockAddr.sin_port = htons(COAP_DEFAULT_PORT);
g_serverFd = CoapCreateUdpServer(&sockAddr);
if (g_serverFd < 0) {
return NSTACKX_OVERFLOW;
}
COAP_SoftBusInitMsgId();
return NSTACKX_EOK;
}
  • 第 8 行写入了默认的端口号 5684 :sockAddr.sin_port = htons(COAP_DEFAULT_PORT);
  • 紧接着第 9 行调用了CoapCreateUdpServer创建 socket 并把描述符赋值给全局变量g_serverFd

CoapCreateUdpServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int CoapCreateUdpServer(const struct sockaddr_in *sockAddr)
{
if (sockAddr == NULL) {
return NSTACKX_EINVAL;
}

struct sockaddr_in localAddr;
socklen_t len = sizeof(localAddr);
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
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;
if (sockAddr->sin_addr.s_addr != 0) {
localAddr.sin_addr.s_addr = sockAddr->sin_addr.s_addr;
} else {
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
}

if (bind(sockfd, (struct sockaddr *)&localAddr, len) == -1) {
CloseSocket(&sockfd);
return NSTACKX_EFAILED;
}

if (getsockname(sockfd, (struct sockaddr *)&localAddr, &len) == -1) {
CloseSocket(&sockfd);
return NSTACKX_EFAILED;
}
return sockfd;
}
  • 第 9 行创建了一个 socket 并拿到了描述符:int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  • 第 23 行把 socket 与端口进行了绑定:bind(sockfd, (struct sockaddr *)&localAddr, len)

另外sockaddr的定义是:

1
2
3
4
struct sockaddr {  
sa_family_t sin_family;//地址族
   char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};

上面说完了服务端的,现在来看到客户端的:

CoapCreateUdpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int CoapCreateUdpClient(const struct sockaddr_in *sockAddr)
{
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;
}
  • 同样的,在第 8 行创建了一个 socket :int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  • 随后使用获得的 socket 对指定地址进行尝试连接:int ret = connect(sockfd, (struct sockaddr *)sockAddr, sizeof(struct sockaddr));
  • 最后是对连接的状态进行检查,若发送错误则会调用CloseSocket(&sockfd);关闭对应的 socket 并返回错误值。

最后来看到数据的收发:

CoapSocketSend

1
2
3
4
5
6
7
8
9
10
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);
return ret;
}

CoapSocketRecv

1
2
3
4
5
6
7
8
9
10
11
12
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);
return ret;
}

都是根据传入的描述符socketFd、存放数据的缓冲区buffer、数据长度length,分别调用sendtorecvfrom进行数据的收发。