Hongmeng Hi3861 Learning Fifteen-Huawei LiteOS-M (cliente de socket)

1. Introducción

        En la programación de redes, ya sea un cliente o un servidor, Socket es inseparable . Entonces, ¿qué es Socket? Aquí hay una breve introducción. Para obtener contenido detallado, puede consultar este artículo: WIFI Learning 1 (introducción de socket)_wifi socket_t_guest's Blog-CSDN Blog

Socket se traduce como " socket "          en el campo de la informática . Es una convención o método de comunicación entre computadoras mediante el cual una computadora puede recibir o enviar datos a otra computadora.

El zócalo está diseñado         según el modo de " abrir abrir –> leer y escribir escribir/leer –> cerrar cerrar ". Un socket se puede considerar como un archivo especial , que se puede abrir , cerrar y leer/escribir E/S a través de las siguientes funciones de socket .

        El proceso general de programación del cliente de socket se puede resumir en los siguientes pasos: inicializar el socket , conectarse al servidor (conectar) , leer/escribir (escribir/leer) y cerrar (cerrar) .

 2. Introducción a la API

      enchufe

        función función:

        Cree un descriptor de socket para identificar de forma única un socket . Las operaciones posteriores de lectura y escritura deben realizarse a través de este descriptor .

        Prototipo de función:

int socket(int domain, int type, int protocol)

        parámetro:

        dominio: tipo de dirección IP. Los tipos comúnmente utilizados son AF_INET (IPV4), AF_INET6 (IPV6).

        tipo: método de transmisión de datos/tipo de socket. Los tipos comúnmente utilizados son SOCK_STREAM (socket de formato de flujo/socket orientado a la conexión  TCP ) , SOCK_DGRAM (socket de datagrama/socket sin conexión  UDP ) .

        protocolo: protocolo de transporte. El valor predeterminado es 0 y el sistema deduce automáticamente el protocolo utilizado . También se puede ingresar manualmente.Los protocolos comúnmente utilizados incluyen IPPROTO_TCP , IPPTOTO_UDP , IPPROTO_SCTP, IPPROTO_TIPC, etc., que corresponden al protocolo de transmisión TCP, protocolo de transmisión UDP, protocolo de transmisión STCP y protocolo de transmisión TIPC. El tipo y el protocolo no se pueden combinar a voluntad , como SOCK_STREAM no se puede combinar con IPPROTO_UDP.

        valor de retorno:

        NULO: fallido

        Otros valores: descriptor de socket

        Ejemplo:

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
 
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //创建UDP套接字

      lwip_conectar

        función función:

        El cliente establece una conexión con el servidor a través de esta función

        Prototipo de función:

#define lwip_connect      connect
int connect(int fd, const struct sockaddr *addr, socklen_t len)

        parámetro:

        fd: descriptor de socket, devuelto por la función socket() .

        addr: información sobre el servidor a conectar, incluyendo IP y puerto, etc.

        len: la longitud de la información relacionada con el servidor

        valor de retorno:

        -1: fallado

        0: éxito

        Ejemplo:

    int sock_fd; 

    struct sockaddr_in socket_addr;
    memset(&socket_addr, 0, sizeof(socket_addr));

    socket_addr.sin_family = AF_INET;	//IPV4
    socket_addr.sin_port = htons(_PROT_);	//端口
    socket_addr.sin_addr.s_addr = inet_addr("192.168.3.198");	//IP地址转换

    socklen_t addr_length = sizeof(socket_addr);

    LOG_I( "connect port:%d, addr:%s",_PROT_,inet_ntoa(socket_addr.sin_addr));

    int ret  = 0;

    ret = lwip_connect(sock_fd, (struct sockaddr *)&socket_addr, addr_length);
    if(ret < 0) //失败{}

         Si la puerta de enlace es el servidor , la información relevante aquí se puede escribir de la siguiente manera.

struct netif *sta_if = netifapi_netif_find("wlan0"); 
socket_addr.sin_addr.s_addr = inet_addr(ipaddr_ntoa(&sta_if->gw));    //网关IP

      lwip_escribir

        función función:

escribir datos         en el zócalo

        Prototipo de función:

ssize_t lwip_write(int s, const void *data, size_t size)

        parámetro:

        s: descriptor de socket, devuelto por la función socket() .

        data: los datos a enviar

        tamaño: la longitud de los datos a enviar

        valor de retorno:

        -1: fallado

        Otros valores: número de bytes enviados con éxito

        Ejemplo:

int sock_fd;
const char *send_data = "This is a socket client test!\r\n";
if(lwip_write(sock_fd,send_data, strlen(send_data)) != -1)    //发送成功
{}

      lwip_leer

        función función:

