当前位置:   article > 正文

鸿蒙Hi3861学习十六-Huawei LiteOS-M(Socket服务端)_鸿蒙socket bind和connect

鸿蒙socket bind和connect

一、简介

        具体概念可以参考上一章内容:鸿蒙Hi3861学习十五-Huawei LiteOS-M(Socket客户端)_t_guest的博客-CSDN博客

 WIFI学习一(socket介绍)_wifi socket_t_guest的博客-CSDN博客

 

 二、API介绍

      bind

        函数功能:

        将socket和输入参数的地址属性进行绑定

        函数原型:

int bind(int fd, const struct sockaddr *addr, socklen_t len)

        参数:

        fd:套接字描述符,socket()函数返回值

        addr:要绑定的属性值。包括端口IP地址

  1. struct sockaddr_in {
  2. u8_t sin_len; //长度
  3. sa_family_t sin_family; //地址族(address family),也就是地址类型
  4. in_port_t sin_port; //16位端口号
  5. struct in_addr sin_addr; //32位IP地址
  6. #define SIN_ZERO_LEN 8
  7. char sin_zero[SIN_ZERO_LEN]; //不使用,一般用0填充
  8. }

         这里需要注意的是,bind函数的第二个参数,会将sockaddr_in类型强转为socketaddr

        sockaddr结构体定义如下:

  1. struct sockaddr {
  2. u8_t sa_len; //长度
  3. sa_family_t sa_family; //地址族(address family),也就是地址类型
  4. char sa_data[14]; //IP地址和端口号
  5. };

        sockaddr sockaddr_in 长度相同,都是16字节,只是将IP地址和端口号合并到一起,用一个成员 sa_data 表示。要想给 sa_data 赋值,必须同时指明IP地址和端口号,例如”127.0.0.1:80“,遗憾的是,没有相关函数将这个字符串转换成需要的形式,也就很难给 sockaddr 类型的变量赋值,所以使用 sockaddr_in 来代替。这两个结构体的长度相同,强制转换类型时不会丢失字节,也没有多余的字节。

        可以认为,sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体

         len:属性值长度

        返回值:

        0:成功

        其他值:失败

        实例:

  1. struct sockaddr_in server_sock;
  2. bzero(&server_sock, sizeof(server_sock));
  3. server_sock.sin_family = AF_INET; //IPV4
  4. server_sock.sin_addr.s_addr = htonl(INADDR_ANY); //地质自动分配
  5. server_sock.sin_port = htons(_PROT_); //端口
  6. //调用bind函数绑定socket和地址
  7. if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1)
  8. {}

      listen

        函数功能:

        让套接字进入被动监听状态,如果客户端此时调用lwip_connect发送连接请求,服务器端就会收到这个请求

        函数原型:

int listen(int fd, int backlog)

        参数:

        fd:套接字描述符,socket()函数返回值

        backlog:请求队列的最大长度。当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没办法处理的,只能先放入缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,就将他们按照先后顺序存放在缓冲区队列中,直到缓冲区满。这个缓冲区,称为请求队列(Request Queue)。

        返回值:

        0:成功

        其他值:失败

        实例:

  1. int sock_fd;
  2. if (listen(sock_fd, 10) == -1) //失败
  3. {}

      accept

        函数功能:

        当套接字处于监听状态时,可以通过accept()函数来接收客户端的请求

        函数原型:

int accept(int fd, struct sockaddr *restrict addr, socklen_t *restrict len)

        参数:

        fd:套接字描述符,lwip_accept()函数返回值

        addr:客户端的IP和端口(输出

        len:客户端信息最大长度(输入

        返回值:

        -1:失败

        其他值:监听到的客户端套接字描述符

        实例:

  1. int new_fd;
  2. struct sockaddr_in client_sock;
  3. int sin_size;
  4. sin_size = sizeof(struct sockaddr_in);
  5. if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1) //失败
  6. {}

      setsockopt

        函数功能:

        设置套接字描述符选项

        函数原型:

int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)

        参数:

        fd:套接字描述符,socket()函数返回值

        level:选项定义的层次。

  1. SOL_SOCKET,套接字层
  2. IPPROTO_TCP,TCP层
  3. IPPROTO_IP,IP层
  4. IPPROTO_IPV6,IPV6层

        optname:需要设置的选项

        level为SOL_SOCKET(套接字层),optname可以取以下值:

  1. #define SO_DEBUG 0x0001 /* turn on debugging info recording */
  2. #define SO_DONTROUTE 0x0010 /* just use interface addresses */
  3. #define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
  4. #define SO_LINGER 0x0080 /* linger on close if data present */
  5. #define SO_DONTLINGER ((int)(~SO_LINGER))
  6. #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
  7. #define SO_REUSEPORT 0x0200 /* allow local address & port reuse */
  8. #define SO_SNDBUF 0x1001 /* send buffer size */
  9. #define SO_RCVBUF 0x1002 /* receive buffer size */
  10. #define SO_CONTIMEO 0x1009 /* connect timeout */
  11. #define SO_NO_CHECK 0x100a /* don't create UDP checksum */
  12. #define SO_BINDTODEVICE 0x100b /* bind to device */
  13. #define SO_REUSEADDR 0x0004 /* Allow local address reuse */
  14. #define SO_KEEPALIVE 0x0008 /* keep connections alive */
  15. #define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
  16. #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
  17. #define SO_ERROR 0x1007 /* get error status and clear */
  18. #define SO_SNDLOWAT 0x1003 /* send low-water mark */
  19. #define SO_SNDTIMEO 0x1005 /* send timeout */
  20. #define SO_RCVLOWAT 0x1004 /* receive low-water mark */
  21. #define SO_RCVTIMEO 0x1006 /* receive timeout */
  22. #define SO_TYPE 0x1008 /* get socket type */

        SO_DEBUG打开或关闭调试信息。BOOL

当option_value不等于0时,打开调试信息,否则,关闭调试信息。它实际所做的工作是在sock->sk->sk_flag中置 SOCK_DBG(第10)位,或清SOCK_DBG位。

        SO_DONTROUTE打开或关闭路由查找功能。BOOL

当option_value不等于0时,打开,否则,关闭。它实际所做的工作是在sock->sk->sk_flag中置或清SOCK_LOCALROUTE位。

        SO_LINGER延缓关闭。struct linger

如果选择此选项, close或 shutdown将等到所有套接字里排队的消息成功发送或到达延迟时间后>才会返回. 否则, 调用将立即返回。

该选项的参数(option_value)是一个linger结构:

        struct linger {
            int   l_onoff;   //开关
            int   l_linger;  //延迟时间
        };

如果linger.l_onoff值为0(关闭),则清 sock->sk->sk_flag中的SOCK_LINGER位;否则,置该位,并赋sk->sk_lingertime值为 linger.l_linger。

        SO_DONTLINER ,不延缓关闭。BOOL

不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。

        SO_OOBINLINE,紧急数据放入普通数据流。BOOL

该操作根据option_value的值置或清sock->sk->sk_flag中的SOCK_URGINLINE位。

        SO_SNDBUF设置发送缓冲区的大小。INT

发送缓冲区的大小是有上下限的,其上限为256 * (sizeof(struct sk_buff) + 256),下限为2048字节。该操作将sock->sk->sk_sndbuf设置为val * 2,之所以要乘以2,是防止大数据量的发送,突然导致缓冲区溢出。最后,该操作完成后,因为对发送缓冲的大小作了改变,要检查sleep队列,如果有进程正在等待写,将它们唤醒。

        SO_RCVBUF设置接收缓冲区的大小。INT

接收缓冲区大小的上下限分别是:256 * (sizeof(struct sk_buff) + 256)和256字节。该操作将sock->sk->sk_rcvbuf设置为val * 2。

        SO_NO_CHECK打开或关闭校验和。BOOL

该操作根据option_value的值,设置sock->sk->sk_no_check。

        SO_BINDTODEVICE,将套接字绑定到一个特定的设备上。BOOL

