Fork me on GitHub
0%

LwIP+websocket

WebSocket流程

websocket https://github.com/dhbaird/easywsclient

Server端

  1. socket()
  2. bind()
  3. listen()
  4. accept()
  5. recv()

Client端

  1. socket()
  2. connect()
  3. send()

Lwip-API

lwip_socket

  • 功能:用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数

  • 函数原型

    1
    #define socket(domain,type,protocol)	  lwip_socket(domain,type,protocol)
  • 参数选项

    • domain:地址族AF_INET(IP_V4)、AF_INET6(IP_V6)

    • type:

      1
      2
      3
      #define SOCK_STREAM     1 //流式套接字提供可靠的、面向连接的通信流;它使用TCP 协议,从而保证了数据传输的正确性和顺序性。
      #define SOCK_DGRAM 2// 数据报套接字定义了一种无连接的服 ,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
      #define SOCK_RAW 3// 原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
    • ptotocol

      1
      2
      3
      4
      5
      #define IPPROTO_IP      0
      #define IPPROTO_ICMP 1
      #define IPPROTO_TCP 6
      #define IPPROTO_UDP 17
      // 一般自动设置为0, 系统自动设置合适的协议

lwip_connect

  • 函数功能:用于建立与指定socket的连接。一般用于客户端,

  • 函数原型

    1
    #define connect (s,	name, namelen)	   lwip_connect(s,name,namelen)
  • 参数

    • s :socket()返回文件描述符代表的套接字
    • const struct sockaddr *name:存放了服务端用于通信的地址和端口。
    • socklen_t namelen:name参数结构体的长度。

lwip_setsockopt

  • 功能:设置与某个套接字关联的选项,当操作套接字选项时,选项位于的层和选项的名称必须给出

  • 函数原型:

    1
    int setsockopt(int sock, int level,int optname, const void *optval, socklen_t optlen)
  • 参数选项

    • sock:将要被设置选项的套接字

    • optname:需要访问的选项名

    • optval:指向包含新选项值得缓冲

    • optlen:现选项的长度

    • level:参数(level)详细说明:level是指定控制套接字的层次,可以取如下三种值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      选项名称(optname)   说明                   数据类型
      ========================================================================
      SO_BROADCAST      允许发送广播数据         int
      SO_DEBUG        允许调试            int
      SO_DONTROUTE      不查找             int
      SO_ERROR        获得套接字错误         int
      SO_KEEPALIVE      保持连接            int
      SO_LINGER        延迟关闭连接          struct linger
      SO_OOBINLINE      带外数据放入正常数据流     int
      SO_RCVBUF        接收缓冲区大小         int
      SO_SNDBUF        发送缓冲区大小         int
      SO_RCVLOWAT       接收缓冲区下限         int
      SO_SNDLOWAT       发送缓冲区下限         int
      SO_RCVTIMEO       接收超时            struct timeval
      SO_SNDTIMEO       发送超时            struct timeval
      SO_REUSERADDR      允许重用本地地址和端口     int
      SO_TYPE         获得套接字类型         int
      SO_BSDCOMPAT      与BSD系统兼容          int
      ========================================================================
      // SOL_SOCKET:通用套接字选项
      1
      2
      3
      4
      5
      6
      7
      8
      选项名称(optname)   说明                   数据类型
      ========================================================================
      IP_HDRINCL       在数据包中包含IP首部      int
      IP_OPTINOS       IP首部选项           int
      IP_TOS         类型
      IP_TTL         生存时间             int
      ========================================================================
      //IPPROTO_IP:IP选项
      1
      2
      3
      4
      5
      6
      选项名称(optname)   说明                   数据类型
      ========================================================================
      TCP_MAXSEG      TCP最大数据段的大小        int
      TCP_NODELAY      不使用Nagle算法          int
      ========================================================================
      // IPPROTO_TCP:TCP选项

lwip_fcntl

  • 功能:fcntl 的最小实现。 目前仅实现了命令 F_GETFL 和 F_SETFL。 只有标志 O_NONBLOCK 被实现。

lwip_send / lwip_sendto / lwip_write

  • 功能:发送数据

  • 区别:

    • lwip_send:核心, 最终的调用
    • lwip_write:直接调用lwip_send
    • Lwip_sendto:相较于lwip_to多了远程主机的ip地址和端口号相关的参数,处理中多了netconn_connect的调用(lwip_connect + lwip_send)
  • 函数原型

    1
    #define sendmsg	(s,	message,flags )	   lwip_sendmsg(s,message,flags)

lwip_select

  • 功能:用于I/O多路复用的API,使用select可以同时监听多个socket的状态,以便读写数据

  • 原型

    1
    2
    3

    #define select (maxfdp1,readset,writeset,exceptset,timeout)
    lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
  • 参数含义

    • maxfdp1:要检查的最大文件描述符的数目,一般是最大dp+1

    • readset/writeset/exceptset:

      fd_set类型,指向需要监听的可读、可写和异常事件的文件描述符集合

lwip_recv

  • 函数功能:接受数据

  • 函数原型

    1
    #define recv(s,	mem,len,flags)	   lwip_recv(s,mem,len,flags)

Q&A

  1. Q:调用lwip_send / lwip_recv时出现WSAEWOULDBLOCK / WSAEINPROGRESS

    A:本质上以上两个错误码不是错误,而是一种警告,接收方告诉发送方它现在处于busy状态

    ​ WSAEWOULDBLOCK的意思是output buffer已经满了,无法再写入数据。按照网上博主的说法,在绝大多数情况下其实不会出现buffer满的情况,而是 处于busy状态。我理解的这与overrun的意思差不多,只不过这里不会对数据正确性、完整性造成影响,接收方告诉我它的缓冲区满了或者数据已经处理 不过来了,它不再接受数据了,所以给我们一个异常。

    ​ WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

    解决:当出现 WSAEWOULDBLOCK 异常后直到空出 Output Buffer 时,系统会发送一个 FD_WRITE 给发送方。我们完全可以在等收到 FD_WRITE 消息后再重新发送从出现异常开始的数据包即可(该包需要全部重新发送)。

    参考链接

参考资料