Leer datos         del socket . espera de bloqueo

        Prototipo de función:

ssize_t lwip_read(int s, void *mem, size_t len)

        parámetro:

        s: descriptor de socket, devuelto por la función socket() .

        mem: la dirección del almacenamiento de datos recibido

        len: longitud máxima de datos recibidos

        valor de retorno:

        -1: fallado

Otros valores: Devuelve el número de bytes leídos         si es correcto y devuelve 0 cuando encuentra el final del archivo.

        Ejemplo:

int sock_fd; 
char recvBuf[512] = {0};
if((temp_len = lwip_read(sock_fd, recvBuf, sizeof(recvBuf))) > 0) //读成功
{}

      lwip_cerrar

        función función:

        Cerrar un socket previamente abierto

        Prototipo de función:

int closesocket(int s)

        parámetro:

        s: descriptor de socket, devuelto por la función socket() .

        valor de retorno:

        0: éxito

        Otros valores: fallar

        Ejemplo:

int sock_fd; 
closesocket(sock_fd);

      enviar a

        función función:

        Envía datos al servidor (usualmente usado para UDP ).

        Prototipo de función:

ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen)

        parámetro:

        fd: descriptor de socket, el valor de retorno de socket().

        buf: los datos a enviar

        len: la longitud de los datos a enviar

        banderas: por defecto 0

        addr: información relacionada con el servidor.

        alen: longitud de la información del servidor

        valor de retorno:

        -1: fallado

        Otros valores: número de bytes enviados con éxito

        Ejemplo:

int sock_fd;

//服务器的地址信息
struct sockaddr_in send_addr;
socklen_t addr_length = sizeof(send_addr);

//初始化预连接的服务端地址
send_addr.sin_family = AF_INET;
send_addr.sin_port = htons(_PROT_);
send_addr.sin_addr.s_addr = inet_addr("192.168.3.198");
addr_length = sizeof(send_addr);
sendto(sock_fd, send_data, strlen(send_data), 0, (struct sockaddr *)&send_addr, addr_length);

      recibido desde

        función función:

        Recibir los datos transmitidos por el socket (usualmente usado para UDP ), bloquear y esperar .

        Prototipo de función:

ssize_t recvfrom(int s, void *mem, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen)

        parámetro:

        s: descriptor de socket, devuelto por la función socket() .

        mem: la dirección donde se almacenan los datos recibidos

        len: longitud máxima de datos recibidos,

        banderas: el valor predeterminado es 0.

        addr: información relacionada con el servidor.

        alen: longitud de la información del servidor

        valor de retorno:

        -1: fallado

        Otros valores: longitud de datos recibidos

        Ejemplo:

    int sock_fd;    //在sock_fd 进行监听,在 new_fd 接收新的链接
    char recvBuf[512] = {0};
    
    //服务器的地址信息
    struct sockaddr_in send_addr;
    socklen_t addr_length = sizeof(send_addr);

    //初始化预连接的服务端地址
    send_addr.sin_family = AF_INET;
    send_addr.sin_port = htons(_PROT_);
    send_addr.sin_addr.s_addr = inet_addr("192.168.3.198");
    addr_length = sizeof(send_addr);


    recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&send_addr, &addr_length);

      lwip_setsockopt

        función función:

        Establecer opciones de descriptor de socket

        Prototipo de función:

#define lwip_setsockopt   setsockopt
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)

        parámetro:

        s: descriptor de socket, devuelto por la función socket() .

        nivel: el nivel definido por la opción

SOL_SOCKET,套接字层
IPPROTO_TCP,TCP层
IPPROTO_IP,IP层
IPPROTO_IPV6,IPV6层

        optname: la opción que debe configurarse. Aquí solo se describen las opciones más utilizadas. Cuando el nivel es SOL_COCKET (capa de socket), optname puede elegir los siguientes valores:

/*设置发送超时*/
#define SO_SNDTIMEO     0x1005 /* send timeout */
/*设置接收超时*/
#define SO_RCVTIMEO     0x1006 /* receive timeout */

         optval: apunta al búfer que almacena el nuevo valor de la opción a establecer

        optlen: longitud del búfer

        valor de retorno:

        -1: fallado

        0: éxito

        Ejemplo:

struct timeval timeout;
timeout.tv_sec  = 30;   //秒
timeout.tv_usec = 0;    //微秒
if (setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
    LOG_I(lwip_socket_example, "Setsockopt failed - set rcvtimeo\n");
}

      htonl()、htons()、ntohl()、ntohs()

        función función:

        Al programar, a menudo encuentra problemas con el orden de los bytes de la red y el orden del host . En este momento, es necesario ajustar las cuatro funciones anteriores.