该选项最终将设备赋给sock->sk->sk_bound_dev_if。

        SO_REUSEADDR打开或关闭地址复用功能。BOOL

当option_value不等于0时,打开,否则,关闭。它实际所做的工作是置sock->sk->sk_reuse为1或0。

        SO_KEEPALIVE,套接字保活。BOOL

如果协议是TCP,并且当前的套接字状态不是侦听(listen)或关闭(close),那么,当option_value不是零时,启用TCP保活定时 器,否则关闭保活定时器。对于所有协议,该操作都会根据option_value置或清 sock->sk->sk_flag中的 SOCK_KEEPOPEN位。

        SO_BROADCAST允许或禁止发送广播数据。BOOL

当option_value不等于0时,允许,否则,禁止。它实际所做的工作是在sock->sk->sk_flag中置或清SOCK_BROADCAST位。

        SO_RCVTIMEO,设置接收超时时间。INT

该选项最终将接收超时时间赋给sock->sk->sk_rcvtimeo。

         SO_SNDTIMEO,设置发送超时时间。INT

  1. int keepAlive = 1; // 非0值,开启keepalive属性
  2. //设置保活模式
  3. if(setsockopt(*client, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int)) < 0)
  4. {
  5. } //失败

        level为IPPROTO_TCP(TCP层),optname可以取一下值:

  1. #define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
  2. #define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
  3. #define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
  4. #define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */
  5. #define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */
  6. #define TCP_MAXSEG 0x06 /* set maximum segment size */

        TCP_NODELAY不延迟发送。BOOL

        TCP_KEEPALIVE,当在空闲时,发送keepalive探测包

        TCP_KEEPIDLE,设置连接上如果没有数据发送时,多久发送keepalive探测分组,单位为秒。

        TCP_KEEPINTVL,前后两次探测之间的时间间隔,单位是秒

        TCP_KEEPCNT最大重试次数

        返回值:

        -1:失败

        其他值:成功

        实例:

  1. int keepAlive = 1; //开启keepalive属性
  2. int keepIdle = 5; //如果连接在5秒内没有任何数据来往,则进行此TCP层的探测
  3. int keepInterval = 5; //探测发包间隔为5秒
  4. int keepCount = 2; //尝试探测的最多次数
  5. if(setsockopt(*client, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int)) < 0)
  6. {
  7. //失败
  8. }
  9. if(setsockopt(*client, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int)) < 0)
  10. {
  11. //失败
  12. }
  13. if(setsockopt(*client, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int)) < 0)
  14. {
  15. //失败
  16. }
  17. if(setsockopt(*client, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int)) < 0)
  18. {
  19. //失败
  20. }