htonl()--"Host to Network Long"
 
ntohl()--"Network to Host Long"
 
htons()--"Host to Network Short"
 
ntohs()--"Network to Host Short"

        Orden de host : big endian o little endian . Big-endian significa que los bytes de orden superior se organizan en el extremo de dirección inferior de la memoria, y los bytes de orden inferior se organizan en el extremo de dirección superior de la memoria (los bytes de orden superior van primero y siguen los bytes de orden bajo ). Little-endian significa que los bytes de orden inferior se organizan en el extremo de dirección inferior de la memoria, y los bytes de orden superior se organizan en el extremo de dirección superior de la memoria ( los bytes de orden inferior van primero y siguen los bytes de orden superior ).

       Orden de bytes de red : El valor de 32 bits de 4 bytes se transmite en el siguiente orden: primero 0~7 bits, luego 8~15 bits, luego 16~23 bits y finalmente 24~31 bits. Este orden de transmisión se llama big-endian . Dado que todos los enteros binarios en el encabezado TCP/IP deben estar en este orden cuando se transmiten en la red, también se denomina orden de bytes de red .

        Prototipo de función:

uint32_t htonl(uint32_t hostlong);

        parámetro:

        Valor antes de la conversión

        valor de retorno:

        valor convertido

        Ejemplo:

server_sock.sin_addr.s_addr = htonl(INADDR_ANY);    //地址,随意分配

        

     dirección_inet()      

        función función:   

        Convierta una dirección de host de red (192.168.xx) en una dirección de orden de bytes de red .

        Prototipo de función:

in_addr_t inet_addr(const char* cp)

        parámetro:

        cp: dirección de host de red (192.168.xx)

        valor de retorno:

        -1: Fallido. Cuando cp no es válido (255.255.255.0 u otro) devuelve -1.

        Otros valores: dirección en orden de bytes de red

        Ejemplo:

send_addr.sin_addr.s_addr = inet_addr("192.168.3.198");

     inet_aton()      

        función función:   

        Convierta la dirección del host (192.168.xx) a valor binario .

Nota: esta función no se puede usar para la transmisión de red         después de la conversión , y se debe llamar a la función htons o htonl para convertir el orden de bytes del host al orden de bytes de la red .

        Prototipo de función:

int inet_aton(const char* cp, struct in_addr* inp)

        parámetro:

        cp: valor de entrada, dirección de host de red (192.168.xx)

        inp: valor de salida , valor binario convertido

        valor de retorno:

        0: dirección de host no válida

        Otros valores: la dirección del host es válida

        Ejemplo:

struct sockaddr_in sin1;	
inet_aton("127.0.0.1", &sin1.sin_addr);

     guerra_inet()      

        función función:   

        Convierta una dirección en orden de bytes de red a ASICC (xxxx).

        Nota: El espacio para la cadena se asigna de forma estática , lo que significa que cuando se llama a la función por segunda vez, se sobrescribirá el valor de salida de la llamada anterior .

        Prototipo de función:

char *inet_ntoa(struct in_addr in)

        parámetro:

        in: El tipo es in_addr. Dirección en orden de bytes de red.

typedef uint32_t in_addr_t;

        valor de retorno:

        cadena convertida

        Ejemplo:

struct in_addr addr1,addr2;

addr1 = inet_addr("192.168.0.74");
addr2 = inet_addr("211.100.21.179");

printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //不可以这么用,结果会被覆盖  
printf("%s\n", inet_ntoa(addr1));   
printf("%s\n", inet_ntoa(addr2));

输出结果:
192.168.0.74 : 192.168.0.74          //从这里可以看出,printf里的inet_ntoa只运行了一次。

192.168.0.74  

211.100.21.179 

     guerra_ipaddr()      

        función función:   

        Convierte una dirección de tipo de dirección de red a ASICC (xxxx).

        Nota: ipaddr_ntoa e inet_ntoa tienen la misma función , ambos convierten el parámetro pasado en una cadena . Sin embargo, los tipos de datos de los parámetros pasados ​​son diferentes.El tipo del parámetro ipaddr_ntoa es un puntero a ip_addr_t , mientras que el parámetro inet_ntoa es un número entero de tipo uint32_t .

        Prototipo de función:

char *ipaddr_ntoa(const ip_addr_t *addr)

        parámetro:

        dirección: dirección de red

        valor de retorno:

        cadena convertida

        Ejemplo:

lwip_netif = netifapi_netif_find("ap0");  //获取网络借口,用于IP操作
LOG_I("ip addr:%s",ipaddr_ntoa(&lwip_netif->ip_addr));

     ipaddr_aton()      

        función función:   

        Convierta la dirección del host (192.168.1.1) al tipo de byte de red

        Prototipo de función:

int ipaddr_aton(const char *cp, ip_addr_t *addr)

        parámetro:

        cp: valor de entrada, dirección de host de red (192.168.xx)

        addr: valor de salida, dirección de red, el tipo es ip_addr_t.

typedef struct ip_addr {
  union {
    ip6_addr_t ip6;
    ip4_addr_t ip4;
  } u_addr;
  /** @ref lwip_ip_addr_type */
  u8_t type;
} ip_addr_t;

        valor de retorno:

        0: éxito

        Otros valores: fallar

        Ejemplo:

struct netif *lwip_netif = NULL;
ipaddr_ntoa("192.168.1.1",&lwip_netif->ip_addr));

     inet_pton()      

        función función:   

Convierta la dirección IP         decimal con puntos (192.168.xx) en un formato de datos para la transmisión de red

        Prototipo de función:

int inet_pton(int af, const char *src, void *dst)

        parámetro:

        af: familia de direcciones, AF_INET (IPV4), AF_INET6 (IPV6).

        src: valor de entrada, dirección IP en notación decimal con puntos (192.168.xx)

        dst: valor de salida , formato de datos para transmisión de red

        valor de retorno:

        -1: Anormal

        0: el valor de entrada es anormal

        1: éxito

        Ejemplo:

struct sockaddr_in socket_addr;
inet_pton(AF_INET, "192.168.1.110", &socket_addr.sin_addr); 
/*
代替socket_addr.sin_addr.s_addr = inet_addr("192.168.1.110");	//IP地址转换
*/

     inet_ntop()      

        función función:   

        Convierta el formato de datos utilizado para la transmisión de red en formato de dirección IP decimal con puntos (192.168.xx)

        Prototipo de función:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)

        parámetro:

        af: familia de direcciones, AF_INET (IPV4), AF_INET6 (IPV6).

        src: valor de entrada, datos en formato de datos de transmisión de red

        dst: valor de salida , formato de dirección IP (192.168.xx)

        tamaño: valor de entrada, tamaño de la unidad de almacenamiento de destino.

        valor de retorno:

        0: éxito

        Otros valores: fallar. La longitud del tamaño de ENOSPC es demasiado pequeña.

        Ejemplo:

char str[INET_ADDRSTRLEN];
struct sockaddr_in socket_addr;

char *ptr = inet_ntop(AF_INET,&socket_addr.sin_addr, str, sizeof(str));      
// 代替 ptr = inet_ntoa(socket_addr.sin_addr)

3. Ejemplo

        Cree un TCP y un UDP aquí respectivamente

         Ahora agregue el siguiente código al archivo BUILD.gn

    include_dirs = [
        "//utild/native/lite/include",
        "//base/iot_hardware/interfaces/kits/wifiiot_lite",
        "//utils/native/lite/include",
        "//kernel/liteos_m/components/cmsis/2.0",
        "//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
        "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include/",
    ]
/*TCP*/


#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"

#include "wifi_device.h"
#include "lwip/netifapi.h"
#include "lwip/api_shell.h"
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include "lwip/sockets.h"

#define LOG_I(fmt, args...)   printf("<%8ld> - [SOCKET_CLIENT]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...)   printf("<%8ld>-[SOCKET_CLIENT_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);

#define _PROT_ 7682


static const char *send_data = "Hello! I'm BearPi-HM_Nano UDP Client!\r\n";

static void SocketClientTask(void)
{
    int sock_fd;    //在sock_fd 进行监听,在 new_fd 接收新的链接
    char recvBuf[512] = {0};

    //连接Wifi
    extern int drv_wifi_connect(const char *ssid, const char *psk);
    drv_wifi_connect("Harmony_test_ap", "123123123");

    LOG_I("wifi connect success");

    //创建socket
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        LOG_E("create socket failed!\r\n");
        exit(1);
    }

    LOG_I("socket TCP create done");

    struct sockaddr_in socket_addr;
    memset(&socket_addr, 0, sizeof(socket_addr));

    socket_addr.sin_family = AF_INET;	//IPV4
    socket_addr.sin_port = htons(_PROT_);	//端口
    socket_addr.sin_addr.s_addr = inet_addr("192.168.3.198");	//IP地址转换
    // socket_addr.sin_addr.s_addr = inet_addr(ipaddr_ntoa(&sta_if->gw));    //网关IP

    socklen_t addr_length = sizeof(socket_addr);

    LOG_I( "connect port:%d, addr:%s",_PROT_,inet_ntoa(socket_addr.sin_addr));

    int ret  = 0;
    do{
        ret = lwip_connect(sock_fd, (struct sockaddr *)&socket_addr, addr_length);
        if(ret < 0) //失败
        {
            LOG_I("socket connect fail");
            // lwip_close(sock_fd);    //关闭socket
            osDelay(100);
        }
    }while(1);

    LOG_I("socket connect success");

    struct timeval timeout;
    timeout.tv_sec = 5;       //秒
    timeout.tv_usec = 0;     //微秒
    if (lwip_setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) //设置接收超时
    {
        LOG_E("Setsockopt failed - set rcvtimeo\n");
    }

    LOG_I("set socket receive timeout:%d",timeout.tv_sec);

    int temp_len = 0;

    while(1)
    {
        bzero(recvBuf, sizeof(recvBuf));

        if(lwip_write(sock_fd,send_data, strlen(send_data)) != -1)
        {
            LOG_I("socket write success");
        }
        else
        {
            LOG_E("socket write fail");
        }

        temp_len = 0;
        if((temp_len = lwip_read(sock_fd, recvBuf, sizeof(recvBuf))) > 0) //读成功
        {
            LOG_I( "TCP client >>>>>read>>>>> data,len:%d,data:%s", temp_len, recvBuf);
        }
        else
        {
            LOG_E("socket client read fail");
        }
    }

    //关闭这个 socket
    closesocket(sock_fd);
}

void app_socket_client_init(void)
{
    osThreadAttr_t attr;

    attr.name = "UDPClientTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    if (osThreadNew((osThreadFunc_t)SocketClientTask, NULL, &attr) == NULL)
    {
        LOG_E("[UDPClientDemo] Falied to create UDPClientTask!\n");
    }
}
/*UDP*/


#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"

#include "wifi_device.h"
#include "lwip/netifapi.h"
#include "lwip/api_shell.h"
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include "lwip/sockets.h"

#define LOG_I(fmt, args...)   printf("<%8ld> - [SOCKET_CLIENT]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...)   printf("<%8ld>-[SOCKET_CLIENT_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);

#define _PROT_ 7682


static const char *send_data = "This is a UDP client test!\r\n";

static void SocketClientTask(void)
{
    int sock_fd;    //在sock_fd 进行监听,在 new_fd 接收新的链接
    char recvBuf[512] = {0};

    //连接Wifi
    extern int drv_wifi_connect(const char *ssid, const char *psk);
    drv_wifi_connect("Harmony_test_ap", "123123123");

    LOG_I("wifi connect success");


    //创建socket
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        LOG_E("create socket failed!\r\n");
        exit(1);
    }

    LOG_I("socket UDP create done");

    //服务器的地址信息
    struct sockaddr_in send_addr;
    socklen_t addr_length = sizeof(send_addr);

    //初始化预连接的服务端地址
    send_addr.sin_family = AF_INET;
    send_addr.sin_port = htons(_PROT_);
    send_addr.sin_addr.s_addr = inet_addr("192.168.3.198");
    addr_length = sizeof(send_addr);

    //总计发送 count 次数据
    while (1)
    {
        bzero(recvBuf, sizeof(recvBuf));

        //发送数据到服务远端
        sendto(sock_fd, send_data, strlen(send_data), 0, (struct sockaddr *)&send_addr, addr_length);

        LOG_I("socket send done");

        //线程休眠一段时间
        sleep(10);
        // osDelay(500);

        LOG_I("socket wait receive data");

        //接收服务端返回的字符串
        recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&send_addr, &addr_length);
        LOG_I("%s:%d=>%s\n", inet_ntoa(send_addr.sin_addr), ntohs(send_addr.sin_port), recvBuf);
    } 

    //关闭这个 socket
    closesocket(sock_fd);
}

void app_socket_client_init(void)
{
    osThreadAttr_t attr;

    attr.name = "UDPClientTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    if (osThreadNew((osThreadFunc_t)SocketClientTask, NULL, &attr) == NULL)
    {
        LOG_E("[UDPClientDemo] Falied to create UDPClientTask!\n");
    }
}

        Resultado: debido a que se usa la versión 1.0 de SDK, hay un problema con TCP, así que primero veamos los resultados de UDP, y el TCP de seguimiento se reemplazará con una versión diferente de SDK para compensar.

 

Supongo que te gusta

Origin blog.csdn.net/qq_26226375/article/details/130708762
Recomendado
Clasificación