三、实例

        这里创建一个热点,并且再创建一个socket服务端,等待设备连接。

        在BUILD.gn文件中添加如下代码:

  1. include_dirs = [
  2. "//utild/native/lite/include",
  3. "//base/iot_hardware/interfaces/kits/wifiiot_lite",
  4. "//utils/native/lite/include",
  5. "//kernel/liteos_m/components/cmsis/2.0",
  6. "//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
  7. "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include/",
  8. "src",
  9. ]
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include "ohos_init.h"
  4. #include "cmsis_os2.h"
  5. #include "lwip/sockets.h"
  6. #include "lwip/netifapi.h"
  7. #define LOG_I(fmt, args...) printf("<%8ld> - [SOCKET_SERVER]:"fmt"\r\n",osKernelGetTickCount(),##args);
  8. #define LOG_E(fmt, args...) printf("<%8ld>-[SOCKET_SERVER_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);
  9. #define _PROT_ 8888
  10. #define TCP_BACKLOG 10
  11. //在sock_fd 进行监听,在 new_fd 接收新的链接
  12. static int sock_fd, new_fd;
  13. static char recvbuf[512] = {0};
  14. static char *buf = "Hello! This is a socket server test";
  15. static void TCPServerTask(void)
  16. {
  17. //服务端地址信息
  18. struct sockaddr_in server_sock;
  19. //客户端地址信息
  20. struct sockaddr_in client_sock;
  21. int sin_size;
  22. struct sockaddr_in *cli_addr;
  23. //连接Wifi
  24. extern BOOL drv_wifi_create_ap(const char *ssid, const char *psk);
  25. if(drv_wifi_create_ap("Harmony_test_ap", "123123123") != 0)
  26. {
  27. LOG_E("AP create error\r\n");
  28. exit(1);
  29. }
  30. //创建socket
  31. if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  32. {
  33. LOG_E("socket is error\r\n");
  34. exit(1);
  35. }
  36. LOG_I("socket create success");
  37. bzero(&server_sock, sizeof(server_sock));
  38. server_sock.sin_family = AF_INET; //IPV4
  39. server_sock.sin_addr.s_addr = htonl(INADDR_ANY); //地址自动分配
  40. server_sock.sin_port = htons(_PROT_); //端口
  41. //调用bind函数绑定socket和地址
  42. if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1)
  43. {
  44. LOG_E("bind is error\r\n");
  45. exit(1);
  46. }
  47. LOG_I("socket bind success");
  48. //调用listen函数监听(指定port监听)
  49. if (listen(sock_fd, TCP_BACKLOG) == -1)
  50. {
  51. LOG_E("listen is error\r\n");
  52. exit(1);
  53. }
  54. LOG_I("there is a client has connected");
  55. struct netif *lwip_netif = NULL;
  56. lwip_netif = netifapi_netif_find("ap0"); //获取网络借口,用于IP操作
  57. LOG_I("ip addr:%s",ipaddr_ntoa(&lwip_netif->ip_addr));
  58. LOG_I("netmask:%s",ipaddr_ntoa(&lwip_netif->netmask));
  59. LOG_I("gw:%s",ipaddr_ntoa(&lwip_netif->gw));
  60. //调用accept函数从队列中
  61. while (1)
  62. {
  63. sin_size = sizeof(struct sockaddr_in);
  64. LOG_I("socket start accepte");
  65. if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1)
  66. {
  67. LOG_E("accept");
  68. continue;
  69. }
  70. cli_addr = malloc(sizeof(struct sockaddr));
  71. LOG_I("accept client,ip:%s\r\n",inet_ntoa(client_sock.sin_addr));
  72. if (cli_addr != NULL)
  73. {
  74. memcpy(cli_addr, &client_sock, sizeof(struct sockaddr));
  75. }
  76. //处理目标
  77. ssize_t ret;
  78. uint8_t error_cnt = 0;
  79. while (1)
  80. {
  81. ret = -1;
  82. memset((void *)recvbuf,0,sizeof(recvbuf));
  83. if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1)
  84. {
  85. LOG_I("recv error \r\n");
  86. error_cnt++;
  87. if(error_cnt > 3) break;
  88. }
  89. LOG_I("recv :%s\r\n", recvbuf);
  90. sleep(2);
  91. ret = -1;
  92. if ((ret = send(new_fd, buf, strlen(buf) + 1, 0)) == -1)
  93. {
  94. LOG_E("send error ");
  95. error_cnt++;
  96. if(error_cnt > 3) break;
  97. }
  98. else
  99. {
  100. LOG_I("socket send success");
  101. }
  102. sleep(2);
  103. }
  104. lwip_close(new_fd);
  105. }
  106. }
  107. void app_socket_service_init(void)
  108. {
  109. osThreadAttr_t attr;
  110. attr.name = "TCPServerTask";
  111. attr.attr_bits = 0U;
  112. attr.cb_mem = NULL;
  113. attr.cb_size = 0U;
  114. attr.stack_mem = NULL;
  115. attr.stack_size = 10240;
  116. attr.priority = osPriorityNormal;
  117. if (osThreadNew((osThreadFunc_t)TCPServerTask, NULL, &attr) == NULL)
  118. {
  119. LOG_I("[TCPServerDemo] Falied to create TCPServerTask!\n");
  120. }
  121. }

        看结果:

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/106779
推荐阅读
相关标签
  

闽ICP备14008